Home | History | Annotate | Download | only in utility
      1 /* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
      2  * Use of this source code is governed by a BSD-style license that can be
      3  * found in the LICENSE file.
      4  */
      5 
      6 /* C port of DumpPublicKey.java from the Android Open source project with
      7  * support for additional RSA key sizes. (platform/system/core,git/libmincrypt
      8  * /tools/DumpPublicKey.java). Uses the OpenSSL X509 and BIGNUM library.
      9  */
     10 
     11 #include <openssl/pem.h>
     12 
     13 #include <stdint.h>
     14 #include <string.h>
     15 #include <unistd.h>
     16 
     17 /* Command line tool to extract RSA public keys from X.509 certificates
     18  * and output a pre-processed version of keys for use by RSA verification
     19  * routines.
     20  */
     21 
     22 int check(RSA* key) {
     23   int public_exponent = BN_get_word(key->e);
     24   int modulus = BN_num_bits(key->n);
     25 
     26   if (public_exponent != 65537) {
     27     fprintf(stderr, "WARNING: Public exponent should be 65537 (but is %d).\n",
     28             public_exponent);
     29   }
     30 
     31   if (modulus != 1024 && modulus != 2048 && modulus != 4096
     32       && modulus != 8192) {
     33     fprintf(stderr, "ERROR: Unknown modulus length = %d.\n", modulus);
     34     return 0;
     35   }
     36   return 1;
     37 }
     38 
     39 /* Pre-processes and outputs RSA public key to standard out.
     40  */
     41 void output(RSA* key) {
     42   int i, nwords;
     43   BIGNUM *N = key->n;
     44   BIGNUM *Big1 = NULL, *Big2 = NULL, *Big32 = NULL, *BigMinus1 = NULL;
     45   BIGNUM *B = NULL;
     46   BIGNUM *N0inv= NULL, *R = NULL, *RR = NULL, *RRTemp = NULL, *NnumBits = NULL;
     47   BIGNUM *n = NULL, *rr = NULL;
     48   BN_CTX *bn_ctx = BN_CTX_new();
     49   uint32_t n0invout;
     50 
     51   N = key->n;
     52   /* Output size of RSA key in 32-bit words */
     53   nwords = BN_num_bits(N) / 32;
     54   if (-1 == write(1, &nwords, sizeof(nwords)))
     55     goto failure;
     56 
     57 
     58   /* Initialize BIGNUMs */
     59   Big1 = BN_new();
     60   Big2 = BN_new();
     61   Big32 = BN_new();
     62   BigMinus1 = BN_new();
     63   N0inv= BN_new();
     64   R = BN_new();
     65   RR = BN_new();
     66   RRTemp = BN_new();
     67   NnumBits = BN_new();
     68   n = BN_new();
     69   rr = BN_new();
     70 
     71 
     72   BN_set_word(Big1, 1L);
     73   BN_set_word(Big2, 2L);
     74   BN_set_word(Big32, 32L);
     75   BN_sub(BigMinus1, Big1, Big2);
     76 
     77   B = BN_new();
     78   BN_exp(B, Big2, Big32, bn_ctx); /* B = 2^32 */
     79 
     80   /* Calculate and output N0inv = -1 / N[0] mod 2^32 */
     81   BN_mod_inverse(N0inv, N, B, bn_ctx);
     82   BN_sub(N0inv, B, N0inv);
     83   n0invout = BN_get_word(N0inv);
     84   if (-1 == write(1, &n0invout, sizeof(n0invout)))
     85     goto failure;
     86 
     87   /* Calculate R = 2^(# of key bits) */
     88   BN_set_word(NnumBits, BN_num_bits(N));
     89   BN_exp(R, Big2, NnumBits, bn_ctx);
     90 
     91   /* Calculate RR = R^2 mod N */
     92   BN_copy(RR, R);
     93   BN_mul(RRTemp, RR, R, bn_ctx);
     94   BN_mod(RR, RRTemp, N, bn_ctx);
     95 
     96 
     97   /* Write out modulus as little endian array of integers. */
     98   for (i = 0; i < nwords; ++i) {
     99     uint32_t nout;
    100 
    101     BN_mod(n, N, B, bn_ctx); /* n = N mod B */
    102     nout = BN_get_word(n);
    103     if (-1 == write(1, &nout, sizeof(nout)))
    104       goto failure;
    105 
    106     BN_rshift(N, N, 32); /*  N = N/B */
    107   }
    108 
    109   /* Write R^2 as little endian array of integers. */
    110   for (i = 0; i < nwords; ++i) {
    111     uint32_t rrout;
    112 
    113     BN_mod(rr, RR, B, bn_ctx); /* rr = RR mod B */
    114     rrout = BN_get_word(rr);
    115     if (-1 == write(1, &rrout, sizeof(rrout)))
    116       goto failure;
    117 
    118     BN_rshift(RR, RR, 32); /* RR = RR/B */
    119   }
    120 
    121 failure:
    122   /* Free BIGNUMs. */
    123   BN_free(Big1);
    124   BN_free(Big2);
    125   BN_free(Big32);
    126   BN_free(BigMinus1);
    127   BN_free(N0inv);
    128   BN_free(R);
    129   BN_free(RRTemp);
    130   BN_free(NnumBits);
    131   BN_free(n);
    132   BN_free(rr);
    133 
    134 }
    135 
    136 int main(int argc, char* argv[]) {
    137   int cert_mode = 0;
    138   FILE* fp;
    139   X509* cert = NULL;
    140   RSA* pubkey = NULL;
    141   EVP_PKEY* key;
    142   char *progname;
    143 
    144   if (argc != 3 || (strcmp(argv[1], "-cert") && strcmp(argv[1], "-pub"))) {
    145     progname = strrchr(argv[0], '/');
    146     if (progname)
    147       progname++;
    148     else
    149       progname = argv[0];
    150     fprintf(stderr, "Usage: %s <-cert | -pub> <file>\n", progname);
    151     return -1;
    152   }
    153 
    154   if (!strcmp(argv[1], "-cert"))
    155     cert_mode = 1;
    156 
    157   fp = fopen(argv[2], "r");
    158 
    159   if (!fp) {
    160     fprintf(stderr, "Couldn't open file %s!\n", argv[2]);
    161     return -1;
    162   }
    163 
    164   if (cert_mode) {
    165     /* Read the certificate */
    166     if (!PEM_read_X509(fp, &cert, NULL, NULL)) {
    167       fprintf(stderr, "Couldn't read certificate.\n");
    168       goto fail;
    169     }
    170 
    171     /* Get the public key from the certificate. */
    172     key = X509_get_pubkey(cert);
    173 
    174     /* Convert to a RSA_style key. */
    175     if (!(pubkey = EVP_PKEY_get1_RSA(key))) {
    176       fprintf(stderr, "Couldn't convert to a RSA style key.\n");
    177       goto fail;
    178     }
    179   } else {
    180     /* Read the pubkey in .PEM format. */
    181     if (!(pubkey = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL))) {
    182       fprintf(stderr, "Couldn't read public key file.\n");
    183       goto fail;
    184     }
    185   }
    186 
    187   if (check(pubkey)) {
    188     output(pubkey);
    189   }
    190 
    191 fail:
    192   X509_free(cert);
    193   RSA_free(pubkey);
    194   fclose(fp);
    195 
    196   return 0;
    197 }
    198