1 /*-
2 * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10 /*
11 * Example showing how to generate an RSA key pair.
12 *
13 * When generating an RSA key, you must specify the number of bits in the key. A
14 * reasonable value would be 4096. Avoid using values below 2048. These values
15 * are reasonable as of 2022.
16 */
17
18 #include <string.h>
19 #include <stdio.h>
20 #include <openssl/err.h>
21 #include <openssl/evp.h>
22 #include <openssl/rsa.h>
23 #include <openssl/core_names.h>
24 #include <openssl/pem.h>
25
26 /* A property query used for selecting algorithm implementations. */
27 static const char *propq = NULL;
28
29 /*
30 * Generates an RSA public-private key pair and returns it.
31 * The number of bits is specified by the bits argument.
32 *
33 * This uses the long way of generating an RSA key.
34 */
generate_rsa_key_long(OSSL_LIB_CTX * libctx,unsigned int bits)35 static EVP_PKEY *generate_rsa_key_long(OSSL_LIB_CTX *libctx, unsigned int bits)
36 {
37 EVP_PKEY_CTX *genctx = NULL;
38 EVP_PKEY *pkey = NULL;
39 unsigned int primes = 2;
40
41 /* Create context using RSA algorithm. "RSA-PSS" could also be used here. */
42 genctx = EVP_PKEY_CTX_new_from_name(libctx, "RSA", propq);
43 if (genctx == NULL) {
44 fprintf(stderr, "EVP_PKEY_CTX_new_from_name() failed\n");
45 goto cleanup;
46 }
47
48 /* Initialize context for key generation purposes. */
49 if (EVP_PKEY_keygen_init(genctx) <= 0) {
50 fprintf(stderr, "EVP_PKEY_keygen_init() failed\n");
51 goto cleanup;
52 }
53
54 /*
55 * Here we set the number of bits to use in the RSA key.
56 * See comment at top of file for information on appropriate values.
57 */
58 if (EVP_PKEY_CTX_set_rsa_keygen_bits(genctx, bits) <= 0) {
59 fprintf(stderr, "EVP_PKEY_CTX_set_rsa_keygen_bits() failed\n");
60 goto cleanup;
61 }
62
63 /*
64 * It is possible to create an RSA key using more than two primes.
65 * Do not do this unless you know why you need this.
66 * You ordinarily do not need to specify this, as the default is two.
67 *
68 * Both of these parameters can also be set via EVP_PKEY_CTX_set_params, but
69 * these functions provide a more concise way to do so.
70 */
71 if (EVP_PKEY_CTX_set_rsa_keygen_primes(genctx, primes) <= 0) {
72 fprintf(stderr, "EVP_PKEY_CTX_set_rsa_keygen_primes() failed\n");
73 goto cleanup;
74 }
75
76 /*
77 * Generating an RSA key with a number of bits large enough to be secure for
78 * modern applications can take a fairly substantial amount of time (e.g.
79 * one second). If you require fast key generation, consider using an EC key
80 * instead.
81 *
82 * If you require progress information during the key generation process,
83 * you can set a progress callback using EVP_PKEY_set_cb; see the example in
84 * EVP_PKEY_generate(3).
85 */
86 fprintf(stderr, "Generating RSA key, this may take some time...\n");
87 if (EVP_PKEY_generate(genctx, &pkey) <= 0) {
88 fprintf(stderr, "EVP_PKEY_generate() failed\n");
89 goto cleanup;
90 }
91
92 /* pkey is now set to an object representing the generated key pair. */
93
94 cleanup:
95 EVP_PKEY_CTX_free(genctx);
96 return pkey;
97 }
98
99 /*
100 * Generates an RSA public-private key pair and returns it.
101 * The number of bits is specified by the bits argument.
102 *
103 * This uses a more concise way of generating an RSA key, which is suitable for
104 * simple cases. It is used if -s is passed on the command line, otherwise the
105 * long method above is used. The ability to choose between these two methods is
106 * shown here only for demonstration; the results are equivalent.
107 */
generate_rsa_key_short(OSSL_LIB_CTX * libctx,unsigned int bits)108 static EVP_PKEY *generate_rsa_key_short(OSSL_LIB_CTX *libctx, unsigned int bits)
109 {
110 EVP_PKEY *pkey = NULL;
111
112 fprintf(stderr, "Generating RSA key, this may take some time...\n");
113 pkey = EVP_PKEY_Q_keygen(libctx, propq, "RSA", (size_t)bits);
114
115 if (pkey == NULL)
116 fprintf(stderr, "EVP_PKEY_Q_keygen() failed\n");
117
118 return pkey;
119 }
120
121 /*
122 * Prints information on an EVP_PKEY object representing an RSA key pair.
123 */
dump_key(const EVP_PKEY * pkey)124 static int dump_key(const EVP_PKEY *pkey)
125 {
126 int rv = 0;
127 int bits = 0;
128 BIGNUM *n = NULL, *e = NULL, *d = NULL, *p = NULL, *q = NULL;
129
130 /*
131 * Retrieve value of n. This value is not secret and forms part of the
132 * public key.
133 *
134 * Calling EVP_PKEY_get_bn_param with a NULL BIGNUM pointer causes
135 * a new BIGNUM to be allocated, so these must be freed subsequently.
136 */
137 if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &n) == 0) {
138 fprintf(stderr, "Failed to retrieve n\n");
139 goto cleanup;
140 }
141
142 /*
143 * Retrieve value of e. This value is not secret and forms part of the
144 * public key. It is typically 65537 and need not be changed.
145 */
146 if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_E, &e) == 0) {
147 fprintf(stderr, "Failed to retrieve e\n");
148 goto cleanup;
149 }
150
151 /*
152 * Retrieve value of d. This value is secret and forms part of the private
153 * key. It must not be published.
154 */
155 if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_D, &d) == 0) {
156 fprintf(stderr, "Failed to retrieve d\n");
157 goto cleanup;
158 }
159
160 /*
161 * Retrieve value of the first prime factor, commonly known as p. This value
162 * is secret and forms part of the private key. It must not be published.
163 */
164 if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_FACTOR1, &p) == 0) {
165 fprintf(stderr, "Failed to retrieve p\n");
166 goto cleanup;
167 }
168
169 /*
170 * Retrieve value of the second prime factor, commonly known as q. This value
171 * is secret and forms part of the private key. It must not be published.
172 *
173 * If you are creating an RSA key with more than two primes for special
174 * applications, you can retrieve these primes with
175 * OSSL_PKEY_PARAM_RSA_FACTOR3, etc.
176 */
177 if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_FACTOR2, &q) == 0) {
178 fprintf(stderr, "Failed to retrieve q\n");
179 goto cleanup;
180 }
181
182 /*
183 * We can also retrieve the key size in bits for informational purposes.
184 */
185 if (EVP_PKEY_get_int_param(pkey, OSSL_PKEY_PARAM_BITS, &bits) == 0) {
186 fprintf(stderr, "Failed to retrieve bits\n");
187 goto cleanup;
188 }
189
190 /* Output hexadecimal representations of the BIGNUM objects. */
191 fprintf(stdout, "\nNumber of bits: %d\n\n", bits);
192 fprintf(stderr, "Public values:\n");
193 fprintf(stdout, " n = 0x");
194 BN_print_fp(stdout, n);
195 fprintf(stdout, "\n");
196
197 fprintf(stdout, " e = 0x");
198 BN_print_fp(stdout, e);
199 fprintf(stdout, "\n\n");
200
201 fprintf(stdout, "Private values:\n");
202 fprintf(stdout, " d = 0x");
203 BN_print_fp(stdout, d);
204 fprintf(stdout, "\n");
205
206 fprintf(stdout, " p = 0x");
207 BN_print_fp(stdout, p);
208 fprintf(stdout, "\n");
209
210 fprintf(stdout, " q = 0x");
211 BN_print_fp(stdout, q);
212 fprintf(stdout, "\n\n");
213
214 /* Output a PEM encoding of the public key. */
215 if (PEM_write_PUBKEY(stdout, pkey) == 0) {
216 fprintf(stderr, "Failed to output PEM-encoded public key\n");
217 goto cleanup;
218 }
219
220 /*
221 * Output a PEM encoding of the private key. Please note that this output is
222 * not encrypted. You may wish to use the arguments to specify encryption of
223 * the key if you are storing it on disk. See PEM_write_PrivateKey(3).
224 */
225 if (PEM_write_PrivateKey(stdout, pkey, NULL, NULL, 0, NULL, NULL) == 0) {
226 fprintf(stderr, "Failed to output PEM-encoded private key\n");
227 goto cleanup;
228 }
229
230 rv = 1;
231 cleanup:
232 BN_free(n); /* not secret */
233 BN_free(e); /* not secret */
234 BN_clear_free(d); /* secret - scrub before freeing */
235 BN_clear_free(p); /* secret - scrub before freeing */
236 BN_clear_free(q); /* secret - scrub before freeing */
237 return rv;
238 }
239
main(int argc,char ** argv)240 int main(int argc, char **argv)
241 {
242 int rv = 1;
243 OSSL_LIB_CTX *libctx = NULL;
244 EVP_PKEY *pkey = NULL;
245 unsigned int bits = 4096;
246 int bits_i, use_short = 0;
247
248 /* usage: [-s] [<bits>] */
249 if (argc > 1 && strcmp(argv[1], "-s") == 0) {
250 --argc;
251 ++argv;
252 use_short = 1;
253 }
254
255 if (argc > 1) {
256 bits_i = atoi(argv[1]);
257 if (bits < 512) {
258 fprintf(stderr, "Invalid RSA key size\n");
259 return 1;
260 }
261
262 bits = (unsigned int)bits_i;
263 }
264
265 /* Avoid using key sizes less than 2048 bits; see comment at top of file. */
266 if (bits < 2048)
267 fprintf(stderr, "Warning: very weak key size\n\n");
268
269 /* Generate RSA key. */
270 if (use_short)
271 pkey = generate_rsa_key_short(libctx, bits);
272 else
273 pkey = generate_rsa_key_long(libctx, bits);
274
275 if (pkey == NULL)
276 goto cleanup;
277
278 /* Dump the integers comprising the key. */
279 if (dump_key(pkey) == 0) {
280 fprintf(stderr, "Failed to dump key\n");
281 goto cleanup;
282 }
283
284 rv = 0;
285 cleanup:
286 EVP_PKEY_free(pkey);
287 OSSL_LIB_CTX_free(libctx);
288 return rv;
289 }
290