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