1 /* $OpenBSD: genpkey.c,v 1.14 2022/11/11 17:07:39 joshua Exp $ */ 2 /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL 3 * project 2006 4 */ 5 /* ==================================================================== 6 * Copyright (c) 2006 The OpenSSL Project. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in 17 * the documentation and/or other materials provided with the 18 * distribution. 19 * 20 * 3. All advertising materials mentioning features or use of this 21 * software must display the following acknowledgment: 22 * "This product includes software developed by the OpenSSL Project 23 * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" 24 * 25 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 26 * endorse or promote products derived from this software without 27 * prior written permission. For written permission, please contact 28 * licensing@OpenSSL.org. 29 * 30 * 5. Products derived from this software may not be called "OpenSSL" 31 * nor may "OpenSSL" appear in their names without prior written 32 * permission of the OpenSSL Project. 33 * 34 * 6. Redistributions of any form whatsoever must retain the following 35 * acknowledgment: 36 * "This product includes software developed by the OpenSSL Project 37 * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" 38 * 39 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 40 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 42 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 43 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 44 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 45 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 46 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 48 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 49 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 50 * OF THE POSSIBILITY OF SUCH DAMAGE. 51 * ==================================================================== 52 * 53 * This product includes cryptographic software written by Eric Young 54 * (eay@cryptsoft.com). This product includes software written by Tim 55 * Hudson (tjh@cryptsoft.com). 56 * 57 */ 58 59 #include <stdio.h> 60 #include <string.h> 61 62 #include "apps.h" 63 64 #include <openssl/err.h> 65 #include <openssl/evp.h> 66 #include <openssl/pem.h> 67 68 static int init_keygen_file(BIO * err, EVP_PKEY_CTX **pctx, const char *file); 69 static int genpkey_cb(EVP_PKEY_CTX * ctx); 70 71 struct { 72 const EVP_CIPHER *cipher; 73 EVP_PKEY_CTX **ctx; 74 int do_param; 75 char *outfile; 76 int outformat; 77 char *passarg; 78 int text; 79 } genpkey_config; 80 81 static int 82 genpkey_opt_algorithm(char *arg) 83 { 84 if (!init_gen_str(bio_err, genpkey_config.ctx, arg, 85 genpkey_config.do_param)) 86 return (1); 87 88 return (0); 89 } 90 91 static int 92 genpkey_opt_cipher(int argc, char **argv, int *argsused) 93 { 94 char *name = argv[0]; 95 96 if (*name++ != '-') 97 return (1); 98 99 if (genpkey_config.do_param == 1) 100 return (1); 101 102 if (strcmp(name, "none") == 0) { 103 genpkey_config.cipher = NULL; 104 *argsused = 1; 105 return (0); 106 } 107 108 if ((genpkey_config.cipher = EVP_get_cipherbyname(name)) != NULL) { 109 *argsused = 1; 110 return (0); 111 } 112 113 return (1); 114 } 115 116 static int 117 genpkey_opt_paramfile(char *arg) 118 { 119 if (genpkey_config.do_param == 1) 120 return (1); 121 if (!init_keygen_file(bio_err, genpkey_config.ctx, arg)) 122 return (1); 123 124 return (0); 125 } 126 127 static int 128 genpkey_opt_pkeyopt(char *arg) 129 { 130 if (*genpkey_config.ctx == NULL) { 131 BIO_puts(bio_err, "No keytype specified\n"); 132 return (1); 133 } 134 135 if (pkey_ctrl_string(*genpkey_config.ctx, arg) <= 0) { 136 BIO_puts(bio_err, "parameter setting error\n"); 137 ERR_print_errors(bio_err); 138 return (1); 139 } 140 141 return (0); 142 } 143 144 static const struct option genpkey_options[] = { 145 { 146 .name = "algorithm", 147 .argname = "name", 148 .desc = "Public key algorithm to use (must precede -pkeyopt)", 149 .type = OPTION_ARG_FUNC, 150 .opt.argfunc = genpkey_opt_algorithm, 151 }, 152 { 153 .name = "genparam", 154 .desc = "Generate a set of parameters instead of a private key", 155 .type = OPTION_FLAG, 156 .opt.flag = &genpkey_config.do_param, 157 }, 158 { 159 .name = "out", 160 .argname = "file", 161 .desc = "Output file to write to (default stdout)", 162 .type = OPTION_ARG, 163 .opt.arg = &genpkey_config.outfile, 164 }, 165 { 166 .name = "outform", 167 .argname = "format", 168 .desc = "Output format (DER or PEM)", 169 .type = OPTION_ARG_FORMAT, 170 .opt.value = &genpkey_config.outformat, 171 }, 172 { 173 .name = "paramfile", 174 .argname = "file", 175 .desc = "File to load public key algorithm parameters from\n" 176 "(must precede -pkeyopt)", 177 .type = OPTION_ARG_FUNC, 178 .opt.argfunc = genpkey_opt_paramfile, 179 }, 180 { 181 .name = "pass", 182 .argname = "arg", 183 .desc = "Output file password source", 184 .type = OPTION_ARG, 185 .opt.arg = &genpkey_config.passarg, 186 }, 187 { 188 .name = "pkeyopt", 189 .argname = "opt:value", 190 .desc = "Set public key algorithm option to the given value", 191 .type = OPTION_ARG_FUNC, 192 .opt.argfunc = genpkey_opt_pkeyopt, 193 }, 194 { 195 .name = "text", 196 .desc = "Print the private/public key in human readable form", 197 .type = OPTION_FLAG, 198 .opt.flag = &genpkey_config.text, 199 }, 200 { 201 .name = NULL, 202 .type = OPTION_ARGV_FUNC, 203 .opt.argvfunc = genpkey_opt_cipher, 204 }, 205 {NULL}, 206 }; 207 208 static void 209 genpkey_usage() 210 { 211 fprintf(stderr, 212 "usage: genpkey [-algorithm alg] [cipher] [-genparam] [-out file]\n" 213 " [-outform der | pem] [-paramfile file] [-pass arg]\n" 214 " [-pkeyopt opt:value] [-text]\n\n"); 215 options_usage(genpkey_options); 216 } 217 218 int 219 genpkey_main(int argc, char **argv) 220 { 221 BIO *in = NULL, *out = NULL; 222 EVP_PKEY_CTX *ctx = NULL; 223 EVP_PKEY *pkey = NULL; 224 char *pass = NULL; 225 int ret = 1, rv; 226 227 if (pledge("stdio cpath wpath rpath tty", NULL) == -1) { 228 perror("pledge"); 229 exit(1); 230 } 231 232 memset(&genpkey_config, 0, sizeof(genpkey_config)); 233 genpkey_config.ctx = &ctx; 234 genpkey_config.outformat = FORMAT_PEM; 235 236 if (options_parse(argc, argv, genpkey_options, NULL, NULL) != 0) { 237 genpkey_usage(); 238 goto end; 239 } 240 241 if (ctx == NULL) { 242 genpkey_usage(); 243 goto end; 244 } 245 246 if (!app_passwd(bio_err, genpkey_config.passarg, NULL, &pass, NULL)) { 247 BIO_puts(bio_err, "Error getting password\n"); 248 goto end; 249 } 250 if (genpkey_config.outfile != NULL) { 251 if ((out = BIO_new_file(genpkey_config.outfile, "wb")) == 252 NULL) { 253 BIO_printf(bio_err, "Can't open output file %s\n", 254 genpkey_config.outfile); 255 goto end; 256 } 257 } else { 258 out = BIO_new_fp(stdout, BIO_NOCLOSE); 259 } 260 261 EVP_PKEY_CTX_set_cb(ctx, genpkey_cb); 262 EVP_PKEY_CTX_set_app_data(ctx, bio_err); 263 264 if (genpkey_config.do_param) { 265 if (EVP_PKEY_paramgen(ctx, &pkey) <= 0) { 266 BIO_puts(bio_err, "Error generating parameters\n"); 267 ERR_print_errors(bio_err); 268 goto end; 269 } 270 } else { 271 if (EVP_PKEY_keygen(ctx, &pkey) <= 0) { 272 BIO_puts(bio_err, "Error generating key\n"); 273 ERR_print_errors(bio_err); 274 goto end; 275 } 276 } 277 278 if (genpkey_config.do_param) 279 rv = PEM_write_bio_Parameters(out, pkey); 280 else if (genpkey_config.outformat == FORMAT_PEM) 281 rv = PEM_write_bio_PrivateKey(out, pkey, genpkey_config.cipher, 282 NULL, 0, NULL, pass); 283 else if (genpkey_config.outformat == FORMAT_ASN1) 284 rv = i2d_PrivateKey_bio(out, pkey); 285 else { 286 BIO_printf(bio_err, "Bad format specified for key\n"); 287 goto end; 288 } 289 290 if (rv <= 0) { 291 BIO_puts(bio_err, "Error writing key\n"); 292 ERR_print_errors(bio_err); 293 } 294 if (genpkey_config.text) { 295 if (genpkey_config.do_param) 296 rv = EVP_PKEY_print_params(out, pkey, 0, NULL); 297 else 298 rv = EVP_PKEY_print_private(out, pkey, 0, NULL); 299 300 if (rv <= 0) { 301 BIO_puts(bio_err, "Error printing key\n"); 302 ERR_print_errors(bio_err); 303 } 304 } 305 ret = 0; 306 307 end: 308 EVP_PKEY_free(pkey); 309 EVP_PKEY_CTX_free(ctx); 310 BIO_free_all(out); 311 BIO_free(in); 312 free(pass); 313 314 return ret; 315 } 316 317 static int 318 init_keygen_file(BIO * err, EVP_PKEY_CTX ** pctx, const char *file) 319 { 320 BIO *pbio; 321 EVP_PKEY *pkey = NULL; 322 EVP_PKEY_CTX *ctx = NULL; 323 if (*pctx) { 324 BIO_puts(err, "Parameters already set!\n"); 325 return 0; 326 } 327 pbio = BIO_new_file(file, "r"); 328 if (!pbio) { 329 BIO_printf(err, "Can't open parameter file %s\n", file); 330 return 0; 331 } 332 pkey = PEM_read_bio_Parameters(pbio, NULL); 333 BIO_free(pbio); 334 335 if (!pkey) { 336 BIO_printf(bio_err, "Error reading parameter file %s\n", file); 337 return 0; 338 } 339 ctx = EVP_PKEY_CTX_new(pkey, NULL); 340 if (!ctx) 341 goto err; 342 if (EVP_PKEY_keygen_init(ctx) <= 0) 343 goto err; 344 EVP_PKEY_free(pkey); 345 *pctx = ctx; 346 return 1; 347 348 err: 349 BIO_puts(err, "Error initializing context\n"); 350 ERR_print_errors(err); 351 EVP_PKEY_CTX_free(ctx); 352 EVP_PKEY_free(pkey); 353 return 0; 354 355 } 356 357 int 358 init_gen_str(BIO * err, EVP_PKEY_CTX ** pctx, const char *algname, int do_param) 359 { 360 const EVP_PKEY_ASN1_METHOD *ameth; 361 EVP_PKEY_CTX *ctx = NULL; 362 int pkey_id; 363 364 if (*pctx) { 365 BIO_puts(err, "Algorithm already set!\n"); 366 return 0; 367 } 368 ameth = EVP_PKEY_asn1_find_str(NULL, algname, -1); 369 370 if (!ameth) { 371 BIO_printf(bio_err, "Algorithm %s not found\n", algname); 372 return 0; 373 } 374 ERR_clear_error(); 375 376 EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth); 377 ctx = EVP_PKEY_CTX_new_id(pkey_id, NULL); 378 379 if (!ctx) 380 goto err; 381 if (do_param) { 382 if (EVP_PKEY_paramgen_init(ctx) <= 0) 383 goto err; 384 } else { 385 if (EVP_PKEY_keygen_init(ctx) <= 0) 386 goto err; 387 } 388 389 *pctx = ctx; 390 return 1; 391 392 err: 393 BIO_printf(err, "Error initializing %s context\n", algname); 394 ERR_print_errors(err); 395 EVP_PKEY_CTX_free(ctx); 396 return 0; 397 398 } 399 400 static int 401 genpkey_cb(EVP_PKEY_CTX * ctx) 402 { 403 char c = '*'; 404 BIO *b = EVP_PKEY_CTX_get_app_data(ctx); 405 int p; 406 p = EVP_PKEY_CTX_get_keygen_info(ctx, 0); 407 if (p == 0) 408 c = '.'; 409 if (p == 1) 410 c = '+'; 411 if (p == 2) 412 c = '*'; 413 if (p == 3) 414 c = '\n'; 415 BIO_write(b, &c, 1); 416 (void) BIO_flush(b); 417 return 1; 418 } 419