Just An Application

August 22, 2014

And Another One: Part Twenty Two — The Actual Fix

And after all that, how has it actually been fixed ?

Like this apparently, at least in the short term.

    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 PackageParser loadCertificates method to call the JarEntry getCodeSigners 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 a
java.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.

And Another One: Part Twenty One — The Package Manager Revisited

File

    $(ANDROID_SRC)/frameworks/base/core/java/android/webkit/PluginManager.java

Source

    ...

    private static boolean containsPluginPermissionAndSignatures(PackageInfo pkgInfo) {
    
        // check if the plugin has the required permissions
        String permissions[] = pkgInfo.requestedPermissions;
        if (permissions == null) {
            return false;
        }
        boolean permissionOk = false;
        for (String permit : permissions) {
            if (PLUGIN_PERMISSION.equals(permit)) {
                permissionOk = true;
                break;
            }
        }
        if (!permissionOk) {
            return false;
        }
    
        // check to ensure the plugin is properly signed
        Signature signatures[] = pkgInfo.signatures;
        if (signatures == null) {
            return false;
        }
        if (SystemProperties.getBoolean("ro.secure", false)) {
            boolean signatureMatch = false;
            for (Signature signature : signatures) {
                for (int i = 0; i < SIGNATURES.length; i++) {
                    if (SIGNATURES[i].equals(signature)) {
                        signatureMatch = true;
                        break;
                    }
                }
            }
            if (!signatureMatch) {
                return false;
            }
        }
        
        return true;
    }
    
    ...

This is where we came in.

As an aside, the Signature handling code in this method bears an uncanny resemblance to the PackageManagerService checkSignaturesLP 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.

        ...
    
            int index       = 0;
            int nSignatures = signatures.length;
    
            while (index < nSignatures)
            {
                if (signatures[index].equals(SIGNATURE_1))
                {
                    break;
                }
                ++index;
            }

If we don’t find it we return false.


            if (index == nSignatures)
            {
                // not found
                return false;
            }

If we find it and there is only one Signature then the hard-wired certificate is the signer certificate so we return true.


            else
            if (nSignatures == 1)
            {
                return true;
            }

If we find the hard-wired certificate at index 0 we know it is the first certificate of the first certificate chain so, irrespective of the number of Signatures, we know it is a signer certificate.

Whether we ought to know this is debateable, but given that we already know that Signatures are encoded certificates and are taking advantage of the fact, its a bit late in the day to worry about it.


            else
            if (index == 0)
            {
                return true;
            }

If none of the above are true then there is nothing for it. We are going to have to establish that the hard-wired certificate is genuinely part of a certificate chain.

We know that the hard-wired certificate is self-signed so it has to appear standalone, i.e., in a certificate chain of length one, or as the last certificate in a chain of length two or greater.

We can determine whether it is at the end of a certificate chain by checking to see who issued the preceding certificate.

Assuming that we have a method makeCertificate for making magically making X509Certificates, then


            X509Certificate ourCert    = makeCertificate(signatures[index--]);
            X509Certificate previous   = makeCertificate(signatures[index--]);
    

If the issuer of the previous certificate wasn’t the subject of the hard-wired certificate then the hard-wired certificate is a signer certificate so we can return true.


            if (!previous.getIssuerDN().equals(ourCert.getSubjectDN()))
            {
                return true;
            }

If the hard-wired certificate is at the end of a certificate chain then we should be able to use the public key from it to verify the previous one.


            try
            {
                previous.verify(ourCert.getPublicKey());
            }
            catch (Exception e)
            {
                return false;
            }

If the verification succeds we keep going until we reach the front of the certificate chain.


            X509Certificate current = previous;

            while (index > 0)
            {
                previous = makeCertificate(signatures[index--]);
                if (!previous.getIssuerDN().equals(current.getSubjectDN()))
                {
                    // we've successfully reached the front of the chain
                    break;
                }
                try
                {
                    previous.verify(current.getPublicKey());
                }
                catch (Exception e)
                {
                    return false;
                }
                current = previous;
            }

If we make it to the first certificate in the chain in one piece we have a valid certificate chain so we return true;

And there you have it.

Not only does it fix the problem but as a bonus it manages to tightly couple the PluginManager to a whole bunch of classes that do not even know it exists.

Note that it would be a lot simpler if there were some known invariants.

For example, if the hard-wired certificate must be the signer certificate and there cannot be multiple signers then the whole thing collapses down to

    return (signatures[length] == 0) && signatures[0].equals(SIGNATURE_1);

which is a lot less fragile.

Now all we need to do is find all the other code that is implementing ad-hoc security policies on the basis of knowing that Signatures are encoded certificates and fix that as well.


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.

August 21, 2014

And Another One: Part Twenty — Abstraction Failure Or Why Does The Class android.content.pm.Signature Exist ?

I have already quoted the class documentation comment for the android.content.pm.Signature class before but here it is again.

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

It is the comment which appeared in the first version of the class and despite subsequent changes it has never been updated.

Even in the original version it was not true as the class defined this method

    ...
    
    /**
     * @return the contents of this signature as a byte array.
     */
    public byte[] toByteArray() {
        byte[] bytes = new byte[mSignature.length];
        System.arraycopy(mSignature, 0, bytes, 0, mSignature.length);
        return bytes;
    }
    
    ...

Why should it be possible to turn the contents of something supposedly opaque into a byte array ?

It looks fairly innocuous but its a major abstraction failure all on its own so lets take the documentation at its word and get rid of it, and while we are at it we will get rid of

    public char[] toChars()

as well as

    public char[] toChars(char[] existingArray, int[] outLen)

not to mention

    public String toCharsString()

just to be on the safe side.

For an opaque object it defines a remarkable number of ways of finding out whats in it, and we’re not finished yet because its

    Parcelable

and you never know, if somebody got really desperate they might write it to a Parcel then go and read it out themselves as a byte array or something.

Best not to put temptation in their way, so we’ll get rid of all of that as well.

Now what can you do with a Signature as originally defined which is genuinely opaque and immutable ?

Not much to be honest.

