本文博客原文
Android
原生自带了个安装器(packages\apps\PackageInstaller),
通过其中的源码
PackageParser.java
(frameworks\base\core\java\android\content\pm)<wbr></wbr>
我们大概就能知道其签名验证机制的验证过程。
其中主要涉及2个函数:
函数1
public
boolean
collectCertificates
(
Package
pkg
,
int
flags
)
{
pkg
.
mSignatures
=
null
;
WeakReference
<
byte
[]>
readBufferRef
;
byte
[]
readBuffer
=
null
;
synchronized
(
mSync
)
{
readBufferRef
=
mReadBuffer
;
if
(
readBufferRef
!=
null
)
{
mReadBuffer
=
null
;
readBuffer
=
readBufferRef
.
get
();
}
if
(
readBuffer
==
null
)
{
readBuffer
=
new
byte
[
8192
];
readBufferRef
=
new
WeakReference
<
byte
[]>(
readBuffer
);
}
}
try
{
JarFile
jarFile
=
new
JarFile
(
mArchiveSourcePath
);
Certificate
[]
certs
=
null
;
if
((
flags
&
PARSE_IS_SYSTEM
)
!=
0
)
{
// If this package comes from the system image, then we
// can trust it... we'll just use the AndroidManifest.xml
// to retrieve its signatures, not validating all of the
// files.
JarEntry jarEntry = jarFile.getJarEntry(ANDROID_MANIFEST_FILENAME);
certs = loadCertificates(jarFile, jarEntry, readBuffer);
if (certs == null) {
Slog.e(TAG, "Package " + pkg.packageName
+ " has no certificates at entry "
+ jarEntry.getName() + "; ignoring!");
jarFile.close();
mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
return false;
}
if (DEBUG_JAR) {
Slog.i(TAG, "File " + mArchiveSourcePath + ": entry=" + jarEntry
+ " certs=" + (certs != null ? certs.length : 0));
if (certs != null) {
final int N = certs.length;
for (int i=0; i<N; i++) {
Slog.i(TAG, " Public key: "
+ certs[i].getPublicKey().getEncoded()
+ " " + certs[i].getPublicKey());
}
}
}
} else {
Enumeration<JarEntry> entries = jarFile.entries();
final Manifest manifest = jarFile.getManifest();
while (entries.hasMoreElements()) {
final JarEntry je = entries.nextElement();
if (je.isDirectory()) continue;
final
String
name
=
je
.
getName
();
if
(
name
.
startsWith
(
"META-INF/"
))
continue
;
if
(
ANDROID_MANIFEST_FILENAME
.
equals
(
name
))
{
final
Attributes
attributes
=
manifest
.
getAttributes
(
name
);
pkg
.
manifestDigest
=
ManifestDigest
.
fromAttributes
(
attributes
);
}
final
Certificate
[]
localCerts
=
loadCertificates
(
jarFile
,
je
,
readBuffer
);
if
(
DEBUG_JAR
)
{
Slog
.
i
(
TAG
,
"File "
+
mArchiveSourcePath
+
" entry "
+
je
.
getName
()
+
": certs="
+
certs
+
" ("
+
(
certs
!=
null
?
certs
.
length
:
0
)
+
")"
);
}
if
(
localCerts
==
null
)
{
Slog
.
e
(
TAG
,
"Package "
+
pkg
.
packageName
+
" has no certificates at entry "
+
je
.
getName
()
+
"; ignoring!"
);
jarFile
.
close
();
mParseError
=
PackageManager
.
INSTALL_PARSE_FAILED_NO_CERTIFICATES
;
return
false
;
}
else
if
(
certs
==
null
)
{
certs
=
localCerts
;
}
else
{
// Ensure all certificates match.
for (int i=0; i<certs.length; i++) {
boolean found = false;
for (int j=0; j<localCerts.length; j++) {
if (certs[i] != null &&
certs[i].equals(localCerts[j])) {
found = true;
break;
}
}
if (!found || certs.length != localCerts.length) {
Slog.e(TAG, "Package " + pkg.packageName
+ " has mismatched certificates at entry "
+ je.getName() + "; ignoring!");
jarFile.close();
mParseError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
return false;
}
}
}
}
}
jarFile.close();
synchronized
(
mSync
)
{
mReadBuffer
=
readBufferRef
;
}
if
(
certs
!=
null
&&
certs
.
length
>
0
)
{
final
int
N
=
certs
.
length
;
pkg
.
mSignatures
=
new
Signature
[
certs
.
length
];
for
(
int
i
=
0
;
i
<
N
;
i
++)
{
pkg
.
mSignatures
[
i
]
=
new
Signature
(
certs
[
i
].
getEncoded
());
}
}
else
{
Slog
.
e
(
TAG
,
"Package "
+
pkg
.
packageName
+
" has no certificates; ignoring!"
);
mParseError
=
PackageManager
.
INSTALL_PARSE_FAILED_NO_CERTIFICATES
;
return
false
;
}
}
catch
(
CertificateEncodingException
e
)
{
Slog
.
w
(
TAG
,
"Exception reading "
+
mArchiveSourcePath
,
e
);
mParseError
=
PackageManager
.
INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING
;
return
false
;
}
catch
(
IOException
e
)
{
Slog
.
w
(
TAG
,
"Exception reading "
+
mArchiveSourcePath
,
e
);
mParseError
=
PackageManager
.
INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING
;
return
false
;
}
catch
(
RuntimeException
e
)
{
Slog
.
w
(
TAG
,
"Exception reading "
+
mArchiveSourcePath
,
e
);
mParseError
=
PackageManager
.
INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION
;
return
false
;
}
return
true
;
}
函数2
private
Certificate
[]
loadCertificates
(
JarFile
jarFile
,
JarEntry
je
,
byte
[]
readBuffer
)
{
try
{
// We must read the stream for the JarEntry to retrieve
// its certificates.
InputStream is = new BufferedInputStream(jarFile.getInputStream(je));
while (is.read(readBuffer, 0, readBuffer.length) != -1) {
// not using
}
is.close();
return je != null ? je.getCertificates() : null;
} catch (IOException e) {
Slog.w(TAG, "Exception reading " + je.getName() + " in "
+ jarFile.getName(), e);
} catch (RuntimeException e) {
Slog.w(TAG, "Exception reading " + je.getName() + " in "
+ jarFile.getName(), e);
}
return null;
}
通过上面的代码,我们已经能够得到了签名的证书,通过证书我们就得到PublicKey,通过比较PublicKey我们就能比较签名是否一致,当然这里假设的是只有一个签名。
实例1
package
edu
.
edut
.
robin
.
utils
;
import
java
.
io
.
ByteArrayInputStream
;
import
java
.
io
.
IOException
;
import
java
.
io
.
InputStream
;
import
java
.
security
.
PublicKey
;
import
java
.
security
.
cert
.
Certificate
;
import
java
.
security
.
cert
.
CertificateFactory
;
import
java
.
security
.
cert
.
X509Certificate
;
import
java
.
util
.
jar
.
JarEntry
;
import
java
.
util
.
jar
.
JarFile
;
import
android
.
content
.
Context
;
import
android
.
content
.
pm
.
PackageInfo
;
import
android
.
content
.
pm
.
PackageManager
;
import
android
.
content
.
pm
.
PackageManager
.
NameNotFoundException
;
import
android
.
util
.
Base64
;
public
class
SigntureUtil
{
final
static
String
TAG
=
"Signture"
;
public
static
String
[]
getPublicKeyString
(
PackageInfo
pi
)
{
PublicKey
pubKeys
[]
=
getPublicKey
(
pi
);
if
(
pubKeys
==
null
||
pubKeys
.
length
==
0
)
{
return
null
;
}
String
[]
strPubKeys
=
new
String
[
pubKeys
.
length
];
for
(
int
i
=
0
;
i
<
pubKeys
.
length
;
i
++)
strPubKeys
[
i
]
=
Base64
.
encodeToString
(
pubKeys
[
i
].
getEncoded
(),
Base64
.
DEFAULT
);
return
strPubKeys
;
}
private
static
PublicKey
[]
getPublicKey
(
PackageInfo
pi
)
{
try
{
if
(
pi
.
signatures
==
null
||
pi
.
signatures
.
length
==
0
)
{
return
null
;
}
PublicKey
[]
publicKeys
=
new
PublicKey
[
pi
.
signatures
.
length
];
for
(
int
i
=
0
;
i
<
publicKeys
.
length
;
i
++)
{
byte
[]
signature
=
pi
.
signatures
[
i
].
toByteArray
();
CertificateFactory
certFactory
=
CertificateFactory
.
getInstance
(
"X.509"
);
InputStream
is
=
new
ByteArrayInputStream
(
signature
);
X509Certificate cert
=
(
X509Certificate
)
certFactory
.
generateCertificate
(
is
);
publicKeys
[
i
]
=
cert
.
getPublicKey
();
}
}
catch
(
Exception
ex
)
{
}
return
null
;
}
private
static
PublicKey
[]
getInstalledAppPublicKey
(
Context
context
,
String
packageName
)
{
PackageManager
pm
=
context
.
getPackageManager
();
PackageInfo
pi
;
try
{
pi
=
pm
.
getPackageInfo
(
packageName
,
PackageManager
.
GET_SIGNATURES
);
if
(
pi
!=
null
&&
pi
.
versionName
!=
null
)
{
return
getPublicKey
(
pi
);
}
}
catch
(
NameNotFoundException
e
)
{
// not installed
return
null
;
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
return
null
;
}
private
static
Certificate
[]
loadCertificates
(
JarFile
jarFile
,
JarEntry
je
)
{
try
{
// We must read the stream for the JarEntry to retrieve
// its certificates.
byte
[]
readBuffer
=
new
byte
[
1024
];
InputStream
is
=
jarFile
.
getInputStream
(
je
);
while
(
is
.
read
(
readBuffer
,
0
,
readBuffer
.
length
)
!=
-
1
)
;
is
.
close
();
return
(
je
!=
null
)
?
je
.
getCertificates
()
:
null
;
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
return
null
;
}
public
static
boolean
verifySignature
(
Context
context
,
String
packageName
,
String
filePath
)
{
boolean
verifyed
=
true
;
try
{
PublicKey
[]
installedAppPubKeys
=
getInstalledAppPublicKey
(
context
,
packageName
);
if
(
installedAppPubKeys
==
null
||
installedAppPubKeys
.
length
==
0
)
{
// package not installed
return
true
;
}
JarFile
jarFile
=
new
JarFile
(
filePath
);
verifyed
=
false
;
JarEntry
je
=
jarFile
.
getJarEntry
(
"classes.dex"
);
Certificate
[]
certs
=
loadCertificates
(
jarFile
,
je
);
if
(
certs
!=
null
&&
certs
.
length
>
0
)
{
for
(
int
i
=
0
;
i
<
certs
.
length
;
i
++)
{
PublicKey
pubKey
=
certs
[
i
].
getPublicKey
();
for
(
int
j
=
0
;
j
<
installedAppPubKeys
.
length
;
j
++)
{
if
(
pubKey
.
equals
(
installedAppPubKeys
[
j
]))
{
verifyed
=
true
;
break
;
}
}
if
(
verifyed
)
break
;
}
}
else
{
verifyed
=
true
;
}
jarFile
.
close
();
}
catch
(
Exception
e
)
{
verifyed
=
true
;
}
return
verifyed
;
}
}
结束