Home | History | Annotate | Download | only in tools
      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 
     17 package com.android.dumpkey;
     18 
     19 import java.io.FileInputStream;
     20 import java.math.BigInteger;
     21 import java.security.cert.CertificateFactory;
     22 import java.security.cert.Certificate;
     23 import java.security.KeyStore;
     24 import java.security.Key;
     25 import java.security.PublicKey;
     26 import java.security.interfaces.RSAPublicKey;
     27 import 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  */
     34 class 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