What’s left are the equals and hashCode methods and as we have already seen all they do is sub-contract the heavy-lifting to the java.util.Arrays class.

To be fair in its modified form it hides the fact that its really just a wrapper around byte array pretty well but nothing about it realls shouts out Signature ! does it ?

Still its an improvement on its original form where it completely fails to hide the fact that its really just a wrapper around a byte array by giving you access to it.

So, to return to the original question, why does it exist at all in its current form, or to put it another why isn’t just a wrapper a Set of encoded certificates ?

The Past Is A Foreign Country, They Do Things Differently There

One possible explanation is that once upon a time Application signatures used to be defined in a different way and that the Signature class has been re-purposed or something.

Looking for the contemporary version of the PackageManegerService class we discover it in the package.

    com.android.server

That’s right, there was a time when the PackageManegerService class was not sufficiently important to warrant its own package ! Hard to believe isn’t it ?

Now lets see how they used to compare Signatures in those days.

Well they didn’t use compareSignatures thats for sure because its not defined.

So how did grantSignaturePermission work if there was no compareSignatures method ? Answer it didn’t because its not defined.

So where are signature permissions granted then ?

A ha, grantPermissionsLP and its calling checkSignaturesLP, should have guessed, and here it is

    ...
    
    int checkSignaturesLP(PackageParser.Package p1, PackageParser.Package p2) {
        if (p1.mSignatures == null) {
            return p2.mSignatures == null
                    ? PackageManager.SIGNATURE_NEITHER_SIGNED
                    : PackageManager.SIGNATURE_FIRST_NOT_SIGNED;
        }
        if (p2.mSignatures == null) {
            return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
        }
        final int N1 = p1.mSignatures.length;
        final int N2 = p2.mSignatures.length;
        for (int i=0; i<N1; i++) {
            boolean match = false;
            for (int j=0; j<N2; j++) {
                if (p1.mSignatures[i].equals(p2.mSignatures[j])) {
                    match = true;
                    break;
                }
            }
            if (!match) {
                return PackageManager.SIGNATURE_NO_MATCH;
            }
        }
        return PackageManager.SIGNATURE_MATCH;
    }

    ...

This looks familiar.

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

  1. that it really had changed a lot, and

  2. 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.

And Another One: Part Nineteen — Surely That Can’t Be The Only Thing The PackageManagerService Does With Signatures ?

It ought to be but then there is this which is just horrid

1.0 PackageManagerService.getUidForVerifier

File

    $(ANDROID_SRC)/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java

Source

    ...
    
    private int getUidForVerifier(VerifierInfo verifierInfo) {
        synchronized (mPackages) {
            final PackageParser.Package pkg = mPackages.get(verifierInfo.packageName);
            if (pkg == null) {
                return -1;
            } else if (pkg.mSignatures.length != 1) {
                Slog.i(TAG, "Verifier package " + verifierInfo.packageName
                        + " has more than one signature; ignoring");
                return -1;
            }
    
            /*
             * If the public key of the package's signature does not match
             * our expected public key, then this is a different package and
             * we should skip.
             */
    
            final byte[] expectedPublicKey;
            try {
                final Signature verifierSig = pkg.mSignatures[0];
                final PublicKey publicKey = verifierSig.getPublicKey();
                expectedPublicKey = publicKey.getEncoded();
            } catch (CertificateException e) {
                return -1;
            }
    
            final byte[] actualPublicKey = verifierInfo.publicKey.getEncoded();
    
            if (!Arrays.equals(actualPublicKey, expectedPublicKey)) {
                Slog.i(TAG, "Verifier package " + verifierInfo.packageName
                        + " does not have the expected public key; ignoring");
                return -1;
            }
    
            return pkg.applicationInfo.uid;
        }
    }
    
    ... 

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 !

File

    $(ANDROID_SRC)/frameworks/base/core/java/android/content/pm/Signature.java

Source

    ...
    
    public PublicKey getPublicKey() throws CertificateException {
        final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
        final ByteArrayInputStream bais = new ByteArrayInputStream(mSignature);
        final Certificate cert = certFactory.generateCertificate(bais);
        return cert.getPublicKey();
    } ...

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

File

    $(ANDROID_SRC)/frameworks/base/core/java/android/content/pm/Signature.java

Source

    ...
    
    private static VerifierInfo parseVerifier(Resources res, XmlPullParser parser,
            AttributeSet attrs, int flags, String[] outError) throws XmlPullParserException,
            IOException {
        final TypedArray sa = res.obtainAttributes(attrs,
                com.android.internal.R.styleable.AndroidManifestPackageVerifier);
    
        final String packageName = sa.getNonResourceString(
                com.android.internal.R.styleable.AndroidManifestPackageVerifier_name);
    
        final String encodedPublicKey = sa.getNonResourceString(
                com.android.internal.R.styleable.AndroidManifestPackageVerifier_publicKey);
    
        sa.recycle();
    
        if (packageName == null || packageName.length() == 0) {
            Slog.i(TAG, "verifier package name was null; skipping");
            return null;
        } else if (encodedPublicKey == null) {
            Slog.i(TAG, "verifier " + packageName + " public key was null; skipping");
        }
    
        EncodedKeySpec keySpec;
        try {
            final byte[] encoded = Base64.decode(encodedPublicKey, Base64.DEFAULT);
            keySpec = new X509EncodedKeySpec(encoded);
        } catch (IllegalArgumentException e) {
            Slog.i(TAG, "Could not parse verifier " + packageName + " public key; invalid Base64");
            return null;
        }
    
        /* First try the key as an RSA key. */
        try {
            final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            final PublicKey publicKey = keyFactory.generatePublic(keySpec);
            return new VerifierInfo(packageName, publicKey);
        } catch (NoSuchAlgorithmException e) {
            Log.wtf(TAG, "Could not parse public key because RSA isn't included in build");
            return null;
        } catch (InvalidKeySpecException e) {
            // Not a RSA public key.
        }
    
        /* Now try it as a DSA key. */
        try {
            final KeyFactory keyFactory = KeyFactory.getInstance("DSA");
            final PublicKey publicKey = keyFactory.generatePublic(keySpec);
            return new VerifierInfo(packageName, publicKey);
        } catch (NoSuchAlgorithmException e) {
            Log.wtf(TAG, "Could not parse public key because DSA isn't included in build");
            return null;
        } catch (InvalidKeySpecException e) {
            // Not a DSA public key.
        }
    
        return null;
    }

    ...

