Add API to check certificate chain signatures
Add hidden API to check certificate chain signatures when needed. The
getCertificates implementation returns a list of all the certificates and
chains and would expect any caller interested in verifying actual chains
to call getCodeSigners instead.
We add this hidden constructor as a stop-gap until we can switch callers
over to getCodeSigners.

The implication of the above is that the long term fix is for the PackageParserloadCertificates method to call the JarEntrygetCodeSigners method rather than the getCertificates method as it does now.

The getCodeSigners method is declared like this

public CodeSigner[] getCodeSigners()

On its own this is not going to achieve anything since getCodeSigners does exactly the same amount of certificate chain verification as the original version of getCertificates, i.e., none.

What it does do is package the certificates up into instances of java.security.CodeSigner for you.

A CodeSigner contains a java.security.cert.CertPath.

Once you have one of those you can either validate the certificates in it yourself or hand the whole thing to ajava.security.cert.CertPathValidator who will do it for you, assuming you can work out how to set up the right CertPathParameters instance.

And after all that and assuming everything verifies you can collect all the constituent Certificates together and then turn them into Signatures just like before.

An alternative long term solution might be to come up with a proper Application Signature abstraction and a proper Security Policy abstraction.

Then, rather have random bits of code deciding to implement ad hoc security policies because they can get at the internals of Signatures, they would have to use the Security Policy !

Copyright (c) 2014 By Simon Lewis. All Rights Reserved.

Unauthorized use and/or duplication of this material without express and written permission from this blog’s author and owner Simon Lewis is strictly prohibited.

Excerpts and links may be used, provided that full and clear credit is given to Simon Lewis and justanapplication.wordpress.com with appropriate and specific direction to the original content.

As an aside, the Signature handling code in this method bears an uncanny resemblance to the PackageManagerServicecheckSignaturesLP method code as it was in 2008 which may be a clue as to where it originated.

Anyway, given what we now know, how do we fix it ?

In what follows I will use the term signer certificate to mean the certificate which specifies the public key corresponding to the private key with which the plugin was signed.

Perhaps the easiest thing to do is start by seeing if we can find the hard-wired certificate at all before worrying whether someone is trying to pull a fast one.

Thats all almost exactly the code structure same as the definition of the compareSignatures method now except that the semantics of Application signatures are completely different !

No Sets there, that’s a ‘member in common’ test !

Whoah, scary ! To be honest I wasn’t expecting that !

(Enough with the exclamation marks.)

Presumably Android Application Signature arrays were constructed somewhat differently in those days or that test does not make a whole lot of sense.

Nope, PackageParser.loadCertificates looks to be nigh on identical.

OK, so at least they used to verify the certificate chains properly surely. No they didn’t do that either. JarUtils.createChain is identical.

Oh dear.

The full implications of that are left as an exercise for the reader but bear in mind that quite possibly there is something somewhere doing something which means it is not as bad as it looks.

Code Rot

Given the way it seems Signature instances were used originally it turns out that the Signature class is not quite as pointless as it appears to be now.

It doesn’t explain why the abstraction was completely broken from day one but it does explain why, for example, the class represents a single encoded certificate rather than a set of them.

The combination of the class being public and the abstraction failure presumably meant that it was felt that it was not possible to substantially modify its behaviour and what it represented.

However, given the major change in semantics that occurred, changing the class substantively so that any code that relied on it broke would have had the benefit of revealing what if anything was relying on the assumption that it contained a certificate and that that certificate could be accessed.

Having said that it does not explain why at least internally a class could not have been defined that simply encapulated all the details of what constitutes an Application’s signature, how one is constructed and how they are compared.

Afterword

When I started writing this although I thought it possible that something about how Android Application signatures worked might have changed between the point at which the Signature class was originally defined and now I didn’t really didn’t know one way or another. It was just a possible explanation for the existence of a fairly pointless class.

The earliest version of the Android source I have immediately accessible is the version I changed to get running standalone, that is, on a standard JVM rather than Dalvik, which dates from late 2008.

Although I clearly changed the PackageManagerService class for some reason at that time I have no recollection of looking at this area at all, so I was genuinely surprised to discover

