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