As you can see the public key is created by Base64 decoding a String and then constructing an instance of

    java.security.spec.X509EncodedKeySpec

with the resulting bytes.

This is then turned into an instance of

    java.security.PublicKey

using brute force, which is interesting.

Given that the Android Application Manifest is an XML file this is perhaps not the best way to specify a public key.

Its not as though it hasn't been done before. See here for a different approach that even manages to include the information about what type of key it is.

There is also the question of the format of the data that needs to be specified.

The X509EncodedKeySpec constructor takes an encoded instance of one of these.

    SubjectPublicKeyInfo ::= SEQUENCE {
    algorithm AlgorithmIdentifier,
    subjectPublicKey BIT STRING }

which is defined in X.509

Now where would you get one of those ?

Given that

  1. it is defined in X.509 and

  2. that specifying a public keys is what a certificates is for

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.

August 10, 2014

And Another One: Part Five — All Done By Sleight Of Hand

There is in fact only one small sleight of hand involved in associating an arbitrary certificate with an Android application.

All the hard work is being done, or not being done as the case may be, by the android.content.pm.PackageParser method collectCertificates and associated Android system code.

File

    $(ANDROID_SRC)/frameworks/base/core/java/android/content/pm/PackageParser.java

Source

    ...
    
    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(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();
                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)) {
                        pkg.manifestDigest =
                            ManifestDigest.fromInputStream(jarFile.getInputStream(je));
                    }
                
                    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;
    }
                    
    ...

JarFile Construction

If we ignore the interesting read buffer memory optimization then the method starts by creating a JarFile instance.

            JarFile jarFile = new JarFile(mArchiveSourcePath);

It uses the single argument JarFile constructor

    public JarFile(File file) throws IOException

which simply calls the two argument constructor

    public JarFile(File file, boolean verify) throws IOException

with a second argument of true thereby ensuring the contents of the JAR will potentially be verified.

Certificate Collection

If the Android package is not a system package, which is the case we are interested in, the method iterates over all the members of the JAR containing the package.


        } else {
            Enumeration<JarEntry> entries = jarFile.entries();
            while (entries.hasMoreElements()) {
                final JarEntry je = entries.nextElement();

    

ignoring any member which is a directory


                if (je.isDirectory()) continue;
                
    

or in the META-INF directory


                final String name = je.getName();
    
                if (name.startsWith("META-INF/"))
                    continue;
                

It calls the method loadCertificates assigning the return value to the local variable localCerts


                final Certificate[] localCerts = loadCertificates(jarFile, je, readBuffer);
                

The method loadCertificates returns an array of java.security.cert.Certificates or null.

If the call to loadCertificates returns null then the method returns immediately and installation of the Android package fails.


                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;
                    

Otherwise if it is the first iteration the array of Certificates are cached in the local variable certs.


                } else if (certs == null) {
                    certs = localCerts;

On subsequent iterations the array of Certificates returned by the current call to loadCertificates are compared with those cached in the local variable certs.


                } 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;
                        }
                    }
                }

If any of the Certificates in certs are not found in localCerts or certs and localCerts are not of equal lengths the method returns immediately and installation of the Android package fails.

If the iteration over the members of the JAR completes successfully then the loadCertificates method has returned the same set of Certificates for each member of the JAR on which it was called.

Signature Construction

The signatures of the Android package are constructed from the array of Certificates cached in the local variable certs.


    
        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());
            }
        } ...

Each instance of android.content.pm.Signature is constructed with the encoded version of a Certificate.


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.

August 7, 2014

And Another One: Part Three — For My Next Trick …

This is a very simple Android application which dumps its certificates to standard out

    package xper.android.app.saithe;
    
    import java.io.ByteArrayInputStream;
    import java.security.Principal;
    import java.security.cert.Certificate;
    import java.security.cert.CertificateFactory;
    import java.security.cert.X509Certificate;
    
    import android.os.Bundle;
    import android.app.Activity;
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.content.pm.Signature;
    import android.view.Menu;
    
    public class MainActivity
                 extends
                     Activity
    {
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            PackageManager pm = getPackageManager();
            try
            {
                PackageInfo pi = pm.getPackageInfo(
                                        "xper.android.app.saithe", 
                                        PackageManager.GET_SIGNATURES);
    
                dumpCertificates(pi.signatures);
    
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu)
        {
            // Inflate the menu; this adds items to the action bar if it is present.
            getMenuInflater().inflate(R.menu.main, menu);
            return true;
        }
	
        private void dumpCertificates(Signature[] theSignatures)
                     throws
                         Exception
        {
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
    
            for (Signature s : theSignatures)
            {
                dumpCertificate(
                    cf.generateCertificate(
                           new ByteArrayInputStream(
                                   s.toByteArray())));
            }
        }
    
        private void dumpCertificate(Certificate theCertificate)
        {
            X509Certificate c = (X509Certificate)theCertificate;
    
            Principal issuer  = c.getIssuerDN();
            Principal subject = c.getSubjectDN();
    
            System.out.println(">> Certificate");
            System.out.println("Version:             " + c.getVersion());
            System.out.println("Serial number:       " + c.getSerialNumber());
            System.out.println("Signature Algorithm: " + c.getSigAlgName());
            System.out.println("Issuer:              " + issuer);
            System.out.println("Validity");
            System.out.println("    Not Before     : " + c.getNotBefore());
            System.out.println("    Not After      : " + c.getNotAfter());
            System.out.println("Subject:             " + subject);
            System.out.println("Raw");
            System.out.println(theCertificate);
            System.out.println("<< Certificate\n");
        }
    }