that it really had changed a lot, and

that at that point it seems it was completely broken

The earliest version of the PackageManagerService class I have locally that constructs sets of Signatures in order to perform a comparison is from March 2011 where it is being done by the checkSignaturesLP method.

At that point there is still no compareSignatures method defined.

Copyright (c) 2014 By Simon Lewis. All Rights Reserved.

Unauthorized use and/or duplication of this material without express and written permission from this blog’s author and owner Simon Lewis is strictly prohibited.

Excerpts and links may be used, provided that full and clear credit is given to Simon Lewis and justanapplication.wordpress.com with appropriate and specific direction to the original content.

This method single-handedly drives a coach and horses through the idea that a Signature is a, to quote the class documentation

Opaque, immutable representation of a signature associated with an application package.

Immutable yes, opaque not so much.

1.1 Signature.getPublicKey

The Signature class did not have a getPublicKey method originally although it has always had a toByteArray method so it has never been as opaque as it ought to have been but know you can ask it to do this !

In fact you cannot ask it to do that because despite being a public method it has been hidden which is bizarre because you can always do the whole thing yourself.

Even more bizarrely it looks as though getUidForVerifier is the only method in the system that calls it.

Why add and a then hide a method that is only used by a single method in the PackageManagerService class ?

Why not do it locally ? The PackageManagerService class is almost 11000 lines long, 5 more aren’t going to make much difference.

1.2 What Does getUidForVerifier Do ?

What the method does is to look up the Package specified by the VerifierInfo‘s packageName instance variable.

If the Package exists the method then checks the length of the Package’s array of Signatures.

If the length is not one the method returns -1.

The length of an Application’s Signature array is going to be greater than one, if either

the Application has been signed more than once, or

there was more than one certificate in the signed signature file

No idea what is significant about either of these cases except that by excluding them it does mean that this method is unaffected by the failure to verify certificate chains.

If the length of an Application’s signature array is one then it necessarily contains the Signature constructed from the certificate which specifies the public key corresponding to the private key with which it was signed.

If the public key specified by the VerifierInfo‘s publicKey instance variable matches the public key extracted from the Signature then …

Then what exactly ?

Then you know you have the named Application and that it was signed by the private key corresponding to the specified public key.

1.4 Why A Public Key ?

If the VerifierInfo specified a name and an array of Signatures getUidForVerifier could have used the compareSignatures method rather than levering open a Signature, so why the public key ?

The VerifierInfo is obtained from the package-verifier element of an Android Application Manifest by this method

its not unreasonable to suppose that you might find one in an X.509 certificate and in fact you would, its actually the payload of the certificate.

So why a bit of a certificate rather than all of the certificate ?

If it was as certificate at least you could maintain the pseudo-opacity of the Signature class and you could use the compareSignatures method so no special-case code required.

The obvious advantages of specifying a public key are that it is smaller and it works with Signatures constructed with any certificate that specifies the public key rather than a specific certificate.

Given that Android package verifiers are something of an enigma there could be other reasons why it is necessary to specify a public key rather than a certificate or a certificate chain but they would need to be very compelling to justify the getUidForVerifier method in its current form.

Copyright (c) 2014 By Simon Lewis. All Rights Reserved.

Unauthorized use and/or duplication of this material without express and written permission from this blog's author and owner Simon Lewis is strictly prohibited.

Excerpts and links may be used, provided that full and clear credit is given to Simon Lewis and justanapplication.wordpress.com with appropriate and specific direction to the original content.

If S(B) is equal to S(A), S(B) must contain the N Signatures that are not Sig(C(PuK(KP))) plus one other Signature.

Since the sets are equal the other Signature must be Sig(C(PuK(KP))).

Note that the converse is not true.

If the compareSignatures method returns PackageManager.SIGNATURE_NO_MATCH it does not prove that the Applications were not signed with the same private key.

3.0 And Exactly Why Is That Useful ?

It is useful because it is precisely what the PackageManagerService wants to know during Application installation, which is an astonishing coincidence, or perhaps not.

