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
public abstract void verify(PublicKey key)
throws
CertificateException,
NoSuchAlgorithmException,
InvalidKeyException,
NoSuchProviderException,
SignatureException
which
Verifies that this certificate was signed using the private key that corresponds to the specified public key.
Verifying A Certificate Chain
This is a modified version of the original application which uses the java.security.certificate.Certificate
verify
method to verify its certificates as encapsulated in its signatures.
package xper.android.app.saithe;
import java.io.ByteArrayInputStream;
import java.security.Principal;
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);
boolean verified = verifyCertificates(pi.signatures);
System.out.println("verified == " + verified);
}
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 boolean verifyCertificates(Signature[] theSignatures)
throws
Exception
{
X509Certificate[] certificates = makeCertificates(theSignatures);
return verifyCertificates(certificates[0], certificates);
}
private boolean verifyCertificates(X509Certificate theSignerCertificate, X509Certificate[] theCertificates)
throws
Exception
{
X509Certificate current = theSignerCertificate;
while (true)
{
System.out.println("current subject == " + current.getSubjectDN());
Principal issuer = current.getIssuerDN();
System.out.println("current issuer == " + issuer);
X509Certificate issuerCertificate = findCertificate(issuer, theCertificates);
if (issuerCertificate == null)
{
return false;
}
current.verify(issuerCertificate.getPublicKey());
if (current.getSubjectDN().equals(issuer))
{
return true;
}
current = issuerCertificate;
}
}
private X509Certificate findCertificate(Principal theIssuer, X509Certificate[] theCertificates)
{
for (X509Certificate c : theCertificates)
{
if (c.getSubjectDN().equals(theIssuer))
{
return c;
}
}
return null;
}
private X509Certificate[] makeCertificates(Signature[] theSignatures)
throws
Exception
{
CertificateFactory cf = CertificateFactory.getInstance("X.509");
int nCertificates = theSignatures.length;
X509Certificate[] certificates = new X509Certificate[nCertificates];
for (int i = 0; i < nCertificates; i++)
{
certificates[i] = (X509Certificate)cf.generateCertificate(
new ByteArrayInputStream(
theSignatures[i].toByteArray()));
}
return certificates;
}
}
and this is what happens when you run it
...
I/ActivityManager( 516): Start proc xper.android.app.saithe for activity xper.android.app.saithe/.MainActivity: pid=2296 uid=10077 gids={50077, 1028}
D/dalvikvm( 2296): Late-enabling CheckJNI
I/System.out( 2296): current subject == CN=AndroidApplication Saithe, OU=AndroidApplication Saithe Group, \
O=ASH Two, L=Emerald City, ST=Erehwon, C=OZ
I/System.out( 2296): current issuer == CN=Adobe Systems Incorporated, OU=Information Systems, \
O=Adobe Systems Incorporated, L=San Jose, ST=California, C=US
W/System.err( 2296): java.security.SignatureException: error:04091077:rsa routines:INT_RSA_VERIFY:wrong signature length
W/System.err( 2296): at org.apache.harmony.xnet.provider.jsse.NativeCrypto.X509_verify(Native Method)
W/System.err( 2296): at org.apache.harmony.xnet.provider.jsse.OpenSSLX509Certificate.verifyOpenSSL(OpenSSLX509Certificate.java:333)
W/System.err( 2296): at org.apache.harmony.xnet.provider.jsse.OpenSSLX509Certificate.verify(OpenSSLX509Certificate.java:366)
W/System.err( 2296): at xper.android.app.saithe.MainActivity.verifyCertificates(MainActivity.java:122)
W/System.err( 2296): at xper.android.app.saithe.MainActivity.verifyCertificates(MainActivity.java:99)
W/System.err( 2296): at xper.android.app.saithe.MainActivity.onCreate(MainActivity.java:40)
W/System.err( 2296): at android.app.Activity.performCreate(Activity.java:5133)
W/System.err( 2296): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
W/System.err( 2296): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2175)
W/System.err( 2296): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2261)
W/System.err( 2296): at android.app.ActivityThread.access$600(ActivityThread.java:141)
W/System.err( 2296): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
W/System.err( 2296): at android.os.Handler.dispatchMessage(Handler.java:99)
W/System.err( 2296): at android.os.Looper.loop(Looper.java:137)
W/System.err( 2296): at android.app.ActivityThread.main(ActivityThread.java:5103)
W/System.err( 2296): at java.lang.reflect.Method.invokeNative(Native Method)
W/System.err( 2296): at java.lang.reflect.Method.invoke(Method.java:525)
W/System.err( 2296): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
W/System.err( 2296): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
W/System.err( 2296): at dalvik.system.NativeStart.main(Native Method)
...
Not very surprising really.
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.