and this is what it outputs when run on a Nexus 7 running Android 4.3 (output slighly re-formatted)

    ...
    
    I/System.out( 3123): >> Certificate
    I/System.out( 3123): Version:             1
    I/System.out( 3123): Serial number:       16268562900721825114
    I/System.out( 3123): Signature Algorithm: SHA1WithRSA
    I/System.out( 3123): Issuer:              \
        CN=Adobe Systems Incorporated, OU=Information Systems, \
        O=Adobe Systems Incorporated, L=San Jose, ST=California, C=US
    I/System.out( 3123): Validity
    I/System.out( 3123):     Not Before     : Wed Aug 06 13:36:33 BST 2014
    I/System.out( 3123):     Not After      : Thu Aug 06 13:36:33 BST 2015
    I/System.out( 3123): Subject:             \
        CN=AndroidApplication Saithe, OU=AndroidApplication Saithe Group, \
        O=ASH Two, L=Emerald City, ST=Erehwon, C=OZ
    I/System.out( 3123): Raw
    I/System.out( 3123): Certificate:
    I/System.out( 3123):     Data:
    I/System.out( 3123):         Version: 1 (0x0)
    I/System.out( 3123):         Serial Number:
    I/System.out( 3123):             e1:c5:8b:c3:7f:9f:75:5a
    I/System.out( 3123):     Signature Algorithm: sha1WithRSAEncryption
    I/System.out( 3123):         Issuer: C=US
    I/System.out( 3123):         Validity
    I/System.out( 3123):             Not Before: Aug  6 12:36:33 2014 GMT
    I/System.out( 3123):             Not After : Aug  6 12:36:33 2015 GMT
    I/System.out( 3123):         Subject: C=OZ
    I/System.out( 3123):         Subject Public Key Info:
    I/System.out( 3123):             Public Key Algorithm: rsaEncryption
    I/System.out( 3123):                 Public-Key: (2048 bit)
    I/System.out( 3123):                 Modulus:
    I/System.out( 3123):                     00:8a:e3:50:64:5c:1c:40:1e:35:20:3b:35:d7:aa:
    I/System.out( 3123):                     b6:8e:c0:17:53:52:dc:72:df:ce:0a:27:f8:b6:cc:
    I/System.out( 3123):                     46:7b:86:a5:ea:13:7a:91:5b:9c:7b:ba:41:e3:26:
    I/System.out( 3123):                     88:2e:49:46:e6:13:25:f6:ed:63:f9:b6:85:83:7e:
    I/System.out( 3123):                     f8:b5:73:e8:fe:2b:90:a9:8d:c2:31:51:3e:2c:8e:
    I/System.out( 3123):                     9e:5a:ce:7d:c4:9f:fd:98:ce:b6:48:c5:e9:b6:db:
    I/System.out( 3123):                     ba:83:de:32:49:45:5c:61:ac:77:eb:95:c3:c7:c2:
    I/System.out( 3123):                     c0:bd:be:b6:c4:71:a5:66:bc:09:59:5a:dd:44:c7:
    I/System.out( 3123):                     0a:9c:08:01:b2:fa:81:e1:01:21:5c:b0:63:29:99:
    I/System.out( 3123):                     a0:fe:94:89:3d:58:57:fa:7c:30:79:a7:fa:f9:f8:
    I/System.out( 3123):                     c5:7c:25:67:ec:43:0a:6f:0a:85:5f:6a:76:bf:7a:
    I/System.out( 3123):                     d7:a8:e7:46:73:d4:07:bf:79:c8:c6:99:42:16:c9:
    I/System.out( 3123):                     f6:89:87:01:50:fd:2a:c1:c6:4a:cb:88:3d:b4:db:
    I/System.out( 3123):                     82:dc:6f:e1:65:17:ba:1f:cb:66:12:2d:de:fd:25:
    I/System.out( 3123):                     69:f6:07:8b:10:c5:94:21:ad:b3:02:68:4a:96:32:
    I/System.out( 3123):                     42:33:08:97:7c:fd:b7:55:33:d5:61:b9:73:43:bf:
    I/System.out( 3123):                     f5:db:25:e1:63:bb:03:0a:9e:f9:f5:e0:b7:d6:28:
    I/System.out( 3123):                     00:ff
    I/System.out( 3123):                 Exponent: 65537 (0x10001)
    I/System.out( 3123):     Signature Algorithm: sha1WithRSAEncryption
    I/System.out( 3123):          15:df:7e:12:13:d3:0c:08:9d:a0:11:35:74:66:90:c8:8b:cd:
    I/System.out( 3123):          9f:3f:be:e7:84:31:77:e5:cb:c8:51:0b:24:a3:b9:37:49:8e:
    I/System.out( 3123):          88:5c:d9:89:bf:e1:b7:92:6b:b7:29:be:15:8e:1e:d8:6d:81:
    I/System.out( 3123):          d0:01:38:ee:fa:a1:a7:7d:02:f1:22:09:e6:7b:e9:25:5e:2b:
    I/System.out( 3123):          07:d0:2f:3e:9b:cd:87:60:82:4d:dc:4c:0b:27:70:eb:54:b8:
    I/System.out( 3123):          83:c1:15:26:52:a7:61:f5:dd:b4:f9:4c:6f:cc:69:f9:16:a5:
    I/System.out( 3123):          74:e8:7d:84:35:46:b4:f1:d8:3b:97:4c:b7:4c:3a:62:7f:8c:
    I/System.out( 3123):          78:6d
    I/System.out( 3123): << Certificate
    I/System.out( 3123): >> Certificate
    I/System.out( 3123): Version:             3
    I/System.out( 3123): Serial number:       15549593810524997758
    I/System.out( 3123): Signature Algorithm: SHA1WithRSA
    I/System.out( 3123): Issuer:\
        CN=Adobe Systems Incorporated, OU=Information Systems, \
        O=Adobe Systems Incorporated, L=San Jose, ST=California, C=US
    I/System.out( 3123): Validity
    I/System.out( 3123):     Not Before     : Thu Oct 01 01:23:14 BST 2009
    I/System.out( 3123):     Not After      : Mon Feb 16 00:23:14 GMT+00:00 2037
    I/System.out( 3123): Subject:\
        CN=Adobe Systems Incorporated, OU=Information Systems, \
        O=Adobe Systems Incorporated, L=San Jose, ST=California, C=US
    I/System.out( 3123): Raw
    I/System.out( 3123): Certificate:
    I/System.out( 3123):     Data:
    I/System.out( 3123):         Version: 3 (0x2)
    I/System.out( 3123):         Serial Number:
    I/System.out( 3123):             d7:cb:41:2f:75:f4:88:7e
    I/System.out( 3123):     Signature Algorithm: sha1WithRSAEncryption
    I/System.out( 3123):         Issuer: C=US
    I/System.out( 3123):         Validity
    I/System.out( 3123):             Not Before: Oct  1 00:23:14 2009 GMT
    I/System.out( 3123):             Not After : Feb 16 00:23:14 2037 GMT
    I/System.out( 3123):         Subject: C=US
    I/System.out( 3123):         Subject Public Key Info:
    I/System.out( 3123):             Public Key Algorithm: rsaEncryption
    I/System.out( 3123):                 Public-Key: (2048 bit)
    I/System.out( 3123):                 Modulus:
    I/System.out( 3123):                     00:99:72:4f:3e:05:bb:d7:88:43:79:4f:35:77:76:
    I/System.out( 3123):                     e0:4b:34:0e:13:cb:1c:9c:cb:30:44:86:51:80:d7:
    I/System.out( 3123):                     d8:fe:c8:16:6c:5b:bd:87:6d:a8:b8:0a:a7:1e:b6:
    I/System.out( 3123):                     ba:3d:4d:34:55:c9:a8:de:16:2d:24:a2:5c:4c:1c:
    I/System.out( 3123):                     d0:4c:95:23:af:fd:06:a2:79:fc:8f:0d:01:8f:24:
    I/System.out( 3123):                     24:86:bd:bb:2d:bf:bf:6f:cb:21:ed:56:78:79:09:
    I/System.out( 3123):                     19:28:b8:76:f7:cc:eb:c7:bc:ce:f1:57:36:6e:be:
    I/System.out( 3123):                     74:e3:3a:e1:d7:e9:37:30:91:ad:ab:83:27:48:21:
    I/System.out( 3123):                     54:af:c0:69:3a:54:95:22:f8:c7:96:dd:84:d1:6e:
    I/System.out( 3123):                     24:bb:22:1f:5d:bb:80:9c:a5:6d:d2:b6:e7:99:c5:
    I/System.out( 3123):                     fa:06:b6:d9:c5:c0:9a:da:54:ea:4c:5d:b1:52:3a:
    I/System.out( 3123):                     97:94:ed:22:a3:88:9e:5e:05:b2:9f:8e:e0:a8:d6:
    I/System.out( 3123):                     1e:fe:07:ae:28:f6:5d:ec:e2:ff:7e:dc:5b:14:16:
    I/System.out( 3123):                     d7:c7:aa:d7:f0:d3:5e:8f:4a:4b:96:4d:bf:50:ae:
    I/System.out( 3123):                     9a:a6:d6:20:15:77:70:d9:74:13:1b:3e:7e:3a:bd:
    I/System.out( 3123):                     6d:16:3d:65:75:8e:2f:08:22:db:9c:88:59:8b:9d:
    I/System.out( 3123):                     b6:26:3d:96:3d:13:94:2c:91:fc:5e:fe:34:fc:1e:
    I/System.out( 3123):                     06:e3
    I/System.out( 3123):                 Exponent: 3 (0x3)
    I/System.out( 3123):         X509v3 extensions:
    I/System.out( 3123):             X509v3 Subject Key Identifier:
    I/System.out( 3123):                 5A:F4:18:E4:19:A6:39:E1:65:7D:B9:60:99:63:64:A3:7E:F2:0D:40
    I/System.out( 3123):             X509v3 Authority Key Identifier:
    I/System.out( 3123):                 keyid:5A:F4:18:E4:19:A6:39:E1:65:7D:B9:60:99:63:64:A3:7E:F2:0D:40
    I/System.out( 3123):                 DirName:\
        /C=US/ST=California/L=San Jose/O=Adobe Systems Incorporated/OU=Information Systems/CN=Adobe Systems Incorporated
    I/System.out( 3123):                 serial:D7:CB:41:2F:75:F4:88:7E
    I/System.out( 3123):             X509v3 Basic Constraints:
    I/System.out( 3123):                 CA:TRUE
    I/System.out( 3123):     Signature Algorithm: sha1WithRSAEncryption
    I/System.out( 3123):          76:c2:a1:1f:e3:03:35:96:89:c2:eb:c7:b2:c3:98:ef:f8:c3:
    I/System.out( 3123):          f9:ad:54:5c:db:ac:75:df:63:bf:7b:53:95:b6:98:8d:18:42:
    I/System.out( 3123):          d6:aa:15:56:d5:95:b5:69:2e:08:22:4d:66:7a:4c:9c:43:8f:
    I/System.out( 3123):          05:e7:49:06:c5:3d:d8:01:6d:de:70:04:06:88:66:f0:18:46:
    I/System.out( 3123):          36:5e:fd:14:6e:9b:fa:a4:8c:9e:cf:65:7f:87:b9:7c:75:7d:
    I/System.out( 3123):          a1:1f:22:5c:4a:24:17:7b:f2:d7:18:8e:6c:ce:2a:70:a1:e8:
    I/System.out( 3123):          a8:41:a1:44:71:eb:51:45:73:98:b8:a0:ad:dd:8b:6c:8c:15:
    I/System.out( 3123):          38:ca:8f:1e:40:b4:d8:b9:60:00:9e:a2:2c:18:8d:28:92:48:
    I/System.out( 3123):          13:d2:c0:b4:a4:d3:34:b7:cf:05:50:7e:1f:cf:0a:06:fe:94:
    I/System.out( 3123):          6c:7f:fc:43:5e:17:3a:f6:fc:3e:34:00:64:37:10:ac:c8:06:
    I/System.out( 3123):          f8:30:a1:47:88:29:1d:46:f2:fe:ed:9f:b5:c7:04:23:ca:74:
    I/System.out( 3123):          7e:d1:57:2d:75:28:94:ac:1f:19:f9:39:89:76:63:08:57:93:
    I/System.out( 3123):          93:fa:bb:43:64:9a:a8:80:6a:31:3b:1a:b9:a5:09:22:a4:4c:
    I/System.out( 3123):          24:67:b9:06:20:37:f2:da:0d:48:4d:9f:fd:8f:e6:28:ee:ea:
    I/System.out( 3123):          62:9b:a6:37
    I/System.out( 3123): << Certificate
        
    ...

