1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dumpkey;
18
19import java.io.FileInputStream;
20import java.math.BigInteger;
21import java.security.cert.CertificateFactory;
22import java.security.cert.Certificate;
23import java.security.KeyStore;
24import java.security.Key;
25import java.security.PublicKey;
26import java.security.interfaces.RSAPublicKey;
27import sun.misc.BASE64Encoder;
28
29/**
30 * Command line tool to extract RSA public keys from X.509 certificates
31 * and output source code with data initializers for the keys.
32 * @hide
33 */
34class DumpPublicKey {
35    /**
36     * @param key to perform sanity checks on
37     * @throws Exception if the key has the wrong size or public exponent
38     */
39    static void check(RSAPublicKey key) throws Exception {
40        BigInteger pubexp = key.getPublicExponent();
41        BigInteger modulus = key.getModulus();
42
43        if (!pubexp.equals(BigInteger.valueOf(3)))
44                throw new Exception("Public exponent should be 3 but is " +
45                        pubexp.toString(10) + ".");
46
47        if (modulus.bitLength() != 2048)
48             throw new Exception("Modulus should be 2048 bits long but is " +
49                        modulus.bitLength() + " bits.");
50    }
51
52    /**
53     * @param key to output
54     * @return a C initializer representing this public key.
55     */
56    static String print(RSAPublicKey key) throws Exception {
57        check(key);
58
59        BigInteger N = key.getModulus();
60
61        StringBuilder result = new StringBuilder();
62
63        int nwords = N.bitLength() / 32;    // # of 32 bit integers in modulus
64
65        result.append("{");
66        result.append(nwords);
67
68        BigInteger B = BigInteger.valueOf(0x100000000L);  // 2^32
69        BigInteger N0inv = B.subtract(N.modInverse(B));   // -1 / N[0] mod 2^32
70
71        result.append(",0x");
72        result.append(N0inv.toString(16));
73
74        BigInteger R = BigInteger.valueOf(2).pow(N.bitLength());
75        BigInteger RR = R.multiply(R).mod(N);    // 2^4096 mod N
76
77        // Write out modulus as little endian array of integers.
78        result.append(",{");
79        for (int i = 0; i < nwords; ++i) {
80            long n = N.mod(B).longValue();
81            result.append(n);
82
83            if (i != nwords - 1) {
84                result.append(",");
85            }
86
87            N = N.divide(B);
88        }
89        result.append("}");
90
91        // Write R^2 as little endian array of integers.
92        result.append(",{");
93        for (int i = 0; i < nwords; ++i) {
94            long rr = RR.mod(B).longValue();
95            result.append(rr);
96
97            if (i != nwords - 1) {
98                result.append(",");
99            }
100
101            RR = RR.divide(B);
102        }
103        result.append("}");
104
105        result.append("}");
106        return result.toString();
107    }
108
109    public static void main(String[] args) {
110        if (args.length < 1) {
111            System.err.println("Usage: DumpPublicKey certfile ... > source.c");
112            System.exit(1);
113        }
114        try {
115            for (int i = 0; i < args.length; i++) {
116                FileInputStream input = new FileInputStream(args[i]);
117                CertificateFactory cf = CertificateFactory.getInstance("X.509");
118                Certificate cert = cf.generateCertificate(input);
119                RSAPublicKey key = (RSAPublicKey) (cert.getPublicKey());
120                check(key);
121                System.out.print(print(key));
122                System.out.println(i < args.length - 1 ? "," : "");
123            }
124        } catch (Exception e) {
125            e.printStackTrace();
126            System.exit(1);
127        }
128        System.exit(0);
129    }
130}
131