1322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah * Use of this source code is governed by a BSD-style license that can be
3322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah * found in the LICENSE file.
4322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah */
5322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
6322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah/* C port of DumpPublicKey.java from the Android Open source project with
7322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah * support for additional RSA key sizes. (platform/system/core,git/libmincrypt
8322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah * /tools/DumpPublicKey.java). Uses the OpenSSL X509 and BIGNUM library.
9322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah */
10322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
11322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah#include <openssl/pem.h>
124e4c19602edf3834b50d66d3ba067e895aca6fa0Bill Richardson
134e4c19602edf3834b50d66d3ba067e895aca6fa0Bill Richardson#include <stdint.h>
14322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah#include <string.h>
15322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah#include <unistd.h>
16322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
17322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah/* Command line tool to extract RSA public keys from X.509 certificates
18322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah * and output a pre-processed version of keys for use by RSA verification
19322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah * routines.
20322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah */
21322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
22322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shahint check(RSA* key) {
23322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  int public_exponent = BN_get_word(key->e);
24322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  int modulus = BN_num_bits(key->n);
25322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
26322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  if (public_exponent != 65537) {
27322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah    fprintf(stderr, "WARNING: Public exponent should be 65537 (but is %d).\n",
28322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah            public_exponent);
29322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  }
30322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
31322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  if (modulus != 1024 && modulus != 2048 && modulus != 4096
32322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah      && modulus != 8192) {
33322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah    fprintf(stderr, "ERROR: Unknown modulus length = %d.\n", modulus);
34322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah    return 0;
35322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  }
36322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  return 1;
37322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah}
38322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
39322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah/* Pre-processes and outputs RSA public key to standard out.
40322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah */
41322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shahvoid output(RSA* key) {
42322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  int i, nwords;
43322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BIGNUM *N = key->n;
4448ed9b87eb1a8d09523778ee5005111bc0662a34Gaurav Shah  BIGNUM *Big1 = NULL, *Big2 = NULL, *Big32 = NULL, *BigMinus1 = NULL;
4548ed9b87eb1a8d09523778ee5005111bc0662a34Gaurav Shah  BIGNUM *B = NULL;
4648ed9b87eb1a8d09523778ee5005111bc0662a34Gaurav Shah  BIGNUM *N0inv= NULL, *R = NULL, *RR = NULL, *RRTemp = NULL, *NnumBits = NULL;
4748ed9b87eb1a8d09523778ee5005111bc0662a34Gaurav Shah  BIGNUM *n = NULL, *rr = NULL;
48322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_CTX *bn_ctx = BN_CTX_new();
49322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  uint32_t n0invout;
50322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
51322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  N = key->n;
52322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  /* Output size of RSA key in 32-bit words */
53322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  nwords = BN_num_bits(N) / 32;
5416ca324d4373a754a2314ad79c075a61c2b5ea9aGaurav Shah  if (-1 == write(1, &nwords, sizeof(nwords)))
5516ca324d4373a754a2314ad79c075a61c2b5ea9aGaurav Shah    goto failure;
5616ca324d4373a754a2314ad79c075a61c2b5ea9aGaurav Shah
57322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
58322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  /* Initialize BIGNUMs */
59322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  Big1 = BN_new();
60322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  Big2 = BN_new();
61322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  Big32 = BN_new();
62322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BigMinus1 = BN_new();
63322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  N0inv= BN_new();
64322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  R = BN_new();
65322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  RR = BN_new();
66322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  RRTemp = BN_new();
67322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  NnumBits = BN_new();
68322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  n = BN_new();
69322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  rr = BN_new();
70322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
71322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
72322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_set_word(Big1, 1L);
73322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_set_word(Big2, 2L);
74322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_set_word(Big32, 32L);
75322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_sub(BigMinus1, Big1, Big2);
76322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
77322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  B = BN_new();
78322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_exp(B, Big2, Big32, bn_ctx); /* B = 2^32 */
79322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
80322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  /* Calculate and output N0inv = -1 / N[0] mod 2^32 */
81322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_mod_inverse(N0inv, N, B, bn_ctx);
82322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_sub(N0inv, B, N0inv);
83322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  n0invout = BN_get_word(N0inv);
8416ca324d4373a754a2314ad79c075a61c2b5ea9aGaurav Shah  if (-1 == write(1, &n0invout, sizeof(n0invout)))
8516ca324d4373a754a2314ad79c075a61c2b5ea9aGaurav Shah    goto failure;
86322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
87322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  /* Calculate R = 2^(# of key bits) */
88322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_set_word(NnumBits, BN_num_bits(N));
89322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_exp(R, Big2, NnumBits, bn_ctx);
90322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
91322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  /* Calculate RR = R^2 mod N */
92322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_copy(RR, R);
93322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_mul(RRTemp, RR, R, bn_ctx);
94322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_mod(RR, RRTemp, N, bn_ctx);
95322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
96322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
97322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  /* Write out modulus as little endian array of integers. */
98322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  for (i = 0; i < nwords; ++i) {
99322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah    uint32_t nout;
100322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
101322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah    BN_mod(n, N, B, bn_ctx); /* n = N mod B */
102322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah    nout = BN_get_word(n);
10316ca324d4373a754a2314ad79c075a61c2b5ea9aGaurav Shah    if (-1 == write(1, &nout, sizeof(nout)))
10416ca324d4373a754a2314ad79c075a61c2b5ea9aGaurav Shah      goto failure;
105322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
106322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah    BN_rshift(N, N, 32); /*  N = N/B */
107322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  }
108322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
109322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  /* Write R^2 as little endian array of integers. */
110322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  for (i = 0; i < nwords; ++i) {
111322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah    uint32_t rrout;
112322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
113322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah    BN_mod(rr, RR, B, bn_ctx); /* rr = RR mod B */
114322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah    rrout = BN_get_word(rr);
11516ca324d4373a754a2314ad79c075a61c2b5ea9aGaurav Shah    if (-1 == write(1, &rrout, sizeof(rrout)))
11616ca324d4373a754a2314ad79c075a61c2b5ea9aGaurav Shah      goto failure;
117322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
118322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah    BN_rshift(RR, RR, 32); /* RR = RR/B */
119322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  }
120322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
12116ca324d4373a754a2314ad79c075a61c2b5ea9aGaurav Shahfailure:
122322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  /* Free BIGNUMs. */
123322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_free(Big1);
124322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_free(Big2);
125322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_free(Big32);
126322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_free(BigMinus1);
127322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_free(N0inv);
128322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_free(R);
129322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_free(RRTemp);
130322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_free(NnumBits);
131322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_free(n);
132322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  BN_free(rr);
133322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
134322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah}
135322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
136322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shahint main(int argc, char* argv[]) {
137551037b10e427687b7115751c7d613d66459c427Gaurav Shah  int cert_mode = 0;
138322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  FILE* fp;
139322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  X509* cert = NULL;
140322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  RSA* pubkey = NULL;
141322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  EVP_PKEY* key;
142feb2518166b1cd181e607c611cbb610f0c7300daBill Richardson  char *progname;
143322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
144551037b10e427687b7115751c7d613d66459c427Gaurav Shah  if (argc != 3 || (strcmp(argv[1], "-cert") && strcmp(argv[1], "-pub"))) {
145feb2518166b1cd181e607c611cbb610f0c7300daBill Richardson    progname = strrchr(argv[0], '/');
146feb2518166b1cd181e607c611cbb610f0c7300daBill Richardson    if (progname)
147feb2518166b1cd181e607c611cbb610f0c7300daBill Richardson      progname++;
148feb2518166b1cd181e607c611cbb610f0c7300daBill Richardson    else
149feb2518166b1cd181e607c611cbb610f0c7300daBill Richardson      progname = argv[0];
150feb2518166b1cd181e607c611cbb610f0c7300daBill Richardson    fprintf(stderr, "Usage: %s <-cert | -pub> <file>\n", progname);
151322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah    return -1;
152322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  }
153322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
154551037b10e427687b7115751c7d613d66459c427Gaurav Shah  if (!strcmp(argv[1], "-cert"))
155551037b10e427687b7115751c7d613d66459c427Gaurav Shah    cert_mode = 1;
156551037b10e427687b7115751c7d613d66459c427Gaurav Shah
157551037b10e427687b7115751c7d613d66459c427Gaurav Shah  fp = fopen(argv[2], "r");
158322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
159322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  if (!fp) {
160551037b10e427687b7115751c7d613d66459c427Gaurav Shah    fprintf(stderr, "Couldn't open file %s!\n", argv[2]);
161322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah    return -1;
162322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  }
163322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
164551037b10e427687b7115751c7d613d66459c427Gaurav Shah  if (cert_mode) {
165551037b10e427687b7115751c7d613d66459c427Gaurav Shah    /* Read the certificate */
166551037b10e427687b7115751c7d613d66459c427Gaurav Shah    if (!PEM_read_X509(fp, &cert, NULL, NULL)) {
167551037b10e427687b7115751c7d613d66459c427Gaurav Shah      fprintf(stderr, "Couldn't read certificate.\n");
168551037b10e427687b7115751c7d613d66459c427Gaurav Shah      goto fail;
169551037b10e427687b7115751c7d613d66459c427Gaurav Shah    }
170551037b10e427687b7115751c7d613d66459c427Gaurav Shah
171551037b10e427687b7115751c7d613d66459c427Gaurav Shah    /* Get the public key from the certificate. */
172551037b10e427687b7115751c7d613d66459c427Gaurav Shah    key = X509_get_pubkey(cert);
173551037b10e427687b7115751c7d613d66459c427Gaurav Shah
174551037b10e427687b7115751c7d613d66459c427Gaurav Shah    /* Convert to a RSA_style key. */
175551037b10e427687b7115751c7d613d66459c427Gaurav Shah    if (!(pubkey = EVP_PKEY_get1_RSA(key))) {
176551037b10e427687b7115751c7d613d66459c427Gaurav Shah      fprintf(stderr, "Couldn't convert to a RSA style key.\n");
177551037b10e427687b7115751c7d613d66459c427Gaurav Shah      goto fail;
178551037b10e427687b7115751c7d613d66459c427Gaurav Shah    }
179551037b10e427687b7115751c7d613d66459c427Gaurav Shah  } else {
180551037b10e427687b7115751c7d613d66459c427Gaurav Shah    /* Read the pubkey in .PEM format. */
181551037b10e427687b7115751c7d613d66459c427Gaurav Shah    if (!(pubkey = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL))) {
182551037b10e427687b7115751c7d613d66459c427Gaurav Shah      fprintf(stderr, "Couldn't read public key file.\n");
183551037b10e427687b7115751c7d613d66459c427Gaurav Shah      goto fail;
184551037b10e427687b7115751c7d613d66459c427Gaurav Shah    }
185322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  }
186322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
187322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  if (check(pubkey)) {
188551037b10e427687b7115751c7d613d66459c427Gaurav Shah    output(pubkey);
189322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  }
190322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
191322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shahfail:
192322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  X509_free(cert);
193322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  RSA_free(pubkey);
194322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  fclose(fp);
195322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah
196322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah  return 0;
197322536d2f9d30f42218cc9f2ab40574557da8a9Gaurav Shah}
198