As you can see I have been quite clever, although I say so myself, and have managed to get the hard-wired self-signed Adobe certificate used by the android.web.PluginManager as one of the application’s certificates

Just as well I have no ambitions to load any plugins into a WebView !


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.

And Another One: Part Two — Why Ouch ?

The section of the method PluginManager.containsPluginPermissionAndSignatures of interest is this

    ...
    
        // check to ensure the plugin is properly signed
        Signature signatures[] = pkgInfo.signatures;
        if (signatures == null) {
            return false;
        }
        if (SystemProperties.getBoolean("ro.secure", false)) {
            boolean signatureMatch = false;
            for (Signature signature : signatures) {
                for (int i = 0; i < SIGNATURES.length; i++) {
                    if (SIGNATURES[i].equals(signature)) {
                        signatureMatch = true;
                        break;
                    }
                }
            }
            if (!signatureMatch) {
                return false;
            }
        }
        
    ...

It is trying to determine whether any of the signatures of the given Android package are present in the array of signatures in the static variable SIGNATURES.

If so then

the plugin is properly signed

The static variable SIGNATURES is an array of length one.

    ...
    
    private static final Signature[] SIGNATURES = new Signature[] {
        new Signature(SIGNATURE_1)
    };
    
    ...

The signature it contains is hard-wired, its raw value being held in the static variable SIGNATURE_1.

    ...
    
    // Only plugin matches one of the signatures in the list can be loaded
    // inside the WebView process
    private static final String SIGNATURE_1 = "308204c5308203ada003020102020900d ...  629ba637";
    
    ...

