1dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project/*
2dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
3dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project *
4dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * you may not use this file except in compliance with the License.
6dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * You may obtain a copy of the License at
7dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project *
8dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project *
10dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * See the License for the specific language governing permissions and
14dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * limitations under the License.
15dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */
16dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
17dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectpackage com.android.dumpkey;
18dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
19dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport java.io.FileInputStream;
20dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport java.math.BigInteger;
21dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport java.security.cert.CertificateFactory;
22dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport java.security.cert.Certificate;
23dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport java.security.KeyStore;
24dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport java.security.Key;
25dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport java.security.PublicKey;
26dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport java.security.interfaces.RSAPublicKey;
27dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
28dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project/**
29dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Command line tool to extract RSA public keys from X.509 certificates
30dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * and output source code with data initializers for the keys.
31dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * @hide
32dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */
33dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectclass DumpPublicKey {
34dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    /**
35dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project     * @param key to perform sanity checks on
3635d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker     * @return version number of key.  Supported versions are:
3735d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker     *     1: 2048-bit key with e=3
3835d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker     *     2: 2048-bit key with e=65537
39dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project     * @throws Exception if the key has the wrong size or public exponent
4035d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker
41dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project     */
4235d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker    static int check(RSAPublicKey key) throws Exception {
43dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        BigInteger pubexp = key.getPublicExponent();
44dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        BigInteger modulus = key.getModulus();
4535d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker        int version;
46dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
4735d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker        if (pubexp.equals(BigInteger.valueOf(3))) {
4835d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker            version = 1;
4935d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker        } else if (pubexp.equals(BigInteger.valueOf(65537))) {
5035d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker            version = 2;
5135d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker        } else {
5235d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker            throw new Exception("Public exponent should be 3 or 65537 but is " +
5335d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker                                pubexp.toString(10) + ".");
5435d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker        }
55dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
5635d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker        if (modulus.bitLength() != 2048) {
57dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project             throw new Exception("Modulus should be 2048 bits long but is " +
58dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                        modulus.bitLength() + " bits.");
5935d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker        }
6035d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker
6135d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker        return version;
62dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    }
63dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
64dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    /**
65dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project     * @param key to output
6635d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker     * @return a String representing this public key.  If the key is a
6735d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker     *    version 1 key, the string will be a C initializer; this is
6835d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker     *    not true for newer key versions.
69dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project     */
70dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    static String print(RSAPublicKey key) throws Exception {
7135d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker        int version = check(key);
72dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
73dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        BigInteger N = key.getModulus();
74dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
75dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        StringBuilder result = new StringBuilder();
76dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
77dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        int nwords = N.bitLength() / 32;    // # of 32 bit integers in modulus
78dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
7935d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker        if (version > 1) {
8035d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker            result.append("v");
8135d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker            result.append(Integer.toString(version));
8235d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker            result.append(" ");
8335d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker        }
8435d9ad5ae72de846967b91aed97060f0e8558661Doug Zongker
85dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        result.append("{");
86dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        result.append(nwords);
87dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
88dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        BigInteger B = BigInteger.valueOf(0x100000000L);  // 2^32
89dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        BigInteger N0inv = B.subtract(N.modInverse(B));   // -1 / N[0] mod 2^32
90dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
91dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        result.append(",0x");
92dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        result.append(N0inv.toString(16));
93dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
94dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        BigInteger R = BigInteger.valueOf(2).pow(N.bitLength());
95dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        BigInteger RR = R.multiply(R).mod(N);    // 2^4096 mod N
96dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
97dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        // Write out modulus as little endian array of integers.
98dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        result.append(",{");
99dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        for (int i = 0; i < nwords; ++i) {
1005e12d739c6b86b79d761f45a1c0399e0d42d1cb5Doug Zongker            long n = N.mod(B).longValue();
101dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            result.append(n);
102dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
103dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            if (i != nwords - 1) {
104dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                result.append(",");
105dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            }
106dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
107dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            N = N.divide(B);
108dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        }
109dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        result.append("}");
110dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
111dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        // Write R^2 as little endian array of integers.
112dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        result.append(",{");
113dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        for (int i = 0; i < nwords; ++i) {
1145e12d739c6b86b79d761f45a1c0399e0d42d1cb5Doug Zongker            long rr = RR.mod(B).longValue();
115dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            result.append(rr);
116dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
117dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            if (i != nwords - 1) {
118dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                result.append(",");
119dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            }
120dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
121dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            RR = RR.divide(B);
122dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        }
123dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        result.append("}");
124dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
125dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        result.append("}");
126dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        return result.toString();
127dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    }
128dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
129dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    public static void main(String[] args) {
130dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        if (args.length < 1) {
131dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            System.err.println("Usage: DumpPublicKey certfile ... > source.c");
132dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            System.exit(1);
133dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        }
134dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        try {
135dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            for (int i = 0; i < args.length; i++) {
136dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                FileInputStream input = new FileInputStream(args[i]);
137dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                CertificateFactory cf = CertificateFactory.getInstance("X.509");
138dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                Certificate cert = cf.generateCertificate(input);
139dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                RSAPublicKey key = (RSAPublicKey) (cert.getPublicKey());
140dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                check(key);
141dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                System.out.print(print(key));
142dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                System.out.println(i < args.length - 1 ? "," : "");
143dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            }
144dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        } catch (Exception e) {
145dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            e.printStackTrace();
146dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            System.exit(1);
147dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        }
148dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        System.exit(0);
149dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    }
150dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project}
151