1cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra/* 2cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * Copyright (C) 2014 The Android Open Source Project 3cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * 4cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * Licensed under the Apache License, Version 2.0 (the "License"); 5cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * you may not use this file except in compliance with the License. 6cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * You may obtain a copy of the License at 7cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * 8cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * http://www.apache.org/licenses/LICENSE-2.0 9cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * 10cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * Unless required by applicable law or agreed to in writing, software 11cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * distributed under the License is distributed on an "AS IS" BASIS, 12cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * See the License for the specific language governing permissions and 14cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * limitations under the License. 15cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra */ 16cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 17cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condrapackage com.android.verity; 18cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 19cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.lang.reflect.Constructor; 20cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.io.File; 21f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport java.io.ByteArrayInputStream; 22f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport java.io.Console; 23cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.io.FileInputStream; 24cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.io.FileOutputStream; 25f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport java.io.InputStreamReader; 26cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.io.IOException; 27f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport java.security.GeneralSecurityException; 28f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport java.security.Key; 29cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.security.PrivateKey; 30cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.security.PublicKey; 31cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.security.KeyFactory; 32cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.security.Provider; 33cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.security.Security; 34cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.security.Signature; 35f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport java.security.cert.Certificate; 36f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport java.security.cert.CertificateFactory; 37f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport java.security.cert.X509Certificate; 3843548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanenimport java.security.spec.ECPublicKeySpec; 3943548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanenimport java.security.spec.ECPrivateKeySpec; 40cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.security.spec.X509EncodedKeySpec; 41cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.security.spec.PKCS8EncodedKeySpec; 42f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport java.security.spec.InvalidKeySpecException; 43f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport java.util.Arrays; 44f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport java.util.HashMap; 45f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport java.util.Map; 46f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen 47f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport javax.crypto.Cipher; 48f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport javax.crypto.EncryptedPrivateKeyInfo; 49f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport javax.crypto.SecretKeyFactory; 50f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport javax.crypto.spec.PBEKeySpec; 51f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen 52f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport org.bouncycastle.asn1.ASN1InputStream; 53f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport org.bouncycastle.asn1.ASN1ObjectIdentifier; 54f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport org.bouncycastle.asn1.pkcs.PrivateKeyInfo; 55f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 56f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanenimport org.bouncycastle.asn1.x509.AlgorithmIdentifier; 5743548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanenimport org.bouncycastle.asn1.x9.X9ObjectIdentifiers; 58cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.util.encoders.Base64; 59cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 60cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condrapublic class Utils { 61cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 62f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen private static final Map<String, String> ID_TO_ALG; 63f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen private static final Map<String, String> ALG_TO_ID; 64f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen 65f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen static { 66f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen ID_TO_ALG = new HashMap<String, String>(); 67f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen ALG_TO_ID = new HashMap<String, String>(); 68f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen 6943548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA256.getId(), "SHA256withECDSA"); 7043548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA384.getId(), "SHA384withECDSA"); 7143548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen ID_TO_ALG.put(X9ObjectIdentifiers.ecdsa_with_SHA512.getId(), "SHA512withECDSA"); 72f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen ID_TO_ALG.put(PKCSObjectIdentifiers.sha1WithRSAEncryption.getId(), "SHA1withRSA"); 73f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen ID_TO_ALG.put(PKCSObjectIdentifiers.sha256WithRSAEncryption.getId(), "SHA256withRSA"); 74f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen ID_TO_ALG.put(PKCSObjectIdentifiers.sha512WithRSAEncryption.getId(), "SHA512withRSA"); 75f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen 7643548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen ALG_TO_ID.put("SHA256withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA256.getId()); 7743548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen ALG_TO_ID.put("SHA384withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA384.getId()); 7843548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen ALG_TO_ID.put("SHA512withECDSA", X9ObjectIdentifiers.ecdsa_with_SHA512.getId()); 79f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen ALG_TO_ID.put("SHA1withRSA", PKCSObjectIdentifiers.sha1WithRSAEncryption.getId()); 80f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen ALG_TO_ID.put("SHA256withRSA", PKCSObjectIdentifiers.sha256WithRSAEncryption.getId()); 81f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen ALG_TO_ID.put("SHA512withRSA", PKCSObjectIdentifiers.sha512WithRSAEncryption.getId()); 82f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen } 83f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen 84cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra private static void loadProviderIfNecessary(String providerClassName) { 85cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra if (providerClassName == null) { 86cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return; 87cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 88cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 89cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra final Class<?> klass; 90cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra try { 91cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra final ClassLoader sysLoader = ClassLoader.getSystemClassLoader(); 92cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra if (sysLoader != null) { 93cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra klass = sysLoader.loadClass(providerClassName); 94cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } else { 95cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra klass = Class.forName(providerClassName); 96cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 97cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } catch (ClassNotFoundException e) { 98cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra e.printStackTrace(); 99cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra System.exit(1); 100cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return; 101cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 102cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 103cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra Constructor<?> constructor = null; 104cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra for (Constructor<?> c : klass.getConstructors()) { 105cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra if (c.getParameterTypes().length == 0) { 106cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra constructor = c; 107cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra break; 108cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 109cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 110cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra if (constructor == null) { 111cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra System.err.println("No zero-arg constructor found for " + providerClassName); 112cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra System.exit(1); 113cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return; 114cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 115cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 116cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra final Object o; 117cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra try { 118cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra o = constructor.newInstance(); 119cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } catch (Exception e) { 120cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra e.printStackTrace(); 121cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra System.exit(1); 122cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return; 123cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 124cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra if (!(o instanceof Provider)) { 125cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra System.err.println("Not a Provider class: " + providerClassName); 126cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra System.exit(1); 127cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 128cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 129cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra Security.insertProviderAt((Provider) o, 1); 130cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 131cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 132cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra static byte[] pemToDer(String pem) throws Exception { 133cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra pem = pem.replaceAll("^-.*", ""); 134cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra String base64_der = pem.replaceAll("-.*$", ""); 135cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return Base64.decode(base64_der); 136cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 137cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 138f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen private static PKCS8EncodedKeySpec decryptPrivateKey(byte[] encryptedPrivateKey) 139f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen throws GeneralSecurityException { 140f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen EncryptedPrivateKeyInfo epkInfo; 141f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen try { 142f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen epkInfo = new EncryptedPrivateKeyInfo(encryptedPrivateKey); 143f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen } catch (IOException ex) { 144f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen // Probably not an encrypted key. 145f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen return null; 146f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen } 147f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen 148f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen char[] password = System.console().readPassword("Password for the private key file: "); 149f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen 150f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen SecretKeyFactory skFactory = SecretKeyFactory.getInstance(epkInfo.getAlgName()); 151f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen Key key = skFactory.generateSecret(new PBEKeySpec(password)); 152f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen Arrays.fill(password, '\0'); 153f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen 154f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen Cipher cipher = Cipher.getInstance(epkInfo.getAlgName()); 155f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen cipher.init(Cipher.DECRYPT_MODE, key, epkInfo.getAlgParameters()); 156f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen 157f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen try { 158f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen return epkInfo.getKeySpec(cipher); 159f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen } catch (InvalidKeySpecException ex) { 160f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen System.err.println("Password may be bad."); 161f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen throw ex; 162f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen } 163f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen } 164f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen 165cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra static PrivateKey loadDERPrivateKey(byte[] der) throws Exception { 166f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen PKCS8EncodedKeySpec spec = decryptPrivateKey(der); 167f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen 168f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen if (spec == null) { 169f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen spec = new PKCS8EncodedKeySpec(der); 170f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen } 171f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen 172f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(spec.getEncoded())); 173f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject()); 174f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId(); 175f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen 176f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen return KeyFactory.getInstance(algOid).generatePrivate(spec); 177cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 178cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 179cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra static PrivateKey loadPEMPrivateKey(byte[] pem) throws Exception { 180cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra byte[] der = pemToDer(new String(pem)); 181cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return loadDERPrivateKey(der); 182cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 183cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 184cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra static PrivateKey loadPEMPrivateKeyFromFile(String keyFname) throws Exception { 185cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return loadPEMPrivateKey(read(keyFname)); 186cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 187cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 188cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra static PrivateKey loadDERPrivateKeyFromFile(String keyFname) throws Exception { 189cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return loadDERPrivateKey(read(keyFname)); 190cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 191cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 192cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra static PublicKey loadDERPublicKey(byte[] der) throws Exception { 193cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(der); 194cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra KeyFactory factory = KeyFactory.getInstance("RSA"); 195cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return factory.generatePublic(publicKeySpec); 196cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 197cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 198cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra static PublicKey loadPEMPublicKey(byte[] pem) throws Exception { 199cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra byte[] der = pemToDer(new String(pem)); 200cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(der); 201cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra KeyFactory factory = KeyFactory.getInstance("RSA"); 202cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return factory.generatePublic(publicKeySpec); 203cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 204cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 205cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra static PublicKey loadPEMPublicKeyFromFile(String keyFname) throws Exception { 206cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return loadPEMPublicKey(read(keyFname)); 207cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 208cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 209cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra static PublicKey loadDERPublicKeyFromFile(String keyFname) throws Exception { 210cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return loadDERPublicKey(read(keyFname)); 211cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 212cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 213f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen static X509Certificate loadPEMCertificate(String fname) throws Exception { 214f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen try (FileInputStream fis = new FileInputStream(fname)) { 215f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen CertificateFactory cf = CertificateFactory.getInstance("X.509"); 216f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen return (X509Certificate) cf.generateCertificate(fis); 217f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen } 218f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen } 219f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen 22043548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen private static String getSignatureAlgorithm(Key key) throws Exception { 22143548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen if ("EC".equals(key.getAlgorithm())) { 22243548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen int curveSize; 22343548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen KeyFactory factory = KeyFactory.getInstance("EC"); 22443548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen 22543548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen if (key instanceof PublicKey) { 22643548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen ECPublicKeySpec spec = factory.getKeySpec(key, ECPublicKeySpec.class); 22743548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen curveSize = spec.getParams().getCurve().getField().getFieldSize(); 22843548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen } else if (key instanceof PrivateKey) { 22943548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen ECPrivateKeySpec spec = factory.getKeySpec(key, ECPrivateKeySpec.class); 23043548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen curveSize = spec.getParams().getCurve().getField().getFieldSize(); 23143548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen } else { 23243548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen throw new InvalidKeySpecException(); 23343548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen } 23443548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen 23543548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen if (curveSize <= 256) { 23643548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen return "SHA256withECDSA"; 23743548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen } else if (curveSize <= 384) { 23843548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen return "SHA384withECDSA"; 23943548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen } else { 24043548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen return "SHA512withECDSA"; 24143548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen } 24243548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen } else if ("RSA".equals(key.getAlgorithm())) { 243f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen return "SHA256withRSA"; 244f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen } else { 245f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen throw new IllegalArgumentException("Unsupported key type " + key.getAlgorithm()); 246f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen } 247f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen } 248f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen 24943548de0ef3b04e04ee26c910caf3b379a945b01Sami Tolvanen static AlgorithmIdentifier getSignatureAlgorithmIdentifier(Key key) throws Exception { 250f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen String id = ALG_TO_ID.get(getSignatureAlgorithm(key)); 251f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen 252f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen if (id == null) { 253f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen throw new IllegalArgumentException("Unsupported key type " + key.getAlgorithm()); 254f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen } 255f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen 256f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen return new AlgorithmIdentifier(new ASN1ObjectIdentifier(id)); 257f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen } 258f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen 2597999c089cfa82d63d4a28606c78b381b74509175Sami Tolvanen static boolean verify(PublicKey key, byte[] input, byte[] signature, 2607999c089cfa82d63d4a28606c78b381b74509175Sami Tolvanen AlgorithmIdentifier algId) throws Exception { 261ab9a0c802d88ff47f3467fd66415a27f8f141b19Kenny Root String algName = ID_TO_ALG.get(algId.getAlgorithm().getId()); 2627999c089cfa82d63d4a28606c78b381b74509175Sami Tolvanen 2637999c089cfa82d63d4a28606c78b381b74509175Sami Tolvanen if (algName == null) { 264ab9a0c802d88ff47f3467fd66415a27f8f141b19Kenny Root throw new IllegalArgumentException("Unsupported algorithm " + algId.getAlgorithm()); 2657999c089cfa82d63d4a28606c78b381b74509175Sami Tolvanen } 2667999c089cfa82d63d4a28606c78b381b74509175Sami Tolvanen 2677999c089cfa82d63d4a28606c78b381b74509175Sami Tolvanen Signature verifier = Signature.getInstance(algName); 2687999c089cfa82d63d4a28606c78b381b74509175Sami Tolvanen verifier.initVerify(key); 2697999c089cfa82d63d4a28606c78b381b74509175Sami Tolvanen verifier.update(input); 2707999c089cfa82d63d4a28606c78b381b74509175Sami Tolvanen 2717999c089cfa82d63d4a28606c78b381b74509175Sami Tolvanen return verifier.verify(signature); 2727999c089cfa82d63d4a28606c78b381b74509175Sami Tolvanen } 2737999c089cfa82d63d4a28606c78b381b74509175Sami Tolvanen 274cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra static byte[] sign(PrivateKey privateKey, byte[] input) throws Exception { 275f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen Signature signer = Signature.getInstance(getSignatureAlgorithm(privateKey)); 276cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra signer.initSign(privateKey); 277cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra signer.update(input); 278cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return signer.sign(); 279cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 280cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 281cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra static byte[] read(String fname) throws Exception { 282cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra long offset = 0; 283cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra File f = new File(fname); 284cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra long length = f.length(); 285cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra byte[] image = new byte[(int)length]; 286cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra FileInputStream fis = new FileInputStream(f); 287cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra while (offset < length) { 288cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra offset += fis.read(image, (int)offset, (int)(length - offset)); 289cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 290cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra fis.close(); 291cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return image; 292cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 293cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 294cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra static void write(byte[] data, String fname) throws Exception{ 295cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra FileOutputStream out = new FileOutputStream(fname); 296cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra out.write(data); 297cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra out.close(); 298cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 299f67d3764c641e53d4a1b925b4d02fec92b7e894aSami Tolvanen} 300