The raw value is obviously a bunch of hex digits.

Less obviously, for some but not others, it is a DER encoded certificate which looks like this in DER-ese

      0 1221: SEQUENCE {
      4  941:   SEQUENCE {
      8    3:     [0] {
     10    1:       INTEGER 2
            :       }
     13    9:     INTEGER 00 D7 CB 41 2F 75 F4 88 7E
     24   13:     SEQUENCE {
     26    9:       OBJECT IDENTIFIER sha1withRSAEncryption (1 2 840 113549 1 1 5)
     37    0:       NULL
            :       }
     39  157:     SEQUENCE {
     42   11:       SET {
     44    9:         SEQUENCE {
     46    3:           OBJECT IDENTIFIER countryName (2 5 4 6)
     51    2:           PrintableString 'US'
            :           }
            :         }
     55   19:       SET {
     57   17:         SEQUENCE {
     59    3:           OBJECT IDENTIFIER stateOrProvinceName (2 5 4 8)
     64   10:           PrintableString 'California'
            :           }
            :         }
     76   17:       SET {
     78   15:         SEQUENCE {
     80    3:           OBJECT IDENTIFIER localityName (2 5 4 7)
     85    8:           PrintableString 'San Jose'
            :           }
            :         }
     95   35:       SET {
     97   33:         SEQUENCE {
     99    3:           OBJECT IDENTIFIER organizationName (2 5 4 10)
    104   26:           PrintableString 'Adobe Systems Incorporated'
            :           }
            :         }
    132   28:       SET {
    134   26:         SEQUENCE {
    136    3:           OBJECT IDENTIFIER organizationalUnitName (2 5 4 11)
    141   19:           PrintableString 'Information Systems'
            :           }
            :         }
    162   35:       SET {
    164   33:         SEQUENCE {
    166    3:           OBJECT IDENTIFIER commonName (2 5 4 3)
    171   26:           PrintableString 'Adobe Systems Incorporated'
            :           }
            :         }
            :       }
    199   30:     SEQUENCE {
    201   13:       UTCTime 01/10/2009 00:23:14 GMT
    216   13:       UTCTime 16/02/2037 00:23:14 GMT
            :       }
    231  157:     SEQUENCE {
    234   11:       SET {
    236    9:         SEQUENCE {
    238    3:           OBJECT IDENTIFIER countryName (2 5 4 6)
    243    2:           PrintableString 'US'
            :           }
            :         }
    247   19:       SET {
    249   17:         SEQUENCE {
    251    3:           OBJECT IDENTIFIER stateOrProvinceName (2 5 4 8)
    256   10:           PrintableString 'California'
            :           }
            :         }
    268   17:       SET {
    270   15:         SEQUENCE {
    272    3:           OBJECT IDENTIFIER localityName (2 5 4 7)
    277    8:           PrintableString 'San Jose'
            :           }
            :         }
    287   35:       SET {
    289   33:         SEQUENCE {
    291    3:           OBJECT IDENTIFIER organizationName (2 5 4 10)
    296   26:           PrintableString 'Adobe Systems Incorporated'
            :           }
            :         }
    324   28:       SET {
    326   26:         SEQUENCE {
    328    3:           OBJECT IDENTIFIER organizationalUnitName (2 5 4 11)
    333   19:           PrintableString 'Information Systems'
            :           }
            :         }
    354   35:       SET {
    356   33:         SEQUENCE {
    358    3:           OBJECT IDENTIFIER commonName (2 5 4 3)
    363   26:           PrintableString 'Adobe Systems Incorporated'
            :           }
            :         }
            :       }
    391  288:     SEQUENCE {
    395   13:       SEQUENCE {
    397    9:         OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
    408    0:         NULL
            :         }
    410  269:       BIT STRING, encapsulates {
    415  264:         SEQUENCE {
    419  257:           INTEGER
            :             00 99 72 4F 3E 05 BB D7 88 43 79 4F 35 77 76 E0
            :             4B 34 0E 13 CB 1C 9C CB 30 44 86 51 80 D7 D8 FE
            :             C8 16 6C 5B BD 87 6D A8 B8 0A A7 1E B6 BA 3D 4D
            :             34 55 C9 A8 DE 16 2D 24 A2 5C 4C 1C D0 4C 95 23
            :             AF FD 06 A2 79 FC 8F 0D 01 8F 24 24 86 BD BB 2D
            :             BF BF 6F CB 21 ED 56 78 79 09 19 28 B8 76 F7 CC
            :             EB C7 BC CE F1 57 36 6E BE 74 E3 3A E1 D7 E9 37
            :             30 91 AD AB 83 27 48 21 54 AF C0 69 3A 54 95 22
            :                     [ Another 129 bytes skipped ]
    680    1:           INTEGER 3
            :           }
            :         }
            :       }
    683  262:     [3] {
    687  258:       SEQUENCE {
    691   29:         SEQUENCE {
    693    3:           OBJECT IDENTIFIER subjectKeyIdentifier (2 5 29 14)
    698   22:           OCTET STRING, encapsulates {
    700   20:             OCTET STRING
            :               5A F4 18 E4 19 A6 39 E1 65 7D B9 60 99 63 64 A3
            :               7E F2 0D 40
            :             }
            :           }
    722  210:         SEQUENCE {
    725    3:           OBJECT IDENTIFIER authorityKeyIdentifier (2 5 29 35)
    730  202:           OCTET STRING, encapsulates {
    733  199:             SEQUENCE {
    736   20:               [0]
            :                 5A F4 18 E4 19 A6 39 E1 65 7D B9 60 99 63 64 A3
            :                 7E F2 0D 40
    758  163:               [1] {
    761  160:                 [4] {
    764  157:                   SEQUENCE {
    767   11:                     SET {
    769    9:                       SEQUENCE {
    771    3:                         OBJECT IDENTIFIER countryName (2 5 4 6)
    776    2:                         PrintableString 'US'
            :                         }
            :                       }
    780   19:                     SET {
    782   17:                       SEQUENCE {
    784    3:                         OBJECT IDENTIFIER
            :                           stateOrProvinceName (2 5 4 8)
    789   10:                         PrintableString 'California'
            :                         }
            :                       }
    801   17:                     SET {
    803   15:                       SEQUENCE {
    805    3:                         OBJECT IDENTIFIER localityName (2 5 4 7)
    810    8:                         PrintableString 'San Jose'
            :                         }
            :                       }
    820   35:                     SET {
    822   33:                       SEQUENCE {
    824    3:                         OBJECT IDENTIFIER organizationName (2 5 4 10)
    829   26:                         PrintableString 'Adobe Systems Incorporated'
            :                         }
            :                       }
    857   28:                     SET {
    859   26:                       SEQUENCE {
    861    3:                         OBJECT IDENTIFIER
            :                           organizationalUnitName (2 5 4 11)
    866   19:                         PrintableString 'Information Systems'
            :                         }
            :                       }
    887   35:                     SET {
    889   33:                       SEQUENCE {
    891    3:                         OBJECT IDENTIFIER commonName (2 5 4 3)
    896   26:                         PrintableString 'Adobe Systems Incorporated'
            :                         }
            :                       }
            :                     }
            :                   }
            :                 }
    924    9:               [2] 00 D7 CB 41 2F 75 F4 88 7E
            :               }
            :             }
            :           }
    935   12:         SEQUENCE {
    937    3:           OBJECT IDENTIFIER basicConstraints (2 5 29 19)
    942    5:           OCTET STRING, encapsulates {
    944    3:             SEQUENCE {
    946    1:               BOOLEAN TRUE
            :               }
            :             }
            :           }
            :         }
            :       }
            :     }
    949   13:   SEQUENCE {
    951    9:     OBJECT IDENTIFIER sha1withRSAEncryption (1 2 840 113549 1 1 5)
    962    0:     NULL
            :     }
    964  257:   BIT STRING
            :     76 C2 A1 1F E3 03 35 96 89 C2 EB C7 B2 C3 98 EF
            :     F8 C3 F9 AD 54 5C DB AC 75 DF 63 BF 7B 53 95 B6
            :     98 8D 18 42 D6 AA 15 56 D5 95 B5 69 2E 08 22 4D
            :     66 7A 4C 9C 43 8F 05 E7 49 06 C5 3D D8 01 6D DE
            :     70 04 06 88 66 F0 18 46 36 5E FD 14 6E 9B FA A4
            :     8C 9E CF 65 7F 87 B9 7C 75 7D A1 1F 22 5C 4A 24
            :     17 7B F2 D7 18 8E 6C CE 2A 70 A1 E8 A8 41 A1 44
            :     71 EB 51 45 73 98 B8 A0 AD DD 8B 6C 8C 15 38 CA
            :             [ Another 128 bytes skipped ]
            :   }

