X-Git-Url: https://git.openssl.org/?p=openssl.git;a=blobdiff_plain;f=apps%2Freq.c;h=45de46d393598c0551f9aab472b9492bdc4927f8;hp=79425e59393809e9de64178342f00ea5c284f1d5;hb=e1cd94f2dca4056ce042c62b89c468dffc088033;hpb=89a99cd589d69d3861afa8865986e2d98afb979a diff --git a/apps/req.c b/apps/req.c index 79425e5939..45de46d393 100644 --- a/apps/req.c +++ b/apps/req.c @@ -1,7 +1,7 @@ /* - * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html @@ -11,7 +11,10 @@ #include #include #include +#include #include "apps.h" +#include "progs.h" +#include #include #include #include @@ -22,32 +25,30 @@ #include #include #include -#ifndef OPENSSL_NO_RSA -# include -#endif +#include +#include #ifndef OPENSSL_NO_DSA # include #endif -#define SECTION "req" - -#define BITS "default_bits" -#define KEYFILE "default_keyfile" -#define PROMPT "prompt" -#define DISTINGUISHED_NAME "distinguished_name" -#define ATTRIBUTES "attributes" -#define V3_EXTENSIONS "x509_extensions" -#define REQ_EXTENSIONS "req_extensions" -#define STRING_MASK "string_mask" -#define UTF8_IN "utf8" - -#define DEFAULT_KEY_LENGTH 2048 -#define MIN_KEY_LENGTH 512 - -static int make_REQ(X509_REQ *req, EVP_PKEY *pkey, char *dn, int mutlirdn, - int attribs, unsigned long chtype); -static int build_subject(X509_REQ *req, const char *subj, unsigned long chtype, - int multirdn); +#define BITS "default_bits" +#define KEYFILE "default_keyfile" +#define PROMPT "prompt" +#define DISTINGUISHED_NAME "distinguished_name" +#define ATTRIBUTES "attributes" +#define V3_EXTENSIONS "x509_extensions" +#define REQ_EXTENSIONS "req_extensions" +#define STRING_MASK "string_mask" +#define UTF8_IN "utf8" + +#define DEFAULT_KEY_LENGTH 2048 +#define MIN_KEY_LENGTH 512 +#define DEFAULT_DAYS 30 /* default cert validity period in days */ +#define UNSET_DAYS -2 /* -1 may be used for testing expiration checks */ +#define EXT_COPY_UNSET -1 + +static int make_REQ(X509_REQ *req, EVP_PKEY *pkey, X509_NAME *fsubj, + int mutlirdn, int attribs, unsigned long chtype); static int prompt_info(X509_REQ *req, STACK_OF(CONF_VALUE) *dn_sk, const char *dn_sect, STACK_OF(CONF_VALUE) *attr_sk, const char *attr_sect, @@ -61,116 +62,210 @@ static int add_attribute_object(X509_REQ *req, char *text, const char *def, static int add_DN_object(X509_NAME *n, char *text, const char *def, char *value, int nid, int n_min, int n_max, unsigned long chtype, int mval); -static int genpkey_cb(EVP_PKEY_CTX *ctx); +static int build_data(char *text, const char *def, char *value, + int n_min, int n_max, char *buf, const int buf_size, + const char *desc1, const char *desc2); static int req_check_len(int len, int n_min, int n_max); static int check_end(const char *str, const char *end); static int join(char buf[], size_t buf_size, const char *name, const char *tail, const char *desc); static EVP_PKEY_CTX *set_keygen_ctx(const char *gstr, - int *pkey_type, long *pkeylen, - char **palgnam, ENGINE *keygen_engine); + char **pkeytype, long *pkeylen, + ENGINE *keygen_engine); + +static const char *section = "req"; static CONF *req_conf = NULL; +static CONF *addext_conf = NULL; static int batch = 0; typedef enum OPTION_choice { - OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_COMMON, OPT_INFORM, OPT_OUTFORM, OPT_ENGINE, OPT_KEYGEN_ENGINE, OPT_KEY, OPT_PUBKEY, OPT_NEW, OPT_CONFIG, OPT_KEYFORM, OPT_IN, OPT_OUT, OPT_KEYOUT, OPT_PASSIN, OPT_PASSOUT, OPT_NEWKEY, - OPT_PKEYOPT, OPT_SIGOPT, OPT_BATCH, OPT_NEWHDR, OPT_MODULUS, - OPT_VERIFY, OPT_NODES, OPT_NOOUT, OPT_VERBOSE, OPT_UTF8, + OPT_PKEYOPT, OPT_SIGOPT, OPT_VFYOPT, OPT_BATCH, OPT_NEWHDR, OPT_MODULUS, + OPT_VERIFY, OPT_NOENC, OPT_NODES, OPT_NOOUT, OPT_VERBOSE, OPT_UTF8, OPT_NAMEOPT, OPT_REQOPT, OPT_SUBJ, OPT_SUBJECT, OPT_TEXT, OPT_X509, - OPT_MULTIVALUE_RDN, OPT_DAYS, OPT_SET_SERIAL, OPT_EXTENSIONS, - OPT_REQEXTS, OPT_PRECERT, OPT_MD, - OPT_R_ENUM + OPT_CA, OPT_CAKEY, + OPT_MULTIVALUE_RDN, OPT_DAYS, OPT_SET_SERIAL, + OPT_COPY_EXTENSIONS, OPT_EXTENSIONS, OPT_REQEXTS, OPT_ADDEXT, + OPT_PRECERT, OPT_MD, + OPT_SECTION, + OPT_R_ENUM, OPT_PROV_ENUM } OPTION_CHOICE; const OPTIONS req_options[] = { + OPT_SECTION("General"), {"help", OPT_HELP, '-', "Display this summary"}, +#ifndef OPENSSL_NO_ENGINE + {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, + {"keygen_engine", OPT_KEYGEN_ENGINE, 's', + "Specify engine to be used for key generation operations"}, +#endif + {"in", OPT_IN, '<', "X.509 request input file (default stdin)"}, {"inform", OPT_INFORM, 'F', "Input format - DER or PEM"}, - {"outform", OPT_OUTFORM, 'F', "Output format - DER or PEM"}, - {"in", OPT_IN, '<', "Input file"}, - {"out", OPT_OUT, '>', "Output file"}, - {"key", OPT_KEY, 's', "Private key to use"}, - {"keyform", OPT_KEYFORM, 'f', "Key file format"}, - {"pubkey", OPT_PUBKEY, '-', "Output public key"}, + {"verify", OPT_VERIFY, '-', "Verify self-signature on the request"}, + + OPT_SECTION("Certificate"), {"new", OPT_NEW, '-', "New request"}, {"config", OPT_CONFIG, '<', "Request template file"}, - {"keyout", OPT_KEYOUT, '>', "File to send the key to"}, - {"passin", OPT_PASSIN, 's', "Private key password source"}, - {"passout", OPT_PASSOUT, 's', "Output file pass phrase source"}, - OPT_R_OPTIONS, - {"newkey", OPT_NEWKEY, 's', "Specify as type:bits"}, - {"pkeyopt", OPT_PKEYOPT, 's', "Public key options as opt:value"}, - {"sigopt", OPT_SIGOPT, 's', "Signature parameter in n:v form"}, - {"batch", OPT_BATCH, '-', - "Do not ask anything during request generation"}, - {"newhdr", OPT_NEWHDR, '-', "Output \"NEW\" in the header lines"}, - {"modulus", OPT_MODULUS, '-', "RSA modulus"}, - {"verify", OPT_VERIFY, '-', "Verify signature on REQ"}, - {"nodes", OPT_NODES, '-', "Don't encrypt the output key"}, - {"noout", OPT_NOOUT, '-', "Do not output REQ"}, - {"verbose", OPT_VERBOSE, '-', "Verbose output"}, + {"section", OPT_SECTION, 's', "Config section to use (default \"req\")"}, {"utf8", OPT_UTF8, '-', "Input characters are UTF8 (default ASCII)"}, - {"nameopt", OPT_NAMEOPT, 's', "Various certificate name options"}, + {"nameopt", OPT_NAMEOPT, 's', "Certificate subject/issuer name printing options"}, {"reqopt", OPT_REQOPT, 's', "Various request text options"}, {"text", OPT_TEXT, '-', "Text form of request"}, {"x509", OPT_X509, '-', - "Output a x509 structure instead of a cert request"}, + "Output an X.509 certificate structure instead of a cert request"}, + {"CA", OPT_CA, '<', "Issuer cert to use for signing a cert, implies -x509"}, + {"CAkey", OPT_CAKEY, 's', + "Issuer private key to use with -CA; default is -CA arg"}, {OPT_MORE_STR, 1, 1, "(Required by some CA's)"}, - {"subj", OPT_SUBJ, 's', "Set or modify request subject"}, - {"subject", OPT_SUBJECT, '-', "Output the request's subject"}, + {"subj", OPT_SUBJ, 's', "Set or modify subject of request or cert"}, + {"subject", OPT_SUBJECT, '-', + "Print the subject of the output request or cert"}, {"multivalue-rdn", OPT_MULTIVALUE_RDN, '-', - "Enable support for multivalued RDNs"}, + "Deprecated; multi-valued RDNs support is always on."}, {"days", OPT_DAYS, 'p', "Number of days cert is valid for"}, {"set_serial", OPT_SET_SERIAL, 's', "Serial number to use"}, + {"copy_extensions", OPT_COPY_EXTENSIONS, 's', + "copy extensions from request when using -x509"}, {"extensions", OPT_EXTENSIONS, 's', - "Cert extension section (override value in config file)"}, - {"reqexts", OPT_REQEXTS, 's', - "Request extension section (override value in config file)"}, - {"precert", OPT_PRECERT, '-', "Add a poison extension (implies -new)"}, + "Cert or request extension section (override value in config file)"}, + {"reqexts", OPT_REQEXTS, 's', "An alias for -extensions"}, + {"addext", OPT_ADDEXT, 's', + "Additional cert extension key=value pair (may be given more than once)"}, + {"precert", OPT_PRECERT, '-', "Add a poison extension to generated cert (implies -new)"}, + + OPT_SECTION("Keys and Signing"), + {"key", OPT_KEY, 's', "Key for signing, and to include unless -in given"}, + {"keyform", OPT_KEYFORM, 'f', "Key file format (ENGINE, other values ignored)"}, + {"pubkey", OPT_PUBKEY, '-', "Output public key"}, + {"keyout", OPT_KEYOUT, '>', "File to write private key to"}, + {"passin", OPT_PASSIN, 's', "Private key and certificate password source"}, + {"passout", OPT_PASSOUT, 's', "Output file pass phrase source"}, + {"newkey", OPT_NEWKEY, 's', + "Generate new key with [:] or [:] or param:"}, + {"pkeyopt", OPT_PKEYOPT, 's', "Public key options as opt:value"}, + {"sigopt", OPT_SIGOPT, 's', "Signature parameter in n:v form"}, + {"vfyopt", OPT_VFYOPT, 's', "Verification parameter in n:v form"}, {"", OPT_MD, '-', "Any supported digest"}, -#ifndef OPENSSL_NO_ENGINE - {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, - {"keygen_engine", OPT_KEYGEN_ENGINE, 's', - "Specify engine to be used for key generation operations"}, -#endif + + OPT_SECTION("Output"), + {"out", OPT_OUT, '>', "Output file"}, + {"outform", OPT_OUTFORM, 'F', "Output format - DER or PEM"}, + {"batch", OPT_BATCH, '-', + "Do not ask anything during request generation"}, + {"verbose", OPT_VERBOSE, '-', "Verbose output"}, + {"noenc", OPT_NOENC, '-', "Don't encrypt private keys"}, + {"nodes", OPT_NODES, '-', "Don't encrypt private keys; deprecated"}, + {"noout", OPT_NOOUT, '-', "Do not output REQ"}, + {"newhdr", OPT_NEWHDR, '-', "Output \"NEW\" in the header lines"}, + {"modulus", OPT_MODULUS, '-', "RSA modulus"}, + + OPT_R_OPTIONS, + OPT_PROV_OPTIONS, {NULL} }; +/* + * An LHASH of strings, where each string is an extension name. + */ +static unsigned long ext_name_hash(const OPENSSL_STRING *a) +{ + return OPENSSL_LH_strhash((const char *)a); +} + +static int ext_name_cmp(const OPENSSL_STRING *a, const OPENSSL_STRING *b) +{ + return strcmp((const char *)a, (const char *)b); +} + +static void exts_cleanup(OPENSSL_STRING *x) +{ + OPENSSL_free((char *)x); +} + +/* + * Is the |kv| key already duplicated? This is remarkably tricky to get right. + * Return 0 if unique, -1 on runtime error; 1 if found or a syntax error. + */ +static int duplicated(LHASH_OF(OPENSSL_STRING) *addexts, char *kv) +{ + char *p; + size_t off; + + /* Check syntax. */ + /* Skip leading whitespace, make a copy. */ + while (*kv && isspace(*kv)) + if (*++kv == '\0') + return 1; + if ((p = strchr(kv, '=')) == NULL) + return 1; + off = p - kv; + if ((kv = OPENSSL_strdup(kv)) == NULL) + return -1; + + /* Skip trailing space before the equal sign. */ + for (p = kv + off; p > kv; --p) + if (!isspace(p[-1])) + break; + if (p == kv) { + OPENSSL_free(kv); + return 1; + } + *p = '\0'; + + /* Finally have a clean "key"; see if it's there [by attempt to add it]. */ + p = (char *)lh_OPENSSL_STRING_insert(addexts, (OPENSSL_STRING *)kv); + if (p != NULL) { + OPENSSL_free(p); + return 1; + } else if (lh_OPENSSL_STRING_error(addexts)) { + OPENSSL_free(kv); + return -1; + } + + return 0; +} + int req_main(int argc, char **argv) { ASN1_INTEGER *serial = NULL; - BIO *in = NULL, *out = NULL; + BIO *out = NULL; ENGINE *e = NULL, *gen_eng = NULL; - EVP_PKEY *pkey = NULL; + EVP_PKEY *pkey = NULL, *CAkey = NULL; EVP_PKEY_CTX *genctx = NULL; - STACK_OF(OPENSSL_STRING) *pkeyopts = NULL, *sigopts = NULL; - X509 *x509ss = NULL; + STACK_OF(OPENSSL_STRING) *pkeyopts = NULL, *sigopts = NULL, *vfyopts = NULL; + LHASH_OF(OPENSSL_STRING) *addexts = NULL; + X509 *new_x509 = NULL, *CAcert = NULL; X509_REQ *req = NULL; - const EVP_CIPHER *cipher = NULL; - const EVP_MD *md_alg = NULL, *digest = NULL; - char *extensions = NULL, *infile = NULL; - char *outfile = NULL, *keyfile = NULL; + EVP_CIPHER *cipher = NULL; + int ext_copy = EXT_COPY_UNSET; + BIO *addext_bio = NULL; + char *extsect = NULL; + const char *infile = NULL, *CAfile = NULL, *CAkeyfile = NULL; + char *outfile = NULL, *keyfile = NULL, *digest = NULL; char *keyalgstr = NULL, *p, *prog, *passargin = NULL, *passargout = NULL; char *passin = NULL, *passout = NULL; char *nofree_passin = NULL, *nofree_passout = NULL; - char *req_exts = NULL, *subj = NULL; + char *subj = NULL; + X509_NAME *fsubj = NULL; char *template = default_config_file, *keyout = NULL; const char *keyalg = NULL; OPTION_CHOICE o; - int ret = 1, x509 = 0, days = 30, i = 0, newreq = 0, verbose = 0; - int pkey_type = -1, private = 0; - int informat = FORMAT_PEM, outformat = FORMAT_PEM, keyform = FORMAT_PEM; - int modulus = 0, multirdn = 0, verify = 0, noout = 0, text = 0; - int nodes = 0, newhdr = 0, subject = 0, pubkey = 0, precert = 0; - long newkey = -1; + int days = UNSET_DAYS; + int ret = 1, gen_x509 = 0, i = 0, newreq = 0, verbose = 0; + int informat = FORMAT_UNDEF, outformat = FORMAT_PEM, keyform = FORMAT_UNDEF; + int modulus = 0, multirdn = 1, verify = 0, noout = 0, text = 0; + int noenc = 0, newhdr = 0, subject = 0, pubkey = 0, precert = 0; + long newkey_len = -1; unsigned long chtype = MBSTRING_ASC, reqflag = 0; #ifndef OPENSSL_NO_DES - cipher = EVP_des_ede3_cbc(); + cipher = (EVP_CIPHER *)EVP_des_ede3_cbc(); #endif + opt_set_unknown_name("digest"); prog = opt_init(argc, argv, req_options); while ((o = opt_next()) != OPT_EOF) { switch (o) { @@ -196,7 +291,7 @@ int req_main(int argc, char **argv) break; case OPT_KEYGEN_ENGINE: #ifndef OPENSSL_NO_ENGINE - gen_eng = ENGINE_by_id(opt_arg()); + gen_eng = setup_engine(opt_arg(), 0); if (gen_eng == NULL) { BIO_printf(bio_err, "Can't find keygen engine %s\n", *argv); goto opthelp; @@ -215,6 +310,9 @@ int req_main(int argc, char **argv) case OPT_CONFIG: template = opt_arg(); break; + case OPT_SECTION: + section = opt_arg(); + break; case OPT_KEYFORM: if (!opt_format(opt_arg(), OPT_FMT_ANY, &keyform)) goto opthelp; @@ -238,14 +336,19 @@ int req_main(int argc, char **argv) if (!opt_rand(o)) goto end; break; + case OPT_PROV_CASES: + if (!opt_provider(o)) + goto end; + break; case OPT_NEWKEY: keyalg = opt_arg(); newreq = 1; break; case OPT_PKEYOPT: - if (!pkeyopts) + if (pkeyopts == NULL) pkeyopts = sk_OPENSSL_STRING_new_null(); - if (!pkeyopts || !sk_OPENSSL_STRING_push(pkeyopts, opt_arg())) + if (pkeyopts == NULL + || !sk_OPENSSL_STRING_push(pkeyopts, opt_arg())) goto opthelp; break; case OPT_SIGOPT: @@ -254,6 +357,12 @@ int req_main(int argc, char **argv) if (!sigopts || !sk_OPENSSL_STRING_push(sigopts, opt_arg())) goto opthelp; break; + case OPT_VFYOPT: + if (!vfyopts) + vfyopts = sk_OPENSSL_STRING_new_null(); + if (!vfyopts || !sk_OPENSSL_STRING_push(vfyopts, opt_arg())) + goto opthelp; + break; case OPT_BATCH: batch = 1; break; @@ -267,7 +376,8 @@ int req_main(int argc, char **argv) verify = 1; break; case OPT_NODES: - nodes = 1; + case OPT_NOENC: + noenc = 1; break; case OPT_NOOUT: noout = 1; @@ -290,10 +400,22 @@ int req_main(int argc, char **argv) text = 1; break; case OPT_X509: - x509 = 1; + gen_x509 = 1; + break; + case OPT_CA: + CAfile = opt_arg(); + gen_x509 = 1; + break; + case OPT_CAKEY: + CAkeyfile = opt_arg(); break; case OPT_DAYS: days = atoi(opt_arg()); + if (days < -1) { + BIO_printf(bio_err, "%s: -days parameter arg must be >= -1\n", + prog); + goto end; + } break; case OPT_SET_SERIAL: if (serial != NULL) { @@ -311,44 +433,79 @@ int req_main(int argc, char **argv) subj = opt_arg(); break; case OPT_MULTIVALUE_RDN: - multirdn = 1; + /* obsolete */ break; - case OPT_EXTENSIONS: - extensions = opt_arg(); + case OPT_COPY_EXTENSIONS: + if (!set_ext_copy(&ext_copy, opt_arg())) { + BIO_printf(bio_err, "Invalid extension copy option: \"%s\"\n", + opt_arg()); + goto end; + } break; + case OPT_EXTENSIONS: case OPT_REQEXTS: - req_exts = opt_arg(); + extsect = opt_arg(); + break; + case OPT_ADDEXT: + p = opt_arg(); + if (addexts == NULL) { + addexts = lh_OPENSSL_STRING_new(ext_name_hash, ext_name_cmp); + addext_bio = BIO_new(BIO_s_mem()); + if (addexts == NULL || addext_bio == NULL) + goto end; + } + i = duplicated(addexts, p); + if (i == 1) { + BIO_printf(bio_err, "Duplicate extension name: %s\n", p); + goto opthelp; + } + if (i < 0 || BIO_printf(addext_bio, "%s\n", p) < 0) + goto end; break; case OPT_PRECERT: newreq = precert = 1; break; case OPT_MD: - if (!opt_md(opt_unknown(), &md_alg)) - goto opthelp; - digest = md_alg; + digest = opt_unknown(); break; } } - argc = opt_num_rest(); - if (argc != 0) + + /* No extra arguments. */ + if (!opt_check_rest_arg(NULL)) goto opthelp; - if (days && !x509) - BIO_printf(bio_err, "Ignoring -days; not generating a certificate"); - if (x509 && infile == NULL) - newreq = 1; + if (!app_RAND_load()) + goto end; - /* TODO: simplify this as pkey is still always NULL here */ - private = newreq && (pkey == NULL) ? 1 : 0; + if (!gen_x509) { + if (days != UNSET_DAYS) + BIO_printf(bio_err, "Ignoring -days without -x509; not generating a certificate\n"); + if (ext_copy == EXT_COPY_NONE) + BIO_printf(bio_err, "Ignoring -copy_extensions 'none' when -x509 is not given\n"); + } + if (infile == NULL) { + if (gen_x509) + newreq = 1; + else if (!newreq) + BIO_printf(bio_err, + "Warning: Will read cert request from stdin since no -in option is given\n"); + } if (!app_passwd(passargin, passargout, &passin, &passout)) { BIO_printf(bio_err, "Error getting passwords\n"); goto end; } - if (verbose) - BIO_printf(bio_err, "Using configuration from %s\n", template); - req_conf = app_load_config(template); + if ((req_conf = app_load_config_verbose(template, verbose)) == NULL) + goto end; + if (addext_bio != NULL) { + if (verbose) + BIO_printf(bio_err, + "Using additional configuration from -addext options\n"); + if ((addext_conf = app_load_config_bio(addext_bio, NULL)) == NULL) + goto end; + } if (template != default_config_file && !app_load_modules(req_conf)) goto end; @@ -357,14 +514,12 @@ int req_main(int argc, char **argv) if (p == NULL) ERR_clear_error(); if (p != NULL) { - BIO *oid_bio; + BIO *oid_bio = BIO_new_file(p, "r"); - oid_bio = BIO_new_file(p, "r"); if (oid_bio == NULL) { - /*- - BIO_printf(bio_err,"problems opening %s for extra oid's\n",p); - ERR_print_errors(bio_err); - */ + if (verbose) + BIO_printf(bio_err, + "Problems opening '%s' for extra OIDs\n", p); } else { OBJ_create_objects(oid_bio); BIO_free(oid_bio); @@ -374,49 +529,65 @@ int req_main(int argc, char **argv) if (!add_oid_section(req_conf)) goto end; - if (md_alg == NULL) { - p = NCONF_get_string(req_conf, SECTION, "default_md"); - if (p == NULL) { + /* Check that any specified digest is fetchable */ + if (digest != NULL) { + if (!opt_check_md(digest)) + goto opthelp; + } else { + /* No digest specified, default to configuration */ + p = NCONF_get_string(req_conf, section, "default_md"); + if (p == NULL) ERR_clear_error(); - } else { - if (!opt_md(p, &md_alg)) - goto opthelp; - digest = md_alg; - } + else + digest = p; } - if (extensions == NULL) { - extensions = NCONF_get_string(req_conf, SECTION, V3_EXTENSIONS); - if (extensions == NULL) + if (extsect == NULL) { + extsect = NCONF_get_string(req_conf, section, + gen_x509 ? V3_EXTENSIONS : REQ_EXTENSIONS); + if (extsect == NULL) ERR_clear_error(); } - if (extensions != NULL) { - /* Check syntax of file */ + if (extsect != NULL) { + /* Check syntax of extension section in config file */ X509V3_CTX ctx; + X509V3_set_ctx_test(&ctx); X509V3_set_nconf(&ctx, req_conf); - if (!X509V3_EXT_add_nconf(req_conf, &ctx, extensions, NULL)) { + if (!X509V3_EXT_add_nconf(req_conf, &ctx, extsect, NULL)) { BIO_printf(bio_err, - "Error Loading extension section %s\n", extensions); + "Error checking %s extension section %s\n", + gen_x509 ? "x509" : "request", extsect); + goto end; + } + } + if (addext_conf != NULL) { + /* Check syntax of command line extensions */ + X509V3_CTX ctx; + + X509V3_set_ctx_test(&ctx); + X509V3_set_nconf(&ctx, addext_conf); + if (!X509V3_EXT_add_nconf(addext_conf, &ctx, "default", NULL)) { + BIO_printf(bio_err, "Error checking extensions defined using -addext\n"); goto end; } } if (passin == NULL) { passin = nofree_passin = - NCONF_get_string(req_conf, SECTION, "input_password"); + NCONF_get_string(req_conf, section, "input_password"); if (passin == NULL) ERR_clear_error(); } if (passout == NULL) { passout = nofree_passout = - NCONF_get_string(req_conf, SECTION, "output_password"); + NCONF_get_string(req_conf, section, "output_password"); if (passout == NULL) ERR_clear_error(); } - p = NCONF_get_string(req_conf, SECTION, STRING_MASK); + p = NCONF_get_string(req_conf, section, STRING_MASK); if (p == NULL) ERR_clear_error(); @@ -426,129 +597,111 @@ int req_main(int argc, char **argv) } if (chtype != MBSTRING_UTF8) { - p = NCONF_get_string(req_conf, SECTION, UTF8_IN); + p = NCONF_get_string(req_conf, section, UTF8_IN); if (p == NULL) ERR_clear_error(); else if (strcmp(p, "yes") == 0) chtype = MBSTRING_UTF8; } - if (req_exts == NULL) { - req_exts = NCONF_get_string(req_conf, SECTION, REQ_EXTENSIONS); - if (req_exts == NULL) - ERR_clear_error(); - } - if (req_exts != NULL) { - /* Check syntax of file */ - X509V3_CTX ctx; - X509V3_set_ctx_test(&ctx); - X509V3_set_nconf(&ctx, req_conf); - if (!X509V3_EXT_add_nconf(req_conf, &ctx, req_exts, NULL)) { - BIO_printf(bio_err, - "Error Loading request extension section %s\n", - req_exts); - goto end; - } - } - if (keyfile != NULL) { - pkey = load_key(keyfile, keyform, 0, passin, e, "Private Key"); - if (pkey == NULL) { - /* load_key() has already printed an appropriate message */ + pkey = load_key(keyfile, keyform, 0, passin, e, "private key"); + if (pkey == NULL) goto end; - } else { - app_RAND_load_conf(req_conf, SECTION); - } + app_RAND_load_conf(req_conf, section); } + if (keyalg != NULL && pkey != NULL) { + BIO_printf(bio_err, + "Warning: Not generating key via given -newkey option since -key is given\n"); + /* Better throw an error in this case */ + } + if (newreq && pkey == NULL) { + app_RAND_load_conf(req_conf, section); - if (newreq && (pkey == NULL)) { - app_RAND_load_conf(req_conf, SECTION); - - if (!NCONF_get_number(req_conf, SECTION, BITS, &newkey)) { - newkey = DEFAULT_KEY_LENGTH; - } + if (!NCONF_get_number(req_conf, section, BITS, &newkey_len)) + newkey_len = DEFAULT_KEY_LENGTH; - if (keyalg != NULL) { - genctx = set_keygen_ctx(keyalg, &pkey_type, &newkey, - &keyalgstr, gen_eng); - if (genctx == NULL) - goto end; - } + genctx = set_keygen_ctx(keyalg, &keyalgstr, &newkey_len, gen_eng); + if (genctx == NULL) + goto end; - if (newkey < MIN_KEY_LENGTH - && (pkey_type == EVP_PKEY_RSA || pkey_type == EVP_PKEY_DSA)) { - BIO_printf(bio_err, "private key length is too short,\n"); - BIO_printf(bio_err, "it needs to be at least %d bits, not %ld\n", - MIN_KEY_LENGTH, newkey); + if (newkey_len < MIN_KEY_LENGTH + && (EVP_PKEY_CTX_is_a(genctx, "RSA") + || EVP_PKEY_CTX_is_a(genctx, "RSA-PSS") + || EVP_PKEY_CTX_is_a(genctx, "DSA"))) { + BIO_printf(bio_err, "Private key length too short, needs to be at least %d bits, not %ld.\n", + MIN_KEY_LENGTH, newkey_len); goto end; } - if (genctx == NULL) { - genctx = set_keygen_ctx(NULL, &pkey_type, &newkey, - &keyalgstr, gen_eng); - if (!genctx) - goto end; - } + if (newkey_len > OPENSSL_RSA_MAX_MODULUS_BITS + && (EVP_PKEY_CTX_is_a(genctx, "RSA") + || EVP_PKEY_CTX_is_a(genctx, "RSA-PSS"))) + BIO_printf(bio_err, + "Warning: It is not recommended to use more than %d bit for RSA keys.\n" + " Your key size is %ld! Larger key size may behave not as expected.\n", + OPENSSL_RSA_MAX_MODULUS_BITS, newkey_len); + +#ifndef OPENSSL_NO_DSA + if (EVP_PKEY_CTX_is_a(genctx, "DSA") + && newkey_len > OPENSSL_DSA_MAX_MODULUS_BITS) + BIO_printf(bio_err, + "Warning: It is not recommended to use more than %d bit for DSA keys.\n" + " Your key size is %ld! Larger key size may behave not as expected.\n", + OPENSSL_DSA_MAX_MODULUS_BITS, newkey_len); +#endif if (pkeyopts != NULL) { char *genopt; for (i = 0; i < sk_OPENSSL_STRING_num(pkeyopts); i++) { genopt = sk_OPENSSL_STRING_value(pkeyopts, i); if (pkey_ctrl_string(genctx, genopt) <= 0) { - BIO_printf(bio_err, "parameter error \"%s\"\n", genopt); - ERR_print_errors(bio_err); + BIO_printf(bio_err, "Key parameter error \"%s\"\n", genopt); goto end; } } } - if (pkey_type == EVP_PKEY_EC) { - BIO_printf(bio_err, "Generating an EC private key\n"); - } else { - BIO_printf(bio_err, "Generating a %ld bit %s private key\n", - newkey, keyalgstr); - } - - EVP_PKEY_CTX_set_cb(genctx, genpkey_cb); + EVP_PKEY_CTX_set_cb(genctx, progress_cb); EVP_PKEY_CTX_set_app_data(genctx, bio_err); - if (EVP_PKEY_keygen(genctx, &pkey) <= 0) { - BIO_puts(bio_err, "Error Generating Key\n"); - goto end; - } + pkey = app_keygen(genctx, keyalgstr, newkey_len, verbose); EVP_PKEY_CTX_free(genctx); genctx = NULL; + } + if (keyout == NULL && keyfile == NULL) { + keyout = NCONF_get_string(req_conf, section, KEYFILE); + if (keyout == NULL) + ERR_clear_error(); + } - if (keyout == NULL) { - keyout = NCONF_get_string(req_conf, SECTION, KEYFILE); + if (pkey != NULL && (keyfile == NULL || keyout != NULL)) { + if (verbose) { + BIO_printf(bio_err, "Writing private key to "); if (keyout == NULL) - ERR_clear_error(); + BIO_printf(bio_err, "stdout\n"); + else + BIO_printf(bio_err, "'%s'\n", keyout); } - - if (keyout == NULL) - BIO_printf(bio_err, "writing new private key to stdout\n"); - else - BIO_printf(bio_err, "writing new private key to '%s'\n", keyout); - out = bio_open_owner(keyout, outformat, private); + out = bio_open_owner(keyout, outformat, newreq); if (out == NULL) goto end; - p = NCONF_get_string(req_conf, SECTION, "encrypt_rsa_key"); + p = NCONF_get_string(req_conf, section, "encrypt_rsa_key"); if (p == NULL) { ERR_clear_error(); - p = NCONF_get_string(req_conf, SECTION, "encrypt_key"); + p = NCONF_get_string(req_conf, section, "encrypt_key"); if (p == NULL) ERR_clear_error(); } if ((p != NULL) && (strcmp(p, "no") == 0)) cipher = NULL; - if (nodes) + if (noenc) cipher = NULL; i = 0; loop: - assert(private); if (!PEM_write_bio_PrivateKey(out, pkey, cipher, NULL, 0, NULL, passout)) { if ((ERR_GET_REASON(ERR_peek_error()) == @@ -564,145 +717,201 @@ int req_main(int argc, char **argv) BIO_printf(bio_err, "-----\n"); } + /* + * subj is expected to be in the format /type0=value0/type1=value1/type2=... + * where characters may be escaped by \ + */ + if (subj != NULL + && (fsubj = parse_name(subj, chtype, multirdn, "subject")) == NULL) + goto end; + if (!newreq) { - in = bio_open_default(infile, 'r', informat); - if (in == NULL) + if (keyfile != NULL) + BIO_printf(bio_err, + "Warning: Not placing -key in cert or request since request is used\n"); + req = load_csr(infile /* if NULL, reads from stdin */, + informat, "X509 request"); + if (req == NULL) goto end; + } else if (infile != NULL) { + BIO_printf(bio_err, + "Warning: Ignoring -in option since -new or -newkey or -precert is given\n"); + /* Better throw an error in this case, as done in the x509 app */ + } - if (informat == FORMAT_ASN1) - req = d2i_X509_REQ_bio(in, NULL); - else - req = PEM_read_bio_X509_REQ(in, NULL, NULL, NULL); - if (req == NULL) { - BIO_printf(bio_err, "unable to load X509 request\n"); + if (CAkeyfile == NULL) + CAkeyfile = CAfile; + if (CAkeyfile != NULL) { + if (CAfile == NULL) { + BIO_printf(bio_err, + "Warning: Ignoring -CAkey option since no -CA option is given\n"); + } else { + if ((CAkey = load_key(CAkeyfile, FORMAT_UNDEF, + 0, passin, e, + CAkeyfile != CAfile + ? "issuer private key from -CAkey arg" + : "issuer private key from -CA arg")) == NULL) + goto end; + } + } + if (CAfile != NULL) { + if ((CAcert = load_cert_pass(CAfile, FORMAT_UNDEF, 1, passin, + "issuer cert from -CA arg")) == NULL) + goto end; + if (!X509_check_private_key(CAcert, CAkey)) { + BIO_printf(bio_err, + "Issuer CA certificate and key do not match\n"); goto end; } } - - if (newreq || x509) { - if (pkey == NULL) { - BIO_printf(bio_err, "you need to specify a private key\n"); + if (newreq || gen_x509) { + if (pkey == NULL /* can happen only if !newreq */) { + BIO_printf(bio_err, "Must provide a signature key using -key\n"); goto end; } if (req == NULL) { - req = X509_REQ_new(); + req = X509_REQ_new_ex(app_get0_libctx(), app_get0_propq()); if (req == NULL) { goto end; } - i = make_REQ(req, pkey, subj, multirdn, !x509, chtype); - subj = NULL; /* done processing '-subj' option */ - if (!i) { - BIO_printf(bio_err, "problems making Certificate Request\n"); + if (!make_REQ(req, pkey, fsubj, multirdn, !gen_x509, chtype)) { + BIO_printf(bio_err, "Error making certificate request\n"); goto end; } + /* Note that -x509 can take over -key and -subj option values. */ } - if (x509) { - EVP_PKEY *tmppkey; + if (gen_x509) { + EVP_PKEY *pub_key = X509_REQ_get0_pubkey(req); + EVP_PKEY *issuer_key = CAcert != NULL ? CAkey : pkey; X509V3_CTX ext_ctx; - if ((x509ss = X509_new()) == NULL) - goto end; + X509_NAME *issuer = CAcert != NULL ? X509_get_subject_name(CAcert) : + X509_REQ_get_subject_name(req); + X509_NAME *n_subj = fsubj != NULL ? fsubj : + X509_REQ_get_subject_name(req); - /* Set version to V3 */ - if (extensions != NULL && !X509_set_version(x509ss, 2)) + if (CAcert != NULL && keyfile != NULL) + BIO_printf(bio_err, + "Warning: Not using -key or -newkey for signing since -CA option is given\n"); + + if ((new_x509 = X509_new_ex(app_get0_libctx(), + app_get0_propq())) == NULL) goto end; + if (serial != NULL) { - if (!X509_set_serialNumber(x509ss, serial)) + if (!X509_set_serialNumber(new_x509, serial)) goto end; } else { - if (!rand_serial(NULL, X509_get_serialNumber(x509ss))) + if (!rand_serial(NULL, X509_get_serialNumber(new_x509))) goto end; } - if (!X509_set_issuer_name(x509ss, X509_REQ_get_subject_name(req))) + if (!X509_set_issuer_name(new_x509, issuer)) goto end; - if (!set_cert_times(x509ss, NULL, NULL, days)) + if (days == UNSET_DAYS) { + days = DEFAULT_DAYS; + } + if (!set_cert_times(new_x509, NULL, NULL, days)) + goto end; + if (!X509_set_subject_name(new_x509, n_subj)) goto end; - if (!X509_set_subject_name - (x509ss, X509_REQ_get_subject_name(req))) + if (!pub_key || !X509_set_pubkey(new_x509, pub_key)) goto end; - tmppkey = X509_REQ_get0_pubkey(req); - if (!tmppkey || !X509_set_pubkey(x509ss, tmppkey)) + if (ext_copy == EXT_COPY_UNSET) { + if (infile != NULL) + BIO_printf(bio_err, "Warning: No -copy_extensions given; ignoring any extensions in the request\n"); + } else if (!copy_extensions(new_x509, req, ext_copy)) { + BIO_printf(bio_err, "Error copying extensions from request\n"); goto end; + } /* Set up V3 context struct */ - - X509V3_set_ctx(&ext_ctx, x509ss, x509ss, NULL, NULL, 0); + X509V3_set_ctx(&ext_ctx, CAcert != NULL ? CAcert : new_x509, + new_x509, NULL, NULL, X509V3_CTX_REPLACE); + /* prepare fallback for AKID, but only if issuer cert == new_x509 */ + if (CAcert == NULL) { + if (!X509V3_set_issuer_pkey(&ext_ctx, issuer_key)) + goto end; + if (!cert_matches_key(new_x509, issuer_key)) + BIO_printf(bio_err, + "Warning: Signature key and public key of cert do not match\n"); + } X509V3_set_nconf(&ext_ctx, req_conf); /* Add extensions */ - if (extensions != NULL && !X509V3_EXT_add_nconf(req_conf, - &ext_ctx, extensions, - x509ss)) { - BIO_printf(bio_err, "Error Loading extension section %s\n", - extensions); + if (extsect != NULL + && !X509V3_EXT_add_nconf(req_conf, &ext_ctx, extsect, new_x509)) { + BIO_printf(bio_err, "Error adding x509 extensions from section %s\n", + extsect); + goto end; + } + if (addext_conf != NULL + && !X509V3_EXT_add_nconf(addext_conf, &ext_ctx, "default", + new_x509)) { + BIO_printf(bio_err, "Error adding x509 extensions defined via -addext\n"); goto end; } /* If a pre-cert was requested, we need to add a poison extension */ if (precert) { - if (X509_add1_ext_i2d(x509ss, NID_ct_precert_poison, NULL, 1, 0) - != 1) { + if (X509_add1_ext_i2d(new_x509, NID_ct_precert_poison, + NULL, 1, 0) != 1) { BIO_printf(bio_err, "Error adding poison extension\n"); goto end; } } - i = do_X509_sign(x509ss, pkey, digest, sigopts); - if (!i) { - ERR_print_errors(bio_err); + i = do_X509_sign(new_x509, issuer_key, digest, sigopts, &ext_ctx); + if (!i) goto end; - } } else { X509V3_CTX ext_ctx; + if (precert) { + BIO_printf(bio_err, + "Warning: Ignoring -precert flag since no cert is produced\n"); + } /* Set up V3 context struct */ - - X509V3_set_ctx(&ext_ctx, NULL, NULL, req, NULL, 0); + X509V3_set_ctx(&ext_ctx, NULL, NULL, req, NULL, X509V3_CTX_REPLACE); X509V3_set_nconf(&ext_ctx, req_conf); /* Add extensions */ - if (req_exts != NULL - && !X509V3_EXT_REQ_add_nconf(req_conf, &ext_ctx, - req_exts, req)) { - BIO_printf(bio_err, "Error Loading extension section %s\n", - req_exts); + if (extsect != NULL + && !X509V3_EXT_REQ_add_nconf(req_conf, &ext_ctx, extsect, req)) { + BIO_printf(bio_err, "Error adding request extensions from section %s\n", + extsect); goto end; } - i = do_X509_REQ_sign(req, pkey, digest, sigopts); - if (!i) { - ERR_print_errors(bio_err); + if (addext_conf != NULL + && !X509V3_EXT_REQ_add_nconf(addext_conf, &ext_ctx, "default", + req)) { + BIO_printf(bio_err, "Error adding request extensions defined via -addext\n"); goto end; } + i = do_X509_REQ_sign(req, pkey, digest, sigopts); + if (!i) + goto end; } } - if (subj && x509) { - BIO_printf(bio_err, "Cannot modify certificate subject\n"); - goto end; - } - - if (subj && !x509) { + if (subj != NULL && !newreq && !gen_x509) { if (verbose) { - BIO_printf(bio_err, "Modifying Request's Subject\n"); - print_name(bio_err, "old subject=", - X509_REQ_get_subject_name(req), get_nameopt()); + BIO_printf(out, "Modifying subject of certificate request\n"); + print_name(out, "Old subject=", X509_REQ_get_subject_name(req)); } - if (build_subject(req, subj, chtype, multirdn) == 0) { - BIO_printf(bio_err, "ERROR: cannot modify subject\n"); - ret = 1; + if (!X509_REQ_set_subject_name(req, fsubj)) { + BIO_printf(bio_err, "Error modifying subject of certificate request\n"); goto end; } if (verbose) { - print_name(bio_err, "new subject=", - X509_REQ_get_subject_name(req), get_nameopt()); + print_name(out, "New subject=", X509_REQ_get_subject_name(req)); } } - if (verify && !x509) { + if (verify) { EVP_PKEY *tpubkey = pkey; if (tpubkey == NULL) { @@ -711,16 +920,14 @@ int req_main(int argc, char **argv) goto end; } - i = X509_REQ_verify(req, tpubkey); + i = do_X509_REQ_verify(req, tpubkey, vfyopts); - if (i < 0) { + if (i < 0) goto end; - } else if (i == 0) { - BIO_printf(bio_err, "verify failure\n"); - ERR_print_errors(bio_err); - } else { /* if (i > 0) */ - BIO_printf(bio_err, "verify OK\n"); - } + if (i == 0) + BIO_printf(bio_err, "Certificate request self-signature verify failure\n"); + else /* i > 0 */ + BIO_printf(bio_err, "Certificate request self-signature verify OK\n"); } if (noout && !text && !modulus && !subject && !pubkey) { @@ -740,52 +947,58 @@ int req_main(int argc, char **argv) if (tpubkey == NULL) { BIO_printf(bio_err, "Error getting public key\n"); - ERR_print_errors(bio_err); goto end; } PEM_write_bio_PUBKEY(out, tpubkey); } if (text) { - if (x509) - X509_print_ex(out, x509ss, get_nameopt(), reqflag); + if (gen_x509) + ret = X509_print_ex(out, new_x509, get_nameopt(), reqflag); else - X509_REQ_print_ex(out, req, get_nameopt(), reqflag); + ret = X509_REQ_print_ex(out, req, get_nameopt(), reqflag); + + if (ret == 0) { + if (gen_x509) + BIO_printf(bio_err, "Error printing certificate\n"); + else + BIO_printf(bio_err, "Error printing certificate request\n"); + goto end; + } } if (subject) { - if (x509) - print_name(out, "subject=", X509_get_subject_name(x509ss), - get_nameopt()); - else - print_name(out, "subject=", X509_REQ_get_subject_name(req), - get_nameopt()); + print_name(out, "subject=", gen_x509 + ? X509_get_subject_name(new_x509) + : X509_REQ_get_subject_name(req)); } if (modulus) { EVP_PKEY *tpubkey; - if (x509) - tpubkey = X509_get0_pubkey(x509ss); + if (gen_x509) + tpubkey = X509_get0_pubkey(new_x509); else tpubkey = X509_REQ_get0_pubkey(req); if (tpubkey == NULL) { - fprintf(stdout, "Modulus=unavailable\n"); + fprintf(stdout, "Modulus is unavailable\n"); goto end; } fprintf(stdout, "Modulus="); -#ifndef OPENSSL_NO_RSA - if (EVP_PKEY_base_id(tpubkey) == EVP_PKEY_RSA) { - const BIGNUM *n; - RSA_get0_key(EVP_PKEY_get0_RSA(tpubkey), &n, NULL, NULL); + if (EVP_PKEY_is_a(tpubkey, "RSA")) { + BIGNUM *n = NULL; + + if (!EVP_PKEY_get_bn_param(tpubkey, "n", &n)) + goto end; BN_print(out, n); - } else -#endif + BN_free(n); + } else { fprintf(stdout, "Wrong Algorithm type"); + } fprintf(stdout, "\n"); } - if (!noout && !x509) { + if (!noout && !gen_x509) { if (outformat == FORMAT_ASN1) i = i2d_X509_REQ_bio(out, req); else if (newhdr) @@ -793,17 +1006,17 @@ int req_main(int argc, char **argv) else i = PEM_write_bio_X509_REQ(out, req); if (!i) { - BIO_printf(bio_err, "unable to write X509 request\n"); + BIO_printf(bio_err, "Unable to write certificate request\n"); goto end; } } - if (!noout && x509 && (x509ss != NULL)) { + if (!noout && gen_x509 && new_x509 != NULL) { if (outformat == FORMAT_ASN1) - i = i2d_X509_bio(out, x509ss); + i = i2d_X509_bio(out, new_x509); else - i = PEM_write_bio_X509(out, x509ss); + i = PEM_write_bio_X509(out, new_x509); if (!i) { - BIO_printf(bio_err, "unable to write X509 certificate\n"); + BIO_printf(bio_err, "Unable to write X509 certificate\n"); goto end; } } @@ -813,18 +1026,25 @@ int req_main(int argc, char **argv) ERR_print_errors(bio_err); } NCONF_free(req_conf); - BIO_free(in); + NCONF_free(addext_conf); + BIO_free(addext_bio); BIO_free_all(out); EVP_PKEY_free(pkey); EVP_PKEY_CTX_free(genctx); sk_OPENSSL_STRING_free(pkeyopts); sk_OPENSSL_STRING_free(sigopts); + sk_OPENSSL_STRING_free(vfyopts); + lh_OPENSSL_STRING_doall(addexts, exts_cleanup); + lh_OPENSSL_STRING_free(addexts); #ifndef OPENSSL_NO_ENGINE - ENGINE_free(gen_eng); + release_engine(gen_eng); #endif OPENSSL_free(keyalgstr); X509_REQ_free(req); - X509_free(x509ss); + X509_NAME_free(fsubj); + X509_free(new_x509); + X509_free(CAcert); + EVP_PKEY_free(CAkey); ASN1_INTEGER_free(serial); release_engine(e); if (passin != nofree_passin) @@ -834,50 +1054,48 @@ int req_main(int argc, char **argv) return ret; } -static int make_REQ(X509_REQ *req, EVP_PKEY *pkey, char *subj, int multirdn, - int attribs, unsigned long chtype) +static int make_REQ(X509_REQ *req, EVP_PKEY *pkey, X509_NAME *fsubj, + int multirdn, int attribs, unsigned long chtype) { int ret = 0, i; char no_prompt = 0; - STACK_OF(CONF_VALUE) *dn_sk, *attr_sk = NULL; + STACK_OF(CONF_VALUE) *dn_sk = NULL, *attr_sk = NULL; char *tmp, *dn_sect, *attr_sect; - tmp = NCONF_get_string(req_conf, SECTION, PROMPT); + tmp = NCONF_get_string(req_conf, section, PROMPT); if (tmp == NULL) ERR_clear_error(); if ((tmp != NULL) && strcmp(tmp, "no") == 0) no_prompt = 1; - dn_sect = NCONF_get_string(req_conf, SECTION, DISTINGUISHED_NAME); + dn_sect = NCONF_get_string(req_conf, section, DISTINGUISHED_NAME); if (dn_sect == NULL) { - BIO_printf(bio_err, "unable to find '%s' in config\n", - DISTINGUISHED_NAME); - goto err; - } - dn_sk = NCONF_get_section(req_conf, dn_sect); - if (dn_sk == NULL) { - BIO_printf(bio_err, "unable to get '%s' section\n", dn_sect); - goto err; + ERR_clear_error(); + } else { + dn_sk = NCONF_get_section(req_conf, dn_sect); + if (dn_sk == NULL) { + BIO_printf(bio_err, "Unable to get '%s' section\n", dn_sect); + goto err; + } } - attr_sect = NCONF_get_string(req_conf, SECTION, ATTRIBUTES); + attr_sect = NCONF_get_string(req_conf, section, ATTRIBUTES); if (attr_sect == NULL) { ERR_clear_error(); - attr_sk = NULL; } else { attr_sk = NCONF_get_section(req_conf, attr_sect); if (attr_sk == NULL) { - BIO_printf(bio_err, "unable to get '%s' section\n", attr_sect); + BIO_printf(bio_err, "Unable to get '%s' section\n", attr_sect); goto err; } } - /* setup version number */ - if (!X509_REQ_set_version(req, 0L)) - goto err; /* version 1 */ + /* so far there is only version 1 */ + if (!X509_REQ_set_version(req, X509_REQ_VERSION_1)) + goto err; - if (subj) - i = build_subject(req, subj, chtype, multirdn); + if (fsubj != NULL) + i = X509_REQ_set_subject_name(req, fsubj); else if (no_prompt) i = auto_info(req, dn_sk, attr_sk, attribs, chtype); else @@ -894,26 +1112,6 @@ static int make_REQ(X509_REQ *req, EVP_PKEY *pkey, char *subj, int multirdn, return ret; } -/* - * subject is expected to be in the format /type0=value0/type1=value1/type2=... - * where characters may be escaped by \ - */ -static int build_subject(X509_REQ *req, const char *subject, unsigned long chtype, - int multirdn) -{ - X509_NAME *n; - - if ((n = parse_name(subject, chtype, multirdn)) == NULL) - return 0; - - if (!X509_REQ_set_subject_name(req, n)) { - X509_NAME_free(n); - return 0; - } - X509_NAME_free(n); - return 1; -} - static int prompt_info(X509_REQ *req, STACK_OF(CONF_VALUE) *dn_sk, const char *dn_sect, STACK_OF(CONF_VALUE) *attr_sk, const char *attr_sect, @@ -927,8 +1125,7 @@ static int prompt_info(X509_REQ *req, char *type, *value; const char *def; CONF_VALUE *v; - X509_NAME *subj; - subj = X509_REQ_get_subject_name(req); + X509_NAME *subj = X509_REQ_get_subject_name(req); if (!batch) { BIO_printf(bio_err, @@ -948,7 +1145,7 @@ static int prompt_info(X509_REQ *req, if (sk_CONF_VALUE_num(dn_sk)) { i = -1; start: - for ( ; ; ) { + for (;;) { i++; if (sk_CONF_VALUE_num(dn_sk) <= i) break; @@ -1000,7 +1197,6 @@ static int prompt_info(X509_REQ *req, n_min = -1; } - if (!join(buf, sizeof(buf), v->name, "_max", "Name")) return 0; if (!NCONF_get_number(req_conf, dn_sect, buf, &n_max)) { @@ -1013,8 +1209,7 @@ static int prompt_info(X509_REQ *req, return 0; } if (X509_NAME_entry_count(subj) == 0) { - BIO_printf(bio_err, - "error, no objects specified in config file\n"); + BIO_printf(bio_err, "Error: No objects specified in config file\n"); return 0; } @@ -1029,7 +1224,7 @@ static int prompt_info(X509_REQ *req, i = -1; start2: - for ( ; ; ) { + for (;;) { i++; if ((attr_sk == NULL) || (sk_CONF_VALUE_num(attr_sk) <= i)) break; @@ -1055,7 +1250,7 @@ static int prompt_info(X509_REQ *req, value = NULL; } - if (!join(buf, sizeof(buf), type,"_min", "Name")) + if (!join(buf, sizeof(buf), type, "_min", "Name")) return 0; if (!NCONF_get_number(req_conf, attr_sect, buf, &n_min)) { ERR_clear_error(); @@ -1106,10 +1301,10 @@ static int auto_info(X509_REQ *req, STACK_OF(CONF_VALUE) *dn_sk, */ for (p = v->name; *p; p++) { #ifndef CHARSET_EBCDIC - spec_char = ((*p == ':') || (*p == ',') || (*p == '.')); + spec_char = (*p == ':' || *p == ',' || *p == '.'); #else - spec_char = ((*p == os_toascii[':']) || (*p == os_toascii[',']) - || (*p == os_toascii['.'])); + spec_char = (*p == os_toascii[':'] || *p == os_toascii[','] + || *p == os_toascii['.']); #endif if (spec_char) { p++; @@ -1137,7 +1332,7 @@ static int auto_info(X509_REQ *req, STACK_OF(CONF_VALUE) *dn_sk, } if (!X509_NAME_entry_count(subj)) { - BIO_printf(bio_err, "error, no objects specified in config file\n"); + BIO_printf(bio_err, "Error: No objects specified in config file\n"); return 0; } if (attribs) { @@ -1155,58 +1350,19 @@ static int add_DN_object(X509_NAME *n, char *text, const char *def, char *value, int nid, int n_min, int n_max, unsigned long chtype, int mval) { - int i, ret = 0; + int ret = 0; char buf[1024]; - start: - if (!batch) - BIO_printf(bio_err, "%s [%s]:", text, def); - (void)BIO_flush(bio_err); - if (value != NULL) { - if (!join(buf, sizeof(buf), value, "\n", "DN value")) - return 0; - BIO_printf(bio_err, "%s\n", value); - } else { - buf[0] = '\0'; - if (!batch) { - if (!fgets(buf, sizeof buf, stdin)) - return 0; - } else { - buf[0] = '\n'; - buf[1] = '\0'; - } - } - if (buf[0] == '\0') - return 0; - if (buf[0] == '\n') { - if ((def == NULL) || (def[0] == '\0')) - return 1; - if (!join(buf, sizeof(buf), def, "\n", "DN default")) - return 0; - } else if ((buf[0] == '.') && (buf[1] == '\n')) { - return 1; - } - - i = strlen(buf); - if (buf[i - 1] != '\n') { - BIO_printf(bio_err, "weird input :-(\n"); - return 0; - } - buf[--i] = '\0'; -#ifdef CHARSET_EBCDIC - ebcdic2ascii(buf, buf, i); -#endif - if (!req_check_len(i, n_min, n_max)) { - if (batch || value) - return 0; - goto start; - } + ret = build_data(text, def, value, n_min, n_max, buf, sizeof(buf), + "DN value", "DN default"); + if ((ret == 0) || (ret == 1)) + return ret; + ret = 1; if (!X509_NAME_add_entry_by_NID(n, nid, chtype, (unsigned char *)buf, -1, -1, mval)) - goto err; - ret = 1; - err: + ret = 0; + return ret; } @@ -1214,21 +1370,41 @@ static int add_attribute_object(X509_REQ *req, char *text, const char *def, char *value, int nid, int n_min, int n_max, unsigned long chtype) { - int i; - static char buf[1024]; + int ret = 0; + char buf[1024]; + + ret = build_data(text, def, value, n_min, n_max, buf, sizeof(buf), + "Attribute value", "Attribute default"); + if ((ret == 0) || (ret == 1)) + return ret; + ret = 1; + + if (!X509_REQ_add1_attr_by_NID(req, nid, chtype, + (unsigned char *)buf, -1)) { + BIO_printf(bio_err, "Error adding attribute\n"); + ret = 0; + } + + return ret; +} +static int build_data(char *text, const char *def, char *value, + int n_min, int n_max, char *buf, const int buf_size, + const char *desc1, const char *desc2) +{ + int i; start: if (!batch) BIO_printf(bio_err, "%s [%s]:", text, def); (void)BIO_flush(bio_err); if (value != NULL) { - if (!join(buf, sizeof(buf), value, "\n", "Attribute value")) + if (!join(buf, buf_size, value, "\n", desc1)) return 0; BIO_printf(bio_err, "%s\n", value); } else { buf[0] = '\0'; if (!batch) { - if (!fgets(buf, sizeof buf, stdin)) + if (!fgets(buf, buf_size, stdin)) return 0; } else { buf[0] = '\n'; @@ -1241,7 +1417,7 @@ static int add_attribute_object(X509_REQ *req, char *text, const char *def, if (buf[0] == '\n') { if ((def == NULL) || (def[0] == '\0')) return 1; - if (!join(buf, sizeof(buf), def, "\n", "Attribute default")) + if (!join(buf, buf_size, def, "\n", desc2)) return 0; } else if ((buf[0] == '.') && (buf[1] == '\n')) { return 1; @@ -1249,7 +1425,7 @@ static int add_attribute_object(X509_REQ *req, char *text, const char *def, i = strlen(buf); if (buf[i - 1] != '\n') { - BIO_printf(bio_err, "weird input :-(\n"); + BIO_printf(bio_err, "Missing newline at end of input\n"); return 0; } buf[--i] = '\0'; @@ -1261,31 +1437,19 @@ static int add_attribute_object(X509_REQ *req, char *text, const char *def, return 0; goto start; } - - if (!X509_REQ_add1_attr_by_NID(req, nid, chtype, - (unsigned char *)buf, -1)) { - BIO_printf(bio_err, "Error adding attribute\n"); - ERR_print_errors(bio_err); - goto err; - } - - return 1; - err: - return 0; + return 2; } static int req_check_len(int len, int n_min, int n_max) { - if ((n_min > 0) && (len < n_min)) { + if (n_min > 0 && len < n_min) { BIO_printf(bio_err, - "string is too short, it needs to be at least %d bytes long\n", - n_min); + "String too short, must be at least %d bytes long\n", n_min); return 0; } - if ((n_max >= 0) && (len > n_max)) { + if (n_max >= 0 && len > n_max) { BIO_printf(bio_err, - "string is too long, it needs to be no more than %d bytes long\n", - n_max); + "String too long, must be at most %d bytes long\n", n_max); return 0; } return 1; @@ -1324,66 +1488,71 @@ static int join(char buf[], size_t buf_size, const char *name, } static EVP_PKEY_CTX *set_keygen_ctx(const char *gstr, - int *pkey_type, long *pkeylen, - char **palgnam, ENGINE *keygen_engine) + char **pkeytype, long *pkeylen, + ENGINE *keygen_engine) { EVP_PKEY_CTX *gctx = NULL; EVP_PKEY *param = NULL; long keylen = -1; BIO *pbio = NULL; + const char *keytype = NULL; + size_t keytypelen = 0; + int expect_paramfile = 0; const char *paramfile = NULL; + /* Treat the first part of gstr, and only that */ if (gstr == NULL) { - *pkey_type = EVP_PKEY_RSA; + /* + * Special case: when no string given, default to RSA and the + * key length given by |*pkeylen|. + */ + keytype = "RSA"; keylen = *pkeylen; } else if (gstr[0] >= '0' && gstr[0] <= '9') { - *pkey_type = EVP_PKEY_RSA; - keylen = atol(gstr); - *pkeylen = keylen; - } else if (strncmp(gstr, "param:", 6) == 0) { - paramfile = gstr + 6; + /* Special case: only keylength given from string, so default to RSA */ + keytype = "RSA"; + /* The second part treatment will do the rest */ } else { const char *p = strchr(gstr, ':'); int len; - ENGINE *tmpeng; - const EVP_PKEY_ASN1_METHOD *ameth; if (p != NULL) len = p - gstr; else len = strlen(gstr); - /* - * The lookup of a the string will cover all engines so keep a note - * of the implementation. - */ - - ameth = EVP_PKEY_asn1_find_str(&tmpeng, gstr, len); - - if (ameth == NULL) { - BIO_printf(bio_err, "Unknown algorithm %.*s\n", len, gstr); - return NULL; - } - EVP_PKEY_asn1_get0_info(NULL, pkey_type, NULL, NULL, NULL, ameth); -#ifndef OPENSSL_NO_ENGINE - ENGINE_finish(tmpeng); -#endif - if (*pkey_type == EVP_PKEY_RSA) { - if (p != NULL) { - keylen = atol(p + 1); - *pkeylen = keylen; - } else { - keylen = *pkeylen; + if (strncmp(gstr, "param", len) == 0) { + expect_paramfile = 1; + if (p == NULL) { + BIO_printf(bio_err, + "Parameter file requested but no path given: %s\n", + gstr); + return NULL; } - } else if (p != NULL) { - paramfile = p + 1; + } else { + keytype = gstr; + keytypelen = len; } + + if (p != NULL) + gstr = gstr + len + 1; + else + gstr = NULL; + } + + /* Treat the second part of gstr, if there is one */ + if (gstr != NULL) { + /* If the second part starts with a digit, we assume it's a size */ + if (!expect_paramfile && gstr[0] >= '0' && gstr[0] <= '9') + keylen = atol(gstr); + else + paramfile = gstr; } if (paramfile != NULL) { pbio = BIO_new_file(paramfile, "r"); if (pbio == NULL) { - BIO_printf(bio_err, "Can't open parameter file %s\n", paramfile); + BIO_printf(bio_err, "Cannot open parameter file %s\n", paramfile); return NULL; } param = PEM_read_bio_Parameters(pbio, NULL); @@ -1405,139 +1574,77 @@ static EVP_PKEY_CTX *set_keygen_ctx(const char *gstr, BIO_printf(bio_err, "Error reading parameter file %s\n", paramfile); return NULL; } - if (*pkey_type == -1) { - *pkey_type = EVP_PKEY_id(param); - } else if (*pkey_type != EVP_PKEY_base_id(param)) { - BIO_printf(bio_err, "Key Type does not match parameters\n"); - EVP_PKEY_free(param); - return NULL; + if (keytype == NULL) { + keytype = EVP_PKEY_get0_type_name(param); + if (keytype == NULL) { + EVP_PKEY_free(param); + BIO_puts(bio_err, "Unable to determine key type\n"); + return NULL; + } } } - if (palgnam != NULL) { - const EVP_PKEY_ASN1_METHOD *ameth; - ENGINE *tmpeng; - const char *anam; + if (keytypelen > 0) + *pkeytype = OPENSSL_strndup(keytype, keytypelen); + else + *pkeytype = OPENSSL_strdup(keytype); + if (keylen >= 0) + *pkeylen = keylen; - ameth = EVP_PKEY_asn1_find(&tmpeng, *pkey_type); - if (ameth == NULL) { - BIO_puts(bio_err, "Internal error: can't find key algorithm\n"); + if (param != NULL) { + if (!EVP_PKEY_is_a(param, *pkeytype)) { + BIO_printf(bio_err, "Key type does not match parameters\n"); + EVP_PKEY_free(param); return NULL; } - EVP_PKEY_asn1_get0_info(NULL, NULL, NULL, NULL, &anam, ameth); - *palgnam = OPENSSL_strdup(anam); -#ifndef OPENSSL_NO_ENGINE - ENGINE_finish(tmpeng); -#endif - } - if (param != NULL) { - gctx = EVP_PKEY_CTX_new(param, keygen_engine); - *pkeylen = EVP_PKEY_bits(param); + if (keygen_engine != NULL) + gctx = EVP_PKEY_CTX_new(param, keygen_engine); + else + gctx = EVP_PKEY_CTX_new_from_pkey(app_get0_libctx(), + param, app_get0_propq()); + *pkeylen = EVP_PKEY_get_bits(param); EVP_PKEY_free(param); } else { - gctx = EVP_PKEY_CTX_new_id(*pkey_type, keygen_engine); + if (keygen_engine != NULL) { + int pkey_id = get_legacy_pkey_id(app_get0_libctx(), *pkeytype, + keygen_engine); + + if (pkey_id != NID_undef) + gctx = EVP_PKEY_CTX_new_id(pkey_id, keygen_engine); + } else { + gctx = EVP_PKEY_CTX_new_from_name(app_get0_libctx(), + *pkeytype, app_get0_propq()); + } } if (gctx == NULL) { BIO_puts(bio_err, "Error allocating keygen context\n"); - ERR_print_errors(bio_err); return NULL; } if (EVP_PKEY_keygen_init(gctx) <= 0) { BIO_puts(bio_err, "Error initializing keygen context\n"); - ERR_print_errors(bio_err); EVP_PKEY_CTX_free(gctx); return NULL; } -#ifndef OPENSSL_NO_RSA - if ((*pkey_type == EVP_PKEY_RSA) && (keylen != -1)) { - if (EVP_PKEY_CTX_set_rsa_keygen_bits(gctx, keylen) <= 0) { - BIO_puts(bio_err, "Error setting RSA keysize\n"); - ERR_print_errors(bio_err); + if (keylen == -1 && (EVP_PKEY_CTX_is_a(gctx, "RSA") + || EVP_PKEY_CTX_is_a(gctx, "RSA-PSS"))) + keylen = *pkeylen; + + if (keylen != -1) { + OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END }; + size_t bits = keylen; + + params[0] = + OSSL_PARAM_construct_size_t(OSSL_PKEY_PARAM_BITS, &bits); + if (EVP_PKEY_CTX_set_params(gctx, params) <= 0) { + BIO_puts(bio_err, "Error setting keysize\n"); EVP_PKEY_CTX_free(gctx); return NULL; } } -#endif return gctx; } -static int genpkey_cb(EVP_PKEY_CTX *ctx) -{ - char c = '*'; - BIO *b = EVP_PKEY_CTX_get_app_data(ctx); - int p; - p = EVP_PKEY_CTX_get_keygen_info(ctx, 0); - if (p == 0) - c = '.'; - if (p == 1) - c = '+'; - if (p == 2) - c = '*'; - if (p == 3) - c = '\n'; - BIO_write(b, &c, 1); - (void)BIO_flush(b); - return 1; -} - -static int do_sign_init(EVP_MD_CTX *ctx, EVP_PKEY *pkey, - const EVP_MD *md, STACK_OF(OPENSSL_STRING) *sigopts) -{ - EVP_PKEY_CTX *pkctx = NULL; - int i; - - if (ctx == NULL) - return 0; - if (!EVP_DigestSignInit(ctx, &pkctx, md, NULL, pkey)) - return 0; - for (i = 0; i < sk_OPENSSL_STRING_num(sigopts); i++) { - char *sigopt = sk_OPENSSL_STRING_value(sigopts, i); - if (pkey_ctrl_string(pkctx, sigopt) <= 0) { - BIO_printf(bio_err, "parameter error \"%s\"\n", sigopt); - ERR_print_errors(bio_err); - return 0; - } - } - return 1; -} - -int do_X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md, - STACK_OF(OPENSSL_STRING) *sigopts) -{ - int rv; - EVP_MD_CTX *mctx = EVP_MD_CTX_new(); - - rv = do_sign_init(mctx, pkey, md, sigopts); - if (rv > 0) - rv = X509_sign_ctx(x, mctx); - EVP_MD_CTX_free(mctx); - return rv > 0 ? 1 : 0; -} - -int do_X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const EVP_MD *md, - STACK_OF(OPENSSL_STRING) *sigopts) -{ - int rv; - EVP_MD_CTX *mctx = EVP_MD_CTX_new(); - rv = do_sign_init(mctx, pkey, md, sigopts); - if (rv > 0) - rv = X509_REQ_sign_ctx(x, mctx); - EVP_MD_CTX_free(mctx); - return rv > 0 ? 1 : 0; -} - -int do_X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const EVP_MD *md, - STACK_OF(OPENSSL_STRING) *sigopts) -{ - int rv; - EVP_MD_CTX *mctx = EVP_MD_CTX_new(); - rv = do_sign_init(mctx, pkey, md, sigopts); - if (rv > 0) - rv = X509_CRL_sign_ctx(x, mctx); - EVP_MD_CTX_free(mctx); - return rv > 0 ? 1 : 0; -}