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 
check(RSA * key)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  */
output(RSA * key)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 
main(int argc,char * argv[])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