or this in X.509 speak

    Certificate:
        Data:
            Version: 3 (0x2)
            Serial Number:
                d7:cb:41:2f:75:f4:88:7e
            Signature Algorithm: sha1WithRSAEncryption
            Issuer: C=US, ST=California, L=San Jose, O=Adobe Systems Incorporated, \
    OU=Information Systems, CN=Adobe Systems Incorporated
            Validity
                Not Before: Oct  1 00:23:14 2009 GMT
                Not After : Feb 16 00:23:14 2037 GMT
            Subject: C=US, ST=California, L=San Jose, O=Adobe Systems Incorporated, \
    OU=Information Systems, CN=Adobe Systems Incorporated
            Subject Public Key Info:
                Public Key Algorithm: rsaEncryption
                RSA Public Key: (2048 bit)
                    Modulus (2048 bit):
                        00:99:72:4f:3e:05:bb:d7:88:43:79:4f:35:77:76:
                        e0:4b:34:0e:13:cb:1c:9c:cb:30:44:86:51:80:d7:
                        d8:fe:c8:16:6c:5b:bd:87:6d:a8:b8:0a:a7:1e:b6:
                        ba:3d:4d:34:55:c9:a8:de:16:2d:24:a2:5c:4c:1c:
                        d0:4c:95:23:af:fd:06:a2:79:fc:8f:0d:01:8f:24:
                        24:86:bd:bb:2d:bf:bf:6f:cb:21:ed:56:78:79:09:
                        19:28:b8:76:f7:cc:eb:c7:bc:ce:f1:57:36:6e:be:
                        74:e3:3a:e1:d7:e9:37:30:91:ad:ab:83:27:48:21:
                        54:af:c0:69:3a:54:95:22:f8:c7:96:dd:84:d1:6e:
                        24:bb:22:1f:5d:bb:80:9c:a5:6d:d2:b6:e7:99:c5:
                        fa:06:b6:d9:c5:c0:9a:da:54:ea:4c:5d:b1:52:3a:
                        97:94:ed:22:a3:88:9e:5e:05:b2:9f:8e:e0:a8:d6:
                        1e:fe:07:ae:28:f6:5d:ec:e2:ff:7e:dc:5b:14:16:
                        d7:c7:aa:d7:f0:d3:5e:8f:4a:4b:96:4d:bf:50:ae:
                        9a:a6:d6:20:15:77:70:d9:74:13:1b:3e:7e:3a:bd:
                        6d:16:3d:65:75:8e:2f:08:22:db:9c:88:59:8b:9d:
                        b6:26:3d:96:3d:13:94:2c:91:fc:5e:fe:34:fc:1e:
                        06:e3
                    Exponent: 3 (0x3)
            X509v3 extensions:
                X509v3 Subject Key Identifier:
                    5A:F4:18:E4:19:A6:39:E1:65:7D:B9:60:99:63:64:A3:7E:F2:0D:40
                X509v3 Authority Key Identifier:
                    keyid:5A:F4:18:E4:19:A6:39:E1:65:7D:B9:60:99:63:64:A3:7E:F2:0D:40
                    DirName:\
    /C=US/ST=California/L=San Jose/O=Adobe Systems Incorporated/OU=Information Systems/CN=Adobe Systems Incorporated
                    serial:D7:CB:41:2F:75:F4:88:7E
    
                X509v3 Basic Constraints:
                    CA:TRUE
        Signature Algorithm: sha1WithRSAEncryption
            76:c2:a1:1f:e3:03:35:96:89:c2:eb:c7:b2:c3:98:ef:f8:c3:
            f9:ad:54:5c:db:ac:75:df:63:bf:7b:53:95:b6:98:8d:18:42:
            d6:aa:15:56:d5:95:b5:69:2e:08:22:4d:66:7a:4c:9c:43:8f:
            05:e7:49:06:c5:3d:d8:01:6d:de:70:04:06:88:66:f0:18:46:
            36:5e:fd:14:6e:9b:fa:a4:8c:9e:cf:65:7f:87:b9:7c:75:7d:
            a1:1f:22:5c:4a:24:17:7b:f2:d7:18:8e:6c:ce:2a:70:a1:e8:
            a8:41:a1:44:71:eb:51:45:73:98:b8:a0:ad:dd:8b:6c:8c:15:
            38:ca:8f:1e:40:b4:d8:b9:60:00:9e:a2:2c:18:8d:28:92:48:
            13:d2:c0:b4:a4:d3:34:b7:cf:05:50:7e:1f:cf:0a:06:fe:94:
            6c:7f:fc:43:5e:17:3a:f6:fc:3e:34:00:64:37:10:ac:c8:06:
            f8:30:a1:47:88:29:1d:46:f2:fe:ed:9f:b5:c7:04:23:ca:74:
            7e:d1:57:2d:75:28:94:ac:1f:19:f9:39:89:76:63:08:57:93:
            93:fa:bb:43:64:9a:a8:80:6a:31:3b:1a:b9:a5:09:22:a4:4c:
            24:67:b9:06:20:37:f2:da:0d:48:4d:9f:fd:8f:e6:28:ee:ea:
            62:9b:a6:37

