Home | History | Annotate | Line # | Download | only in apps
      1 /*
      2  * Copyright 2006-2025 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 #include <stdio.h>
     11 #include <string.h>
     12 #include "apps.h"
     13 #include "progs.h"
     14 #include <openssl/pem.h>
     15 #include <openssl/err.h>
     16 #include <openssl/evp.h>
     17 
     18 static int verbose = 1;
     19 
     20 static int init_keygen_file(EVP_PKEY_CTX **pctx, const char *file, ENGINE *e,
     21     OSSL_LIB_CTX *libctx, const char *propq);
     22 typedef enum OPTION_choice {
     23     OPT_COMMON,
     24     OPT_ENGINE,
     25     OPT_OUTFORM,
     26     OPT_OUT,
     27     OPT_PASS,
     28     OPT_PARAMFILE,
     29     OPT_ALGORITHM,
     30     OPT_PKEYOPT,
     31     OPT_GENPARAM,
     32     OPT_TEXT,
     33     OPT_CIPHER,
     34     OPT_VERBOSE,
     35     OPT_QUIET,
     36     OPT_CONFIG,
     37     OPT_OUTPUBKEY,
     38     OPT_PROV_ENUM,
     39     OPT_R_ENUM
     40 } OPTION_CHOICE;
     41 
     42 const OPTIONS genpkey_options[] = {
     43     OPT_SECTION("General"),
     44     { "help", OPT_HELP, '-', "Display this summary" },
     45 #ifndef OPENSSL_NO_ENGINE
     46     { "engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device" },
     47 #endif
     48     { "paramfile", OPT_PARAMFILE, '<', "Parameters file" },
     49     { "algorithm", OPT_ALGORITHM, 's', "The public key algorithm" },
     50     { "verbose", OPT_VERBOSE, '-', "Output status while generating keys" },
     51     { "quiet", OPT_QUIET, '-', "Do not output status while generating keys" },
     52     { "pkeyopt", OPT_PKEYOPT, 's',
     53         "Set the public key algorithm option as opt:value" },
     54     OPT_CONFIG_OPTION,
     55 
     56     OPT_SECTION("Output"),
     57     { "out", OPT_OUT, '>', "Output (private key) file" },
     58     { "outpubkey", OPT_OUTPUBKEY, '>', "Output public key file" },
     59     { "outform", OPT_OUTFORM, 'F', "output format (DER or PEM)" },
     60     { "pass", OPT_PASS, 's', "Output file pass phrase source" },
     61     { "genparam", OPT_GENPARAM, '-', "Generate parameters, not key" },
     62     { "text", OPT_TEXT, '-', "Print the private key in text" },
     63     { "", OPT_CIPHER, '-', "Cipher to use to encrypt the key" },
     64 
     65     OPT_PROV_OPTIONS,
     66     OPT_R_OPTIONS,
     67 
     68     /* This is deliberately last. */
     69     { OPT_HELP_STR, 1, 1,
     70         "Order of options may be important!  See the documentation.\n" },
     71     { NULL }
     72 };
     73 
     74 static const char *param_datatype_2name(unsigned int type, int *ishex)
     75 {
     76     *ishex = 0;
     77 
     78     switch (type) {
     79     case OSSL_PARAM_INTEGER:
     80         return "int";
     81     case OSSL_PARAM_UNSIGNED_INTEGER:
     82         return "uint";
     83     case OSSL_PARAM_REAL:
     84         return "float";
     85     case OSSL_PARAM_OCTET_STRING:
     86         *ishex = 1;
     87         return "string";
     88     case OSSL_PARAM_UTF8_STRING:
     89         return "string";
     90     default:
     91         return NULL;
     92     }
     93 }
     94 
     95 static void show_gen_pkeyopt(const char *algname, OSSL_LIB_CTX *libctx, const char *propq)
     96 {
     97     EVP_PKEY_CTX *ctx = NULL;
     98     const OSSL_PARAM *params;
     99     int i, ishex = 0;
    100 
    101     if (algname == NULL)
    102         return;
    103     ctx = EVP_PKEY_CTX_new_from_name(libctx, algname, propq);
    104     if (ctx == NULL)
    105         return;
    106 
    107     if (EVP_PKEY_keygen_init(ctx) <= 0)
    108         goto cleanup;
    109     params = EVP_PKEY_CTX_settable_params(ctx);
    110     if (params == NULL)
    111         goto cleanup;
    112 
    113     BIO_printf(bio_err, "\nThe possible -pkeyopt arguments are:\n");
    114     for (i = 0; params[i].key != NULL; ++i) {
    115         const char *name = param_datatype_2name(params[i].data_type, &ishex);
    116 
    117         if (name != NULL)
    118             BIO_printf(bio_err, "    %s%s:%s\n", ishex ? "hex" : "", params[i].key, name);
    119     }
    120 cleanup:
    121     EVP_PKEY_CTX_free(ctx);
    122 }
    123 
    124 int genpkey_main(int argc, char **argv)
    125 {
    126     CONF *conf = NULL;
    127     BIO *mem_out = NULL, *mem_outpubkey = NULL;
    128     ENGINE *e = NULL;
    129     EVP_PKEY *pkey = NULL;
    130     EVP_PKEY_CTX *ctx = NULL;
    131     char *outfile = NULL, *passarg = NULL, *pass = NULL, *prog, *p;
    132     char *outpubkeyfile = NULL;
    133     const char *ciphername = NULL, *paramfile = NULL, *algname = NULL;
    134     EVP_CIPHER *cipher = NULL;
    135     OPTION_CHOICE o;
    136     int outformat = FORMAT_PEM, text = 0, ret = 1, rv, do_param = 0;
    137     int private = 0, i;
    138     OSSL_LIB_CTX *libctx = app_get0_libctx();
    139     STACK_OF(OPENSSL_STRING) *keyopt = NULL;
    140 
    141     opt_set_unknown_name("cipher");
    142     prog = opt_init(argc, argv, genpkey_options);
    143     keyopt = sk_OPENSSL_STRING_new_null();
    144     if (keyopt == NULL)
    145         goto end;
    146     while ((o = opt_next()) != OPT_EOF) {
    147         switch (o) {
    148         case OPT_EOF:
    149         case OPT_ERR:
    150         opthelp:
    151             BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
    152             goto end;
    153         case OPT_HELP:
    154             ret = 0;
    155             opt_help(genpkey_options);
    156             show_gen_pkeyopt(algname, libctx, app_get0_propq());
    157             goto end;
    158         case OPT_OUTFORM:
    159             if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &outformat))
    160                 goto opthelp;
    161             break;
    162         case OPT_OUT:
    163             outfile = opt_arg();
    164             break;
    165         case OPT_OUTPUBKEY:
    166             outpubkeyfile = opt_arg();
    167             break;
    168         case OPT_PASS:
    169             passarg = opt_arg();
    170             break;
    171         case OPT_ENGINE:
    172             e = setup_engine(opt_arg(), 0);
    173             break;
    174         case OPT_PARAMFILE:
    175             if (do_param == 1)
    176                 goto opthelp;
    177             paramfile = opt_arg();
    178             break;
    179         case OPT_ALGORITHM:
    180             algname = opt_arg();
    181             break;
    182         case OPT_PKEYOPT:
    183             if (!sk_OPENSSL_STRING_push(keyopt, opt_arg()))
    184                 goto end;
    185             break;
    186         case OPT_QUIET:
    187             verbose = 0;
    188             break;
    189         case OPT_VERBOSE:
    190             verbose = 1;
    191             break;
    192         case OPT_GENPARAM:
    193             do_param = 1;
    194             break;
    195         case OPT_TEXT:
    196             text = 1;
    197             break;
    198         case OPT_CIPHER:
    199             ciphername = opt_unknown();
    200             break;
    201         case OPT_CONFIG:
    202             conf = app_load_config_modules(opt_arg());
    203             if (conf == NULL)
    204                 goto end;
    205             break;
    206         case OPT_PROV_CASES:
    207             if (!opt_provider(o))
    208                 goto end;
    209             break;
    210         case OPT_R_CASES:
    211             if (!opt_rand(o))
    212                 goto end;
    213             break;
    214         }
    215     }
    216 
    217     /* No extra arguments. */
    218     if (!opt_check_rest_arg(NULL))
    219         goto opthelp;
    220 
    221     if (!app_RAND_load())
    222         goto end;
    223 
    224     /* Fetch cipher, etc. */
    225     if (paramfile != NULL) {
    226         if (!init_keygen_file(&ctx, paramfile, e, libctx, app_get0_propq()))
    227             goto end;
    228     }
    229     if (algname != NULL) {
    230         if (!init_gen_str(&ctx, algname, e, do_param, libctx, app_get0_propq()))
    231             goto end;
    232     }
    233     if (ctx == NULL)
    234         goto opthelp;
    235 
    236     for (i = 0; i < sk_OPENSSL_STRING_num(keyopt); i++) {
    237         p = sk_OPENSSL_STRING_value(keyopt, i);
    238         if (pkey_ctrl_string(ctx, p) <= 0) {
    239             BIO_printf(bio_err, "%s: Error setting %s parameter:\n", prog, p);
    240             ERR_print_errors(bio_err);
    241             goto end;
    242         }
    243     }
    244     if (!opt_cipher(ciphername, &cipher))
    245         goto opthelp;
    246     if (ciphername != NULL && do_param == 1) {
    247         BIO_printf(bio_err, "Cannot use cipher with -genparam option\n");
    248         goto opthelp;
    249     }
    250 
    251     private = do_param ? 0 : 1;
    252 
    253     if (!app_passwd(passarg, NULL, &pass, NULL)) {
    254         BIO_puts(bio_err, "Error getting password\n");
    255         goto end;
    256     }
    257 
    258     mem_out = BIO_new(BIO_s_mem());
    259     if (mem_out == NULL)
    260         goto end;
    261     BIO_set_mem_eof_return(mem_out, 0);
    262 
    263     if (outpubkeyfile != NULL) {
    264         mem_outpubkey = BIO_new(BIO_s_mem());
    265         if (mem_outpubkey == NULL)
    266             goto end;
    267         BIO_set_mem_eof_return(mem_outpubkey, 0);
    268     }
    269 
    270     if (verbose)
    271         EVP_PKEY_CTX_set_cb(ctx, progress_cb);
    272     EVP_PKEY_CTX_set_app_data(ctx, bio_err);
    273 
    274     pkey = do_param ? app_paramgen(ctx, algname)
    275                     : app_keygen(ctx, algname, 0, 0 /* not verbose */);
    276     if (pkey == NULL)
    277         goto end;
    278 
    279     if (do_param) {
    280         rv = PEM_write_bio_Parameters(mem_out, pkey);
    281     } else if (outformat == FORMAT_PEM) {
    282         assert(private);
    283         rv = PEM_write_bio_PrivateKey(mem_out, pkey, cipher, NULL, 0, NULL, pass);
    284         if (rv > 0 && mem_outpubkey != NULL)
    285             rv = PEM_write_bio_PUBKEY(mem_outpubkey, pkey);
    286     } else if (outformat == FORMAT_ASN1) {
    287         assert(private);
    288         rv = i2d_PrivateKey_bio(mem_out, pkey);
    289         if (rv > 0 && mem_outpubkey != NULL)
    290             rv = i2d_PUBKEY_bio(mem_outpubkey, pkey);
    291     } else {
    292         BIO_printf(bio_err, "Bad format specified for key\n");
    293         goto end;
    294     }
    295 
    296     ret = 0;
    297 
    298     if (rv <= 0) {
    299         BIO_puts(bio_err, "Error writing key(s)\n");
    300         ret = 1;
    301     }
    302 
    303     if (text) {
    304         if (do_param)
    305             rv = EVP_PKEY_print_params(mem_out, pkey, 0, NULL);
    306         else
    307             rv = EVP_PKEY_print_private(mem_out, pkey, 0, NULL);
    308 
    309         if (rv <= 0) {
    310             BIO_puts(bio_err, "Error printing key\n");
    311             ret = 1;
    312         }
    313     }
    314 
    315 end:
    316     sk_OPENSSL_STRING_free(keyopt);
    317     if (ret != 0) {
    318         ERR_print_errors(bio_err);
    319     } else {
    320         if (mem_outpubkey != NULL) {
    321             rv = mem_bio_to_file(mem_outpubkey, outpubkeyfile, outformat, private);
    322             if (!rv)
    323                 BIO_printf(bio_err, "Error writing to outpubkey: '%s'. Error: %s\n",
    324                     outpubkeyfile, strerror(errno));
    325         }
    326         if (mem_out != NULL) {
    327             rv = mem_bio_to_file(mem_out, outfile, outformat, private);
    328             if (!rv)
    329                 BIO_printf(bio_err, "Error writing to outfile: '%s'. Error: %s\n",
    330                     outfile, strerror(errno));
    331         }
    332     }
    333     EVP_PKEY_free(pkey);
    334     EVP_PKEY_CTX_free(ctx);
    335     EVP_CIPHER_free(cipher);
    336     BIO_free_all(mem_out);
    337     BIO_free_all(mem_outpubkey);
    338     release_engine(e);
    339     OPENSSL_free(pass);
    340     NCONF_free(conf);
    341     return ret;
    342 }
    343 
    344 static int init_keygen_file(EVP_PKEY_CTX **pctx, const char *file, ENGINE *e,
    345     OSSL_LIB_CTX *libctx, const char *propq)
    346 {
    347     BIO *pbio;
    348     EVP_PKEY *pkey = NULL;
    349     EVP_PKEY_CTX *ctx = NULL;
    350     if (*pctx) {
    351         BIO_puts(bio_err, "Parameters already set!\n");
    352         return 0;
    353     }
    354 
    355     pbio = BIO_new_file(file, "r");
    356     if (pbio == NULL) {
    357         BIO_printf(bio_err, "Can't open parameter file %s\n", file);
    358         return 0;
    359     }
    360 
    361     pkey = PEM_read_bio_Parameters_ex(pbio, NULL, libctx, propq);
    362     BIO_free(pbio);
    363 
    364     if (pkey == NULL) {
    365         BIO_printf(bio_err, "Error reading parameter file %s\n", file);
    366         return 0;
    367     }
    368 
    369     if (e != NULL)
    370         ctx = EVP_PKEY_CTX_new(pkey, e);
    371     else
    372         ctx = EVP_PKEY_CTX_new_from_pkey(libctx, pkey, propq);
    373     if (ctx == NULL)
    374         goto err;
    375     if (EVP_PKEY_keygen_init(ctx) <= 0)
    376         goto err;
    377     EVP_PKEY_free(pkey);
    378     *pctx = ctx;
    379     return 1;
    380 
    381 err:
    382     BIO_puts(bio_err, "Error initializing context\n");
    383     ERR_print_errors(bio_err);
    384     EVP_PKEY_CTX_free(ctx);
    385     EVP_PKEY_free(pkey);
    386     return 0;
    387 }
    388 
    389 int init_gen_str(EVP_PKEY_CTX **pctx,
    390     const char *algname, ENGINE *e, int do_param,
    391     OSSL_LIB_CTX *libctx, const char *propq)
    392 {
    393     EVP_PKEY_CTX *ctx = NULL;
    394     int pkey_id;
    395 
    396     if (*pctx) {
    397         BIO_puts(bio_err, "Algorithm already set!\n");
    398         return 0;
    399     }
    400 
    401     pkey_id = get_legacy_pkey_id(libctx, algname, e);
    402     if (pkey_id != NID_undef)
    403         ctx = EVP_PKEY_CTX_new_id(pkey_id, e);
    404     else
    405         ctx = EVP_PKEY_CTX_new_from_name(libctx, algname, propq);
    406 
    407     if (ctx == NULL)
    408         goto err;
    409     if (do_param) {
    410         if (EVP_PKEY_paramgen_init(ctx) <= 0)
    411             goto err;
    412     } else {
    413         if (EVP_PKEY_keygen_init(ctx) <= 0)
    414             goto err;
    415     }
    416 
    417     *pctx = ctx;
    418     return 1;
    419 
    420 err:
    421     BIO_printf(bio_err, "Error initializing %s context\n", algname);
    422     ERR_print_errors(bio_err);
    423     EVP_PKEY_CTX_free(ctx);
    424     return 0;
    425 }
    426