Those aspects of the Android Application security model which are embodied by the PackageManagerService are all based on establishing whether two applications are signed with the same private key(s).

3.1 PackageManagerService.grantSignaturePermission

As an example consider the grantSignaturePermission method which is used by the PackageManagerService when installing an Application.

A Signature permission is

A permission that the system grants only if the requesting application is signed with the same certificate as the application that declared the permission. If the certificates match, the system automatically grants the permission without notifying the user or asking for the user’s explicit approval.

Substituting signed with the same private key for the endlessly misleading

signed with the same certificate

formulation, you can see that the compareSignatures method is going to tell you exactly what you need to know.

...
private boolean grantSignaturePermission(String perm, PackageParser.Package pkg,
BasePermission bp, HashSet<String> origPermissions) {
boolean allowed;
allowed = (compareSignatures(
bp.packageSetting.signatures.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH)
|| (compareSignatures(mPlatformPackage.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH);
if (!allowed && (bp.protectionLevel
& PermissionInfo.PROTECTION_FLAG_SYSTEM) != 0) {
if (isSystemApp(pkg)) {
// For updated system applications, a system permission
// is granted only if it had been defined by the original application.if (isUpdatedSystemApp(pkg)) {
final PackageSetting sysPs = mSettings
.getDisabledSystemPkgLPr(pkg.packageName);
final GrantedPermissions origGp = sysPs.sharedUser != null
? sysPs.sharedUser : sysPs;
if (origGp.grantedPermissions.contains(perm)) {
allowed = true;
} else {
// The system apk may have been updated with an older
// version of the one on the data partition, but which
// granted a new system permission that it didn't have
// before. In this case we do want to allow the app to
// now get the new permission, because it is allowed by
// the system image.
allowed = false;
if (sysPs.pkg != null) {
for (int j=0;
j<sysPs.pkg.requestedPermissions.size(); j++) {
if (perm.equals(
sysPs.pkg.requestedPermissions.get(j))) {
allowed = true;
break;
}
}
}
}
} else {
allowed = true;
}
}
}
if (!allowed && (bp.protectionLevel
& PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
// For development permissions, a development permission
// is granted only if it was already granted.
allowed = origPermissions.contains(perm);
}
return allowed;
}
...

The grantSignaturePermission method works with both and signature and signatureOrSystem permissions.

The bulk of the method is some special case code for a systemOrSignature permission, but you can see that before that the method is indeed using the compareSignatures method to decide whether to grant a signature permission.

3.2 PackageManager.setInstallerPackageName

The setInstallerPackageName method is the back-end method which implements the functionality accesible via the PackageManager method of the same name.

The documentation for the PackageManager version is as follows

Change the installer associated with a given package. There are limitations
on how the installer package can be changed; in particular:

A SecurityException will be thrown if installerPackageName
is not signed with the same certificate as the calling application.

A SecurityException will be thrown if targetPackage already
has an installer package, and that installer package is not signed with
the same certificate as the calling application.

The appearance, yet again, of the stock phrase

signed with the same certificate

leads us to expect that the method will use the compareSignatures method and indeed it does.

4.0 The Semantics Of The compareSignatures Method Concluded

And there you have it, after all of that it turns out that the semantics of the compareSignatures method are very simple.

Two arrays of Signatures match, if and only if, after the elimination of duplicate Signatures the resulting sets are identical.

One of the rather strange implications of this is that if you want the signatures of different applications to match you had better make sure to include exactly the same number of certificates in the signed certificate file of each one.

If you include the legitimate cerificate chain

A B C

where C is the issuer of B which is the issuer of A, and C is not self-signed, in the signed certificate file of one application, but for some reason you include the legitimate certificate chain

A B C D

where D is the issuer of C, in the signed certificate file of another application, then you end up with two applications whose signatures do not match despite the fact that you actually signed both of them using the same private key, which is a bit odd really.

This applies equally to different versions of the same application.

It also implies that if one of the certificates in the chain you have included with an application expires you are must keep using it despite the fact that it has expired or, again, you end up with non-matching signatures even if the public keys involved do not change.

The simplest thing you can do is to include only the certificate specifying the public key corresponding to the private key with which you signed the signature file, although this does not help in the case of that certificate expiring.

Copyright (c) 2014 By Simon Lewis. All Rights Reserved.

Unauthorized use and/or duplication of this material without express and written permission from this blog's author and owner Simon Lewis is strictly prohibited.

Excerpts and links may be used, provided that full and clear credit is given to Simon Lewis and justanapplication.wordpress.com with appropriate and specific direction to the original content.

If you have made it this far through the labyrinth of method calls you will undoubtedly have noticed that at no point has there been any attempt made to determine whether the certificates that were included in the signed signature file and that were returned by the call to the JarUtils.createChain method actually have any relationship to one another, that is, whether the supposed issuer of each certificate did in fact sign that certificate.

It is not difficult to do so

There is a very helpful method on the class java.security.certificate.Certificate

Given that I do not know the private key corresponding to the public key in the Adobe certificate, there was no way the certificate I created for the public key corresponding to the private key I signed the application with could possibly be verified.

The extent of my sleight of hand was simply to ensure that the issuer of the certificate I created was the same as that of the subject of the Adobe certificate.

Arguably it was not really even a sleight of hand.

I do have a second certificate whose subject is the same as that of the Adobe certificate and it does specify the public key corresponding to the private key with which the first certificate was signed.

It just wasn’t the one that got included in the signed signature file.

Copyright (c) 2014 By Simon Lewis. All Rights Reserved.

Unauthorized use and/or duplication of this material without express and written permission from this blog’s author and owner Simon Lewis is strictly prohibited.

Excerpts and links may be used, provided that full and clear credit is given to Simon Lewis and justanapplication.wordpress.com with appropriate and specific direction to the original content.

...
/**
* Verifies that the digests stored in the manifest match the decrypted
* digests from the .SF file. This indicates the validity of the
* signing, not the integrity of the file, as it's digest must be
* calculated and verified when its contents are read.
*
* @throws SecurityException
* if the digest value stored in the manifest does not
* agree with the decrypted digest as recovered from the
* .SF file.
*/
...

which is interesting, to put it mildly, because it makes no sense at all.

As we have seen a VerifierEntry instance is created by the JarVerifier method initEntry.

The constructor is passed four values

the name of the file in the JAR

a MessageDigest instance

the Base64 encoded value of the digest of the file in the JAR obtained from the Manifest

the cerificates associated with the signature file(s) which contain entries for the attributes of the file in the JAR defined in the Manifest

Calls to the JarFile.JarFileInputStreamread methods result in the VerifierEntry being passed what has been read from the file via calls to its write methods.

The write methods simply update the MessageDigest instance with the byte(s) they have been passed.

Once the end of the file has been reached then the verify method is called.

The method computes the digest of the file as contained in the JAR.

It decodes the Base64 digest as obtained from the Manifest.

It then compares the two and if they do not match it throws a SecurityException.

Otherwise it adds the array of certificates it was constructed with to the JarVerifier verifiedEntries Hashtable with the name of the file as the key.

In other words it does precisely the opposite of what the comment says.

It DOES ensure the integrity of the file, it DOES NOT verify

that the digests stored in the manifest match the decrypted digests from the .SF file.

because the VerifierEntry does not have any information from the signature file and even if it did it could not decrypt it because nothing in a signature file is encrypted.

I can only assume that the documentation comment wandered in from somewhere else and attached itself to the method and nobody noticed.

Just to compound the confusion the class documentation comment reads

...
/**
* Stores and a hash and a message digest and verifies that massage digest
* matches the hash.
*/
...

Copyright (c) 2014 By Simon Lewis. All Rights Reserved.

Unauthorized use and/or duplication of this material without express and written permission from this blog’s author and owner Simon Lewis is strictly prohibited.

Excerpts and links may be used, provided that full and clear credit is given to Simon Lewis and justanapplication.wordpress.com with appropriate and specific direction to the original content.

As we have seen, when the file in the JAR has a digest entry in the Manifest and an associated entry in at least one signature file, theInputStream will be an instance of the JarFile inner class JarFileInputStream.