from which we can see that it is a self-signed certificate apparently originating from Adobe which expires in 2037 !

So why is a signature a certificate as opposed to, oh I don’t know

a message digest encrypted using a private key

say ?

There seems to be no reason other than at some point Android decided to use the term signature to mean certificate thereby confusing themselves and everybody else.

To summarize, a plugin is

properly signed

if the Android package has as one of its signatures certificates a specific certificate which is apparently issued by Adobe.

The intent behind this is presumably that only an Android package signed by Adobe using the hard-wired certificate can load a plugin into a WebView process.

Readers are invited to guess what plugin that might be.

Even as it stands relying on a self-signed hard-wired certificate that does not expire until 2037 is not really a good idea but it gets worse


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.

August 6, 2014

And Another One: Part One — Ouch !

    ...

    private static boolean containsPluginPermissionAndSignatures(PackageInfo pkgInfo) {
    
        // check if the plugin has the required permissions
        String permissions[] = pkgInfo.requestedPermissions;
        if (permissions == null) {
            return false;
        }
        boolean permissionOk = false;
        for (String permit : permissions) {
            if (PLUGIN_PERMISSION.equals(permit)) {
                permissionOk = true;
                break;
            }
        }
        if (!permissionOk) {
            return false;
        }
    
        // check to ensure the plugin is properly signed
        Signature signatures[] = pkgInfo.signatures;
        if (signatures == null) {
            return false;
        }
        if (SystemProperties.getBoolean("ro.secure", false)) {
            boolean signatureMatch = false;
            for (Signature signature : signatures) {
                for (int i = 0; i < SIGNATURES.length; i++) {
                    if (SIGNATURES[i].equals(signature)) {
                        signatureMatch = true;
                        break;
                    }
                }
            }
            if (!signatureMatch) {
                return false;
            }
        }
        
        return true;
    }
    
    ...

from

    $(ANDROID_SRC)/frameworks/base/core/java/android/webkit/PluginManager.java

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.

Create a free website or blog at WordPress.com.