Commits

Stephen Smalley committed 53f7b73 Merge

Merge branch 'master' into seandroid

Comments (0)

Files changed (1)

tools/signapk/SignApk.java

 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.DEROutputStream;
 import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.cert.jcajce.JcaCertStore;
 import org.bouncycastle.cms.CMSException;
 import org.bouncycastle.cms.CMSProcessableByteArray;
 import org.bouncycastle.util.encoders.Base64;
 
 import java.io.BufferedReader;
-import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
 import java.io.File;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.PrintStream;
+import java.lang.reflect.Constructor;
 import java.security.DigestOutputStream;
 import java.security.GeneralSecurityException;
 import java.security.Key;
 import java.security.KeyFactory;
 import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.Provider;
-import java.security.PublicKey;
 import java.security.Security;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.security.spec.InvalidKeySpecException;
-import java.security.spec.KeySpec;
 import java.security.spec.PKCS8EncodedKeySpec;
 import java.util.ArrayList;
 import java.util.Collections;
     }
 
     /**
-     * Decrypt an encrypted PKCS 8 format private key.
+     * Decrypt an encrypted PKCS#8 format private key.
      *
      * Based on ghstark's post on Aug 6, 2006 at
      * http://forums.sun.com/thread.jspa?threadID=758133&messageID=4330949
      * @param encryptedPrivateKey The raw data of the private key
      * @param keyFile The file containing the private key
      */
-    private static KeySpec decryptPrivateKey(byte[] encryptedPrivateKey, File keyFile)
+    private static PKCS8EncodedKeySpec decryptPrivateKey(byte[] encryptedPrivateKey, File keyFile)
         throws GeneralSecurityException {
         EncryptedPrivateKeyInfo epkInfo;
         try {
         }
     }
 
-    /** Read a PKCS 8 format private key. */
+    /** Read a PKCS#8 format private key. */
     private static PrivateKey readPrivateKey(File file)
         throws IOException, GeneralSecurityException {
         DataInputStream input = new DataInputStream(new FileInputStream(file));
             byte[] bytes = new byte[(int) file.length()];
             input.read(bytes);
 
-            KeySpec spec = decryptPrivateKey(bytes, file);
+            /* Check to see if this is in an EncryptedPrivateKeyInfo structure. */
+            PKCS8EncodedKeySpec spec = decryptPrivateKey(bytes, file);
             if (spec == null) {
                 spec = new PKCS8EncodedKeySpec(bytes);
             }
 
-            PrivateKey key;
-            key = decodeAsKeyType(spec, "RSA");
-            if (key != null) {
-                return key;
-            }
-
-            key = decodeAsKeyType(spec, "EC");
-            if (key != null) {
-                return key;
-            }
+            /*
+             * Now it's in a PKCS#8 PrivateKeyInfo structure. Read its Algorithm
+             * OID and use that to construct a KeyFactory.
+             */
+            ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(spec.getEncoded()));
+            PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject());
+            String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId();
 
-            throw new NoSuchAlgorithmException("Must be an EC or RSA key");
+            return KeyFactory.getInstance(algOid).generatePrivate(spec);
         } finally {
             input.close();
         }
     }
 
-    private static PrivateKey decodeAsKeyType(KeySpec spec, String keyType)
-            throws GeneralSecurityException {
-        try {
-            return KeyFactory.getInstance(keyType).generatePrivate(spec);
-        } catch (InvalidKeySpecException e) {
-            return null;
-        }
-    }
-
     /**
      * Add the hash(es) of every file to the manifest, creating it if
      * necessary.
             outputJar.write(signedData);
 
             // CERT.{EC,RSA} / CERT#.{EC,RSA}
+            final String keyType = publicKey[k].getPublicKey().getAlgorithm();
             je = new JarEntry(numKeys == 1 ?
-                              (String.format(CERT_SIG_NAME, privateKey[k].getAlgorithm())) :
-                              (String.format(CERT_SIG_MULTI_NAME, k, privateKey[k].getAlgorithm())));
+                              (String.format(CERT_SIG_NAME, keyType)) :
+                              (String.format(CERT_SIG_MULTI_NAME, k, keyType)));
             je.setTime(timestamp);
             outputJar.putNextEntry(je);
             writeSignatureBlock(new CMSProcessableByteArray(signedData),
         }
     }
 
+    /**
+     * Tries to load a JSE Provider by class name. This is for custom PrivateKey
+     * types that might be stored in PKCS#11-like storage.
+     */
+    private static void loadProviderIfNecessary(String providerClassName) {
+        if (providerClassName == null) {
+            return;
+        }
+
+        final Class<?> klass;
+        try {
+            final ClassLoader sysLoader = ClassLoader.getSystemClassLoader();
+            if (sysLoader != null) {
+                klass = sysLoader.loadClass(providerClassName);
+            } else {
+                klass = Class.forName(providerClassName);
+            }
+        } catch (ClassNotFoundException e) {
+            e.printStackTrace();
+            System.exit(1);
+            return;
+        }
+
+        Constructor<?> constructor = null;
+        for (Constructor<?> c : klass.getConstructors()) {
+            if (c.getParameterTypes().length == 0) {
+                constructor = c;
+                break;
+            }
+        }
+        if (constructor == null) {
+            System.err.println("No zero-arg constructor found for " + providerClassName);
+            System.exit(1);
+            return;
+        }
+
+        final Object o;
+        try {
+            o = constructor.newInstance();
+        } catch (Exception e) {
+            e.printStackTrace();
+            System.exit(1);
+            return;
+        }
+        if (!(o instanceof Provider)) {
+            System.err.println("Not a Provider class: " + providerClassName);
+            System.exit(1);
+        }
+
+        Security.insertProviderAt((Provider) o, 1);
+    }
+
     private static void usage() {
         System.err.println("Usage: signapk [-w] " +
+                           "[-providerClass <className>] " +
                            "publickey.x509[.pem] privatekey.pk8 " +
                            "[publickey2.x509[.pem] privatekey2.pk8 ...] " +
                            "input.jar output.jar");
         Security.addProvider(sBouncyCastleProvider);
 
         boolean signWholeFile = false;
+        String providerClass = null;
+        String providerArg = null;
+
         int argstart = 0;
-        if (args[0].equals("-w")) {
-            signWholeFile = true;
-            argstart = 1;
+        while (argstart < args.length && args[argstart].startsWith("-")) {
+            if ("-w".equals(args[argstart])) {
+                signWholeFile = true;
+                ++argstart;
+            } else if ("-providerClass".equals(args[argstart])) {
+                if (argstart + 1 >= args.length) {
+                    usage();
+                }
+                providerClass = args[++argstart];
+                ++argstart;
+            } else {
+                usage();
+            }
         }
 
         if ((args.length - argstart) % 2 == 1) usage();
             System.exit(2);
         }
 
+        loadProviderIfNecessary(providerClass);
+
         String inputFilename = args[args.length-2];
         String outputFilename = args[args.length-1];