File: [local] / src / usr.bin / openssl / req.c (download)
Revision 1.29, Wed Apr 17 01:24:43 2024 UTC (7 weeks, 5 days ago) by tb
Branch: MAIN
CVS Tags: HEAD Changes since 1.28: +2 -1 lines
openssl req: plug obvious leak
CID 492603
|
/* $OpenBSD: req.c,v 1.29 2024/04/17 01:24:43 tb Exp $ */
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
* This package is an SSL implementation written
* by Eric Young (eay@cryptsoft.com).
* The implementation was written so as to conform with Netscapes SSL.
*
* This library is free for commercial and non-commercial use as long as
* the following conditions are aheared to. The following conditions
* apply to all code found in this distribution, be it the RC4, RSA,
* lhash, DES, etc., code; not just the SSL code. The SSL documentation
* included with this distribution is covered by the same copyright terms
* except that the holder is Tim Hudson (tjh@cryptsoft.com).
*
* Copyright remains Eric Young's, and as such any Copyright notices in
* the code are not to be removed.
* If this package is used in a product, Eric Young should be given attribution
* as the author of the parts of the library used.
* This can be in the form of a textual message at program startup or
* in documentation (online or textual) provided with the package.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* "This product includes cryptographic software written by
* Eric Young (eay@cryptsoft.com)"
* The word 'cryptographic' can be left out if the rouines from the library
* being used are not cryptographic related :-).
* 4. If you include any Windows specific code (or a derivative thereof) from
* the apps directory (application code) you must include an acknowledgement:
* "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
*
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* The licence and distribution terms for any publically available version or
* derivative of this code cannot be changed. i.e. this code cannot simply be
* copied and put under another distribution licence
* [including the GNU Public Licence.]
*/
/* Until the key-gen callbacks are modified to use newer prototypes, we allow
* deprecated functions for openssl-internal code */
#ifdef OPENSSL_NO_DEPRECATED
#undef OPENSSL_NO_DEPRECATED
#endif
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "apps.h"
#include <openssl/asn1.h>
#include <openssl/bio.h>
#include <openssl/bn.h>
#include <openssl/conf.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/objects.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/dsa.h>
#include <openssl/rsa.h>
#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 384
static int make_REQ(X509_REQ * req, EVP_PKEY * pkey, char *dn, int multirdn,
int attribs, unsigned long chtype);
static int build_subject(X509_REQ * req, char *subj, unsigned long chtype,
int multirdn);
static int prompt_info(X509_REQ * req,
STACK_OF(CONF_VALUE) * dn_sk, char *dn_sect,
STACK_OF(CONF_VALUE) * attr_sk, char *attr_sect, int attribs,
unsigned long chtype);
static int auto_info(X509_REQ * req, STACK_OF(CONF_VALUE) * sk,
STACK_OF(CONF_VALUE) * attr, int attribs,
unsigned long chtype);
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);
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 req_check_len(int len, int n_min, int n_max);
static int check_end(const char *str, const char *end);
static EVP_PKEY_CTX *set_keygen_ctx(BIO * err, const char *gstr, int *pkey_type,
long *pkeylen, char **palgnam);
static unsigned long ext_name_hash(const OPENSSL_STRING *a);
static int ext_name_cmp(const OPENSSL_STRING *a, const OPENSSL_STRING *b);
static void exts_cleanup(OPENSSL_STRING *x);
static int duplicated(LHASH_OF(OPENSSL_STRING) *addexts, char *kv);
static CONF *req_conf = NULL;
static CONF *addext_conf = NULL;
static struct {
LHASH_OF(OPENSSL_STRING) *addexts;
BIO *addext_bio;
int batch;
unsigned long chtype;
int days;
const EVP_MD *digest;
char *extensions;
char *infile;
int informat;
char *keyalg;
char *keyfile;
int keyform;
char *keyout;
int modulus;
int multirdn;
int newhdr;
long newkey;
int newreq;
unsigned long nmflag;
int nodes;
int noout;
char *outfile;
int outformat;
char *passargin;
char *passargout;
STACK_OF(OPENSSL_STRING) *pkeyopts;
int pubkey;
char *req_exts;
unsigned long reqflag;
ASN1_INTEGER *serial;
STACK_OF(OPENSSL_STRING) *sigopts;
char *subj;
int subject;
char *template;
int text;
int verbose;
int verify;
int x509;
} cfg;
static int
req_opt_addext(char *arg)
{
int i;
if (cfg.addexts == NULL) {
cfg.addexts = (LHASH_OF(OPENSSL_STRING) *)lh_new(
(LHASH_HASH_FN_TYPE)ext_name_hash,
(LHASH_COMP_FN_TYPE)ext_name_cmp);
cfg.addext_bio = BIO_new(BIO_s_mem());
if (cfg.addexts == NULL ||
cfg.addext_bio == NULL)
return (1);
}
i = duplicated(cfg.addexts, arg);
if (i == 1)
return (1);
if (i < 0 || BIO_printf(cfg.addext_bio, "%s\n", arg) < 0)
return (1);
return (0);
}
static int
req_opt_days(char *arg)
{
const char *errstr;
cfg.days = strtonum(arg, 1, INT_MAX, &errstr);
if (errstr != NULL) {
BIO_printf(bio_err, "bad -days %s, using 0: %s\n",
arg, errstr);
cfg.days = 30;
}
return (0);
}
static int
req_opt_digest(int argc, char **argv, int *argsused)
{
char *name = argv[0];
if (*name++ != '-')
return (1);
if ((cfg.digest = EVP_get_digestbyname(name)) == NULL)
return (1);
*argsused = 1;
return (0);
}
static int
req_opt_newkey(char *arg)
{
cfg.keyalg = arg;
cfg.newreq = 1;
return (0);
}
static int
req_opt_nameopt(char *arg)
{
if (!set_name_ex(&cfg.nmflag, arg))
return (1);
return (0);
}
static int
req_opt_pkeyopt(char *arg)
{
if (cfg.pkeyopts == NULL)
cfg.pkeyopts = sk_OPENSSL_STRING_new_null();
if (cfg.pkeyopts == NULL)
return (1);
if (!sk_OPENSSL_STRING_push(cfg.pkeyopts, arg))
return (1);
return (0);
}
static int
req_opt_reqopt(char *arg)
{
if (!set_cert_ex(&cfg.reqflag, arg))
return (1);
return (0);
}
static int
req_opt_set_serial(char *arg)
{
cfg.serial = s2i_ASN1_INTEGER(NULL, arg);
if (cfg.serial == NULL)
return (1);
return (0);
}
static int
req_opt_sigopt(char *arg)
{
if (cfg.sigopts == NULL)
cfg.sigopts = sk_OPENSSL_STRING_new_null();
if (cfg.sigopts == NULL)
return (1);
if (!sk_OPENSSL_STRING_push(cfg.sigopts, arg))
return (1);
return (0);
}
static int
req_opt_utf8(void)
{
cfg.chtype = MBSTRING_UTF8;
return (0);
}
static const struct option req_options[] = {
{
.name = "addext",
.argname = "key=value",
.desc = "Additional certificate extension (may be repeated)",
.type = OPTION_ARG_FUNC,
.opt.argfunc = req_opt_addext,
},
{
.name = "batch",
.desc = "Operate in batch mode",
.type = OPTION_FLAG,
.opt.flag = &cfg.batch,
},
{
.name = "config",
.argname = "file",
.desc = "Configuration file to use as request template",
.type = OPTION_ARG,
.opt.arg = &cfg.template,
},
{
.name = "days",
.argname = "number",
.desc = "Number of days generated certificate is valid for",
.type = OPTION_ARG_FUNC,
.opt.argfunc = req_opt_days,
},
{
.name = "extensions",
.argname = "section",
.desc = "Config section to use for certificate extensions",
.type = OPTION_ARG,
.opt.arg = &cfg.extensions,
},
{
.name = "in",
.argname = "file",
.desc = "Input file (default stdin)",
.type = OPTION_ARG,
.opt.arg = &cfg.infile,
},
{
.name = "inform",
.argname = "format",
.desc = "Input format (DER or PEM (default))",
.type = OPTION_ARG_FORMAT,
.opt.value = &cfg.informat,
},
{
.name = "key",
.argname = "file",
.desc = "Private key file",
.type = OPTION_ARG,
.opt.arg = &cfg.keyfile,
},
{
.name = "keyform",
.argname = "format",
.desc = "Private key format (DER or PEM (default))",
.type = OPTION_ARG_FORMAT,
.opt.value = &cfg.keyform,
},
{
.name = "keyout",
.argname = "file",
.desc = "Private key output file",
.type = OPTION_ARG,
.opt.arg = &cfg.keyout,
},
{
.name = "modulus",
.desc = "Print RSA modulus",
.type = OPTION_FLAG,
.opt.flag = &cfg.modulus,
},
{
.name = "multivalue-rdn",
.desc = "Enable support for multivalued RDNs",
.type = OPTION_FLAG,
.opt.flag = &cfg.multirdn,
},
{
.name = "nameopt",
.argname = "arg",
.desc = "Certificate name options",
.type = OPTION_ARG_FUNC,
.opt.argfunc = req_opt_nameopt,
},
{
.name = "new",
.desc = "New request",
.type = OPTION_FLAG,
.opt.flag = &cfg.newreq,
},
{
.name = "newhdr",
.desc = "Include 'NEW' in header lines",
.type = OPTION_FLAG,
.opt.flag = &cfg.newhdr,
},
{
.name = "newkey",
.argname = "param",
.desc = "Generate a new key using given parameters",
.type = OPTION_ARG_FUNC,
.opt.argfunc = req_opt_newkey,
},
{
.name = "nodes",
.desc = "Do not encrypt output private key",
.type = OPTION_FLAG,
.opt.flag = &cfg.nodes,
},
{
.name = "noout",
.desc = "Do not output request",
.type = OPTION_FLAG,
.opt.flag = &cfg.noout,
},
{
.name = "out",
.argname = "file",
.desc = "Output file (default stdout)",
.type = OPTION_ARG,
.opt.arg = &cfg.outfile,
},
{
.name = "outform",
.argname = "format",
.desc = "Output format (DER or PEM (default))",
.type = OPTION_ARG_FORMAT,
.opt.value = &cfg.outformat,
},
{
.name = "passin",
.argname = "source",
.desc = "Private key input password source",
.type = OPTION_ARG,
.opt.arg = &cfg.passargin,
},
{
.name = "passout",
.argname = "source",
.desc = "Private key output password source",
.type = OPTION_ARG,
.opt.arg = &cfg.passargout,
},
{
.name = "pkeyopt",
.argname = "opt:val",
.desc = "Set the public key algorithm option opt to val",
.type = OPTION_ARG_FUNC,
.opt.argfunc = req_opt_pkeyopt,
},
{
.name = "pubkey",
.desc = "Output the public key",
.type = OPTION_FLAG,
.opt.flag = &cfg.pubkey,
},
{
.name = "reqexts",
.argname = "section",
.desc = "Config section to use for request extensions",
.type = OPTION_ARG,
.opt.arg = &cfg.req_exts,
},
{
.name = "reqopt",
.argname = "option",
.desc = "Request text options",
.type = OPTION_ARG_FUNC,
.opt.argfunc = req_opt_reqopt,
},
{
.name = "set_serial",
.argname = "serial",
.desc = "Serial number to use for generated certificate",
.type = OPTION_ARG_FUNC,
.opt.argfunc = req_opt_set_serial,
},
{
.name = "sigopt",
.argname = "name:val",
.desc = "Signature options",
.type = OPTION_ARG_FUNC,
.opt.argfunc = req_opt_sigopt,
},
{
.name = "subj",
.argname = "name",
.desc = "Set or modify the request subject",
.type = OPTION_ARG,
.opt.arg = &cfg.subj,
},
{
.name = "subject",
.desc = "Output the subject of the request",
.type = OPTION_FLAG,
.opt.flag = &cfg.subject,
},
{
.name = "text",
.desc = "Print request in text form",
.type = OPTION_FLAG,
.opt.flag = &cfg.text,
},
{
.name = "utf8",
.desc = "Input characters are in UTF-8 (default ASCII)",
.type = OPTION_FUNC,
.opt.func = req_opt_utf8,
},
{
.name = "verbose",
.desc = "Verbose",
.type = OPTION_FLAG,
.opt.flag = &cfg.verbose,
},
{
.name = "verify",
.desc = "Verify signature on request",
.type = OPTION_FLAG,
.opt.flag = &cfg.verify,
},
{
.name = "x509",
.desc = "Output an X.509 structure instead of a certificate request",
.type = OPTION_FLAG,
.opt.flag = &cfg.x509,
},
{
.name = NULL,
.desc = "",
.type = OPTION_ARGV_FUNC,
.opt.argvfunc = req_opt_digest,
},
{ NULL },
};
static void
req_usage(void)
{
fprintf(stderr,
"usage: req [-addext ext] [-batch] [-config file]\n"
" [-days n] [-extensions section] [-in file]\n"
" [-inform der | pem] [-key keyfile] [-keyform der | pem]\n"
" [-keyout file] [-md4 | -md5 | -sha1] [-modulus]\n"
" [-multivalue-rdn] [-nameopt option] [-new] [-newhdr]\n"
" [-newkey arg] [-nodes] [-noout]\n"
" [-out file] [-outform der | pem] [-passin arg]\n"
" [-passout arg] [-pkeyopt opt:value] [-pubkey]\n"
" [-reqexts section] [-reqopt option] [-set_serial n]\n"
" [-sigopt nm:v] [-subj arg] [-subject] [-text] [-utf8]\n"
" [-verbose] [-verify] [-x509]\n\n");
options_usage(req_options);
fprintf(stderr, "\n");
}
int
req_main(int argc, char **argv)
{
int ex = 1;
X509 *x509ss = NULL;
X509_REQ *req = NULL;
EVP_PKEY_CTX *genctx = NULL;
char *keyalgstr = NULL;
const EVP_CIPHER *cipher = NULL;
EVP_PKEY *pkey = NULL;
int i = 0, pkey_type = -1;
BIO *in = NULL, *out = NULL;
char *passin = NULL, *passout = NULL;
const EVP_MD *md_alg = NULL;
char *p;
if (pledge("stdio cpath wpath rpath tty", NULL) == -1) {
perror("pledge");
exit(1);
}
memset(&cfg, 0, sizeof(cfg));
cfg.chtype = MBSTRING_ASC;
cfg.days = 30;
cfg.digest = EVP_sha256();
cfg.newkey = -1;
cfg.informat = FORMAT_PEM;
cfg.keyform = FORMAT_PEM;
cfg.outformat = FORMAT_PEM;
if (options_parse(argc, argv, req_options, NULL, NULL) != 0) {
req_usage();
return (1);
}
req_conf = NULL;
cipher = EVP_aes_256_cbc();
if (!app_passwd(bio_err, cfg.passargin, cfg.passargout, &passin, &passout)) {
BIO_printf(bio_err, "Error getting passwords\n");
goto end;
}
if (cfg.template != NULL) {
long errline = -1;
if (cfg.verbose)
BIO_printf(bio_err, "Using configuration from %s\n", cfg.template);
if ((req_conf = NCONF_new(NULL)) == NULL)
goto end;
if(!NCONF_load(req_conf, cfg.template, &errline)) {
BIO_printf(bio_err, "error on line %ld of %s\n", errline, cfg.template);
goto end;
}
} else {
req_conf = config;
if (req_conf == NULL) {
BIO_printf(bio_err, "Unable to load config info from %s\n", default_config_file);
if (cfg.newreq)
goto end;
} else if (cfg.verbose)
BIO_printf(bio_err, "Using configuration from %s\n",
default_config_file);
}
if (cfg.addext_bio != NULL) {
long errline = -1;
if (cfg.verbose)
BIO_printf(bio_err,
"Using additional configuration from command line\n");
if ((addext_conf = NCONF_new(NULL)) == NULL)
goto end;
if (!NCONF_load_bio(addext_conf, cfg.addext_bio, &errline)) {
BIO_printf(bio_err,
"req: Error on line %ld of config input\n",
errline);
goto end;
}
}
if (req_conf != NULL) {
if (!load_config(bio_err, req_conf))
goto end;
p = NCONF_get_string(req_conf, NULL, "oid_file");
if (p == NULL)
ERR_clear_error();
if (p != NULL) {
BIO *oid_bio;
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);
*/
} else {
OBJ_create_objects(oid_bio);
BIO_free(oid_bio);
}
}
}
if (!add_oid_section(bio_err, req_conf))
goto end;
if (md_alg == NULL) {
p = NCONF_get_string(req_conf, SECTION, "default_md");
if (p == NULL)
ERR_clear_error();
if (p != NULL) {
if ((md_alg = EVP_get_digestbyname(p)) != NULL)
cfg.digest = md_alg;
}
}
if (!cfg.extensions) {
cfg.extensions = NCONF_get_string(req_conf, SECTION, V3_EXTENSIONS);
if (!cfg.extensions)
ERR_clear_error();
}
if (cfg.extensions) {
/* 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, cfg.extensions, NULL)) {
BIO_printf(bio_err,
"Error Loading extension section %s\n", cfg.extensions);
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 Loading command line extensions\n");
goto end;
}
}
if (!passin) {
passin = NCONF_get_string(req_conf, SECTION, "input_password");
if (!passin)
ERR_clear_error();
}
if (!passout) {
passout = NCONF_get_string(req_conf, SECTION, "output_password");
if (!passout)
ERR_clear_error();
}
p = NCONF_get_string(req_conf, SECTION, STRING_MASK);
if (!p)
ERR_clear_error();
if (p && !ASN1_STRING_set_default_mask_asc(p)) {
BIO_printf(bio_err, "Invalid global string mask setting %s\n", p);
goto end;
}
if (cfg.chtype != MBSTRING_UTF8) {
p = NCONF_get_string(req_conf, SECTION, UTF8_IN);
if (!p)
ERR_clear_error();
else if (!strcmp(p, "yes"))
cfg.chtype = MBSTRING_UTF8;
}
if (!cfg.req_exts) {
cfg.req_exts = NCONF_get_string(req_conf, SECTION, REQ_EXTENSIONS);
if (!cfg.req_exts)
ERR_clear_error();
}
if (cfg.req_exts) {
/* 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, cfg.req_exts, NULL)) {
BIO_printf(bio_err,
"Error Loading request extension section %s\n",
cfg.req_exts);
goto end;
}
}
in = BIO_new(BIO_s_file());
out = BIO_new(BIO_s_file());
if ((in == NULL) || (out == NULL))
goto end;
if (cfg.keyfile != NULL) {
pkey = load_key(bio_err, cfg.keyfile, cfg.keyform, 0, passin,
"Private Key");
if (!pkey) {
/*
* load_key() has already printed an appropriate
* message
*/
goto end;
}
}
if (cfg.newreq && (pkey == NULL)) {
if (!NCONF_get_number(req_conf, SECTION, BITS, &cfg.newkey)) {
cfg.newkey = DEFAULT_KEY_LENGTH;
}
if (cfg.keyalg) {
genctx = set_keygen_ctx(bio_err, cfg.keyalg, &pkey_type, &cfg.newkey,
&keyalgstr);
if (!genctx)
goto end;
}
if (cfg.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, cfg.newkey);
goto end;
}
if (!genctx) {
genctx = set_keygen_ctx(bio_err, NULL, &pkey_type, &cfg.newkey,
&keyalgstr);
if (!genctx)
goto end;
}
if (cfg.pkeyopts) {
char *genopt;
for (i = 0; i < sk_OPENSSL_STRING_num(cfg.pkeyopts); i++) {
genopt = sk_OPENSSL_STRING_value(cfg.pkeyopts, i);
if (pkey_ctrl_string(genctx, genopt) <= 0) {
BIO_printf(bio_err,
"parameter error \"%s\"\n",
genopt);
ERR_print_errors(bio_err);
goto end;
}
}
}
BIO_printf(bio_err, "Generating a %ld bit %s private key\n",
cfg.newkey, keyalgstr);
EVP_PKEY_CTX_set_cb(genctx, genpkey_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;
}
EVP_PKEY_CTX_free(genctx);
genctx = NULL;
if (cfg.keyout == NULL) {
cfg.keyout = NCONF_get_string(req_conf, SECTION, KEYFILE);
if (cfg.keyout == NULL)
ERR_clear_error();
}
if (cfg.keyout == NULL) {
BIO_printf(bio_err, "writing new private key to stdout\n");
BIO_set_fp(out, stdout, BIO_NOCLOSE);
} else {
BIO_printf(bio_err, "writing new private key to '%s'\n", cfg.keyout);
if (BIO_write_filename(out, cfg.keyout) <= 0) {
perror(cfg.keyout);
goto end;
}
}
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");
if (p == NULL)
ERR_clear_error();
}
if ((p != NULL) && (strcmp(p, "no") == 0))
cipher = NULL;
if (cfg.nodes)
cipher = NULL;
i = 0;
loop:
if (!PEM_write_bio_PrivateKey(out, pkey, cipher,
NULL, 0, NULL, passout)) {
if ((ERR_GET_REASON(ERR_peek_error()) ==
PEM_R_PROBLEMS_GETTING_PASSWORD) && (i < 3)) {
ERR_clear_error();
i++;
goto loop;
}
goto end;
}
BIO_printf(bio_err, "-----\n");
}
if (!cfg.newreq) {
if (cfg.infile == NULL)
BIO_set_fp(in, stdin, BIO_NOCLOSE);
else {
if (BIO_read_filename(in, cfg.infile) <= 0) {
perror(cfg.infile);
goto end;
}
}
if (cfg.informat == FORMAT_ASN1)
req = d2i_X509_REQ_bio(in, NULL);
else if (cfg.informat == FORMAT_PEM)
req = PEM_read_bio_X509_REQ(in, NULL, NULL, NULL);
else {
BIO_printf(bio_err, "bad input format specified for X509 request\n");
goto end;
}
if (req == NULL) {
BIO_printf(bio_err, "unable to load X509 request\n");
goto end;
}
}
if (cfg.newreq || cfg.x509) {
if (pkey == NULL) {
BIO_printf(bio_err, "you need to specify a private key\n");
goto end;
}
if (req == NULL) {
req = X509_REQ_new();
if (req == NULL) {
goto end;
}
i = make_REQ(req, pkey, cfg.subj, cfg.multirdn, !cfg.x509, cfg.chtype);
cfg.subj = NULL; /* done processing '-subj' option */
if (!i) {
BIO_printf(bio_err, "problems making Certificate Request\n");
goto end;
}
}
if (cfg.x509) {
EVP_PKEY *tmppkey;
X509V3_CTX ext_ctx;
if ((x509ss = X509_new()) == NULL)
goto end;
/* Set version to V3 */
if ((cfg.extensions != NULL || addext_conf != NULL) &&
!X509_set_version(x509ss, 2))
goto end;
if (cfg.serial) {
if (!X509_set_serialNumber(x509ss, cfg.serial))
goto end;
} else {
if (!rand_serial(NULL,
X509_get_serialNumber(x509ss)))
goto end;
}
if (!X509_set_issuer_name(x509ss, X509_REQ_get_subject_name(req)))
goto end;
if (!X509_gmtime_adj(X509_get_notBefore(x509ss), 0))
goto end;
if (!X509_time_adj_ex(X509_get_notAfter(x509ss), cfg.days, 0, NULL))
goto end;
if (!X509_set_subject_name(x509ss, X509_REQ_get_subject_name(req)))
goto end;
if ((tmppkey = X509_REQ_get0_pubkey(req)) == NULL)
goto end;
if (!X509_set_pubkey(x509ss, tmppkey))
goto end;
/* Set up V3 context struct */
X509V3_set_ctx(&ext_ctx, x509ss, x509ss, NULL, NULL, 0);
X509V3_set_nconf(&ext_ctx, req_conf);
/* Add extensions */
if (cfg.extensions && !X509V3_EXT_add_nconf(req_conf,
&ext_ctx, cfg.extensions, x509ss)) {
BIO_printf(bio_err,
"Error Loading extension section %s\n",
cfg.extensions);
goto end;
}
if (addext_conf != NULL &&
!X509V3_EXT_add_nconf(addext_conf, &ext_ctx,
"default", x509ss)) {
BIO_printf(bio_err,
"Error Loading command line extensions\n");
goto end;
}
i = do_X509_sign(bio_err, x509ss, pkey, cfg.digest, cfg.sigopts);
if (!i) {
ERR_print_errors(bio_err);
goto end;
}
} else {
X509V3_CTX ext_ctx;
/* Set up V3 context struct */
X509V3_set_ctx(&ext_ctx, NULL, NULL, req, NULL, 0);
X509V3_set_nconf(&ext_ctx, req_conf);
/* Add extensions */
if (cfg.req_exts && !X509V3_EXT_REQ_add_nconf(req_conf,
&ext_ctx, cfg.req_exts, req)) {
BIO_printf(bio_err,
"Error Loading extension section %s\n",
cfg.req_exts);
goto end;
}
if (addext_conf != NULL &&
!X509V3_EXT_REQ_add_nconf(addext_conf, &ext_ctx,
"default", req)) {
BIO_printf(bio_err,
"Error Loading command line extensions\n");
goto end;
}
i = do_X509_REQ_sign(bio_err, req, pkey, cfg.digest, cfg.sigopts);
if (!i) {
ERR_print_errors(bio_err);
goto end;
}
}
}
if (cfg.subj && cfg.x509) {
BIO_printf(bio_err, "Cannot modify certificate subject\n");
goto end;
}
if (cfg.subj && !cfg.x509) {
if (cfg.verbose) {
BIO_printf(bio_err, "Modifying Request's Subject\n");
print_name(bio_err, "old subject=", X509_REQ_get_subject_name(req), cfg.nmflag);
}
if (build_subject(req, cfg.subj, cfg.chtype, cfg.multirdn) == 0) {
BIO_printf(bio_err, "ERROR: cannot modify subject\n");
ex = 1;
goto end;
}
if (cfg.verbose) {
print_name(bio_err, "new subject=", X509_REQ_get_subject_name(req), cfg.nmflag);
}
}
if (cfg.verify && !cfg.x509) {
EVP_PKEY *pubkey = pkey;
if (pubkey == NULL)
pubkey = X509_REQ_get0_pubkey(req);
if (pubkey == NULL)
goto end;
i = X509_REQ_verify(req, pubkey);
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 (cfg.noout && !cfg.text && !cfg.modulus && !cfg.subject && !cfg.pubkey) {
ex = 0;
goto end;
}
if (cfg.outfile == NULL) {
BIO_set_fp(out, stdout, BIO_NOCLOSE);
} else {
if ((cfg.keyout != NULL) && (strcmp(cfg.outfile, cfg.keyout) == 0))
i = (int) BIO_append_filename(out, cfg.outfile);
else
i = (int) BIO_write_filename(out, cfg.outfile);
if (!i) {
perror(cfg.outfile);
goto end;
}
}
if (cfg.pubkey) {
EVP_PKEY *tpubkey;
if ((tpubkey = X509_REQ_get0_pubkey(req)) == NULL) {
BIO_printf(bio_err, "Error getting public key\n");
ERR_print_errors(bio_err);
goto end;
}
PEM_write_bio_PUBKEY(out, tpubkey);
}
if (cfg.text) {
if (cfg.x509)
X509_print_ex(out, x509ss, cfg.nmflag, cfg.reqflag);
else
X509_REQ_print_ex(out, req, cfg.nmflag, cfg.reqflag);
}
if (cfg.subject) {
if (cfg.x509)
print_name(out, "subject=", X509_get_subject_name(x509ss), cfg.nmflag);
else
print_name(out, "subject=", X509_REQ_get_subject_name(req), cfg.nmflag);
}
if (cfg.modulus) {
EVP_PKEY *tpubkey;
if (cfg.x509)
tpubkey = X509_get0_pubkey(x509ss);
else
tpubkey = X509_REQ_get0_pubkey(req);
if (tpubkey == NULL) {
fprintf(stdout, "Modulus=unavailable\n");
goto end;
}
fprintf(stdout, "Modulus=");
if (EVP_PKEY_base_id(tpubkey) == EVP_PKEY_RSA) {
const BIGNUM *n = NULL;
RSA_get0_key(EVP_PKEY_get0_RSA(tpubkey), &n, NULL, NULL);
BN_print(out, n);
} else
fprintf(stdout, "Wrong Algorithm type");
fprintf(stdout, "\n");
}
if (!cfg.noout && !cfg.x509) {
if (cfg.outformat == FORMAT_ASN1)
i = i2d_X509_REQ_bio(out, req);
else if (cfg.outformat == FORMAT_PEM) {
if (cfg.newhdr)
i = PEM_write_bio_X509_REQ_NEW(out, req);
else
i = PEM_write_bio_X509_REQ(out, req);
} else {
BIO_printf(bio_err, "bad output format specified for outfile\n");
goto end;
}
if (!i) {
BIO_printf(bio_err, "unable to write X509 request\n");
goto end;
}
}
if (!cfg.noout && cfg.x509 && (x509ss != NULL)) {
if (cfg.outformat == FORMAT_ASN1)
i = i2d_X509_bio(out, x509ss);
else if (cfg.outformat == FORMAT_PEM)
i = PEM_write_bio_X509(out, x509ss);
else {
BIO_printf(bio_err, "bad output format specified for outfile\n");
goto end;
}
if (!i) {
BIO_printf(bio_err, "unable to write X509 certificate\n");
goto end;
}
}
ex = 0;
end:
if (ex) {
ERR_print_errors(bio_err);
}
if ((req_conf != NULL) && (req_conf != config))
NCONF_free(req_conf);
NCONF_free(addext_conf);
BIO_free(cfg.addext_bio);
BIO_free(in);
BIO_free_all(out);
EVP_PKEY_free(pkey);
if (genctx)
EVP_PKEY_CTX_free(genctx);
if (cfg.pkeyopts)
sk_OPENSSL_STRING_free(cfg.pkeyopts);
if (cfg.sigopts)
sk_OPENSSL_STRING_free(cfg.sigopts);
lh_OPENSSL_STRING_doall(cfg.addexts, (LHASH_DOALL_FN_TYPE)exts_cleanup);
lh_OPENSSL_STRING_free(cfg.addexts);
free(keyalgstr);
X509_REQ_free(req);
X509_free(x509ss);
ASN1_INTEGER_free(cfg.serial);
if (cfg.passargin && passin)
free(passin);
if (cfg.passargout && passout)
free(passout);
OBJ_cleanup();
return (ex);
}
static int
make_REQ(X509_REQ * req, EVP_PKEY * pkey, char *subj, int multirdn,
int attribs, unsigned long chtype)
{
int ret = 0, i;
char no_prompt = 0;
STACK_OF(CONF_VALUE) * dn_sk, *attr_sk = NULL;
char *tmp, *dn_sect, *attr_sect;
tmp = NCONF_get_string(req_conf, SECTION, PROMPT);
if (tmp == NULL)
ERR_clear_error();
if ((tmp != NULL) && !strcmp(tmp, "no"))
no_prompt = 1;
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;
}
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);
goto err;
}
}
/* setup version number */
if (!X509_REQ_set_version(req, 0L))
goto err; /* version 1 */
if (no_prompt)
i = auto_info(req, dn_sk, attr_sk, attribs, chtype);
else {
if (subj)
i = build_subject(req, subj, chtype, multirdn);
else
i = prompt_info(req, dn_sk, dn_sect, attr_sk, attr_sect, attribs, chtype);
}
if (!i)
goto err;
if (!X509_REQ_set_pubkey(req, pkey))
goto err;
ret = 1;
err:
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, char *subject, unsigned long chtype, int multirdn)
{
X509_NAME *n;
if (!(n = parse_name(subject, chtype, multirdn)))
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, char *dn_sect,
STACK_OF(CONF_VALUE) * attr_sk, char *attr_sect, int attribs,
unsigned long chtype)
{
int i;
char *p, *q;
char buf[100];
int nid, mval;
long n_min, n_max;
char *type, *value;
const char *def;
CONF_VALUE *v;
X509_NAME *subj;
subj = X509_REQ_get_subject_name(req);
if (!cfg.batch) {
BIO_printf(bio_err, "You are about to be asked to enter information that will be incorporated\n");
BIO_printf(bio_err, "into your certificate request.\n");
BIO_printf(bio_err, "What you are about to enter is what is called a Distinguished Name or a DN.\n");
BIO_printf(bio_err, "There are quite a few fields but you can leave some blank\n");
BIO_printf(bio_err, "For some fields there will be a default value,\n");
BIO_printf(bio_err, "If you enter '.', the field will be left blank.\n");
BIO_printf(bio_err, "-----\n");
}
if (sk_CONF_VALUE_num(dn_sk)) {
i = -1;
start: for (;;) {
int ret;
i++;
if (sk_CONF_VALUE_num(dn_sk) <= i)
break;
v = sk_CONF_VALUE_value(dn_sk, i);
p = q = NULL;
type = v->name;
if (!check_end(type, "_min") || !check_end(type, "_max") ||
!check_end(type, "_default") ||
!check_end(type, "_value"))
continue;
/*
* Skip past any leading X. X: X, etc to allow for
* multiple instances
*/
for (p = v->name; *p; p++)
if ((*p == ':') || (*p == ',') ||
(*p == '.')) {
p++;
if (*p)
type = p;
break;
}
if (*type == '+') {
mval = -1;
type++;
} else
mval = 0;
/* If OBJ not recognised ignore it */
if ((nid = OBJ_txt2nid(type)) == NID_undef)
goto start;
ret = snprintf(buf, sizeof buf, "%s_default", v->name);
if (ret < 0 || ret >= sizeof(buf)) {
BIO_printf(bio_err, "Name '%s' too long for default\n",
v->name);
return 0;
}
if ((def = NCONF_get_string(req_conf, dn_sect, buf)) == NULL) {
ERR_clear_error();
def = "";
}
ret = snprintf(buf, sizeof buf, "%s_value", v->name);
if (ret < 0 || ret >= sizeof(buf)) {
BIO_printf(bio_err, "Name '%s' too long for value\n",
v->name);
return 0;
}
if ((value = NCONF_get_string(req_conf, dn_sect, buf)) == NULL) {
ERR_clear_error();
value = NULL;
}
ret = snprintf(buf, sizeof buf, "%s_min", v->name);
if (ret < 0 || ret >= sizeof(buf)) {
BIO_printf(bio_err, "Name '%s' too long for min\n",
v->name);
return 0;
}
if (!NCONF_get_number(req_conf, dn_sect, buf, &n_min)) {
ERR_clear_error();
n_min = -1;
}
ret = snprintf(buf, sizeof buf, "%s_max", v->name);
if (ret < 0 || ret >= sizeof(buf)) {
BIO_printf(bio_err, "Name '%s' too long for max\n",
v->name);
return 0;
}
if (!NCONF_get_number(req_conf, dn_sect, buf, &n_max)) {
ERR_clear_error();
n_max = -1;
}
if (!add_DN_object(subj, v->value, def, value, nid,
n_min, n_max, chtype, mval))
return 0;
}
if (X509_NAME_entry_count(subj) == 0) {
BIO_printf(bio_err, "error, no objects specified in config file\n");
return 0;
}
if (attribs) {
if ((attr_sk != NULL) && (sk_CONF_VALUE_num(attr_sk) > 0) &&
(!cfg.batch)) {
BIO_printf(bio_err,
"\nPlease enter the following 'extra' attributes\n");
BIO_printf(bio_err,
"to be sent with your certificate request\n");
}
i = -1;
start2: for (;;) {
int ret;
i++;
if ((attr_sk == NULL) ||
(sk_CONF_VALUE_num(attr_sk) <= i))
break;
v = sk_CONF_VALUE_value(attr_sk, i);
type = v->name;
if ((nid = OBJ_txt2nid(type)) == NID_undef)
goto start2;
ret = snprintf(buf, sizeof buf, "%s_default", type);
if (ret < 0 || ret >= sizeof(buf)) {
BIO_printf(bio_err, "Name '%s' too long for default\n",
v->name);
return 0;
}
if ((def = NCONF_get_string(req_conf, attr_sect, buf))
== NULL) {
ERR_clear_error();
def = "";
}
ret = snprintf(buf, sizeof buf, "%s_value", type);
if (ret < 0 || ret >= sizeof(buf)) {
BIO_printf(bio_err, "Name '%s' too long for value\n",
v->name);
return 0;
}
if ((value = NCONF_get_string(req_conf, attr_sect, buf))
== NULL) {
ERR_clear_error();
value = NULL;
}
ret = snprintf(buf, sizeof buf, "%s_min", type);
if (ret < 0 || ret >= sizeof(buf)) {
BIO_printf(bio_err, "Name '%s' too long for min\n",
v->name);
return 0;
}
if (!NCONF_get_number(req_conf, attr_sect, buf, &n_min)) {
ERR_clear_error();
n_min = -1;
}
ret = snprintf(buf, sizeof buf, "%s_max", type);
if (ret < 0 || ret >= sizeof(buf)) {
BIO_printf(bio_err, "Name '%s' too long for max\n",
v->name);
return 0;
}
if (!NCONF_get_number(req_conf, attr_sect, buf, &n_max)) {
ERR_clear_error();
n_max = -1;
}
if (!add_attribute_object(req,
v->value, def, value, nid, n_min, n_max, chtype))
return 0;
}
}
} else {
BIO_printf(bio_err, "No template, please set one up.\n");
return 0;
}
return 1;
}
static int
auto_info(X509_REQ * req, STACK_OF(CONF_VALUE) * dn_sk,
STACK_OF(CONF_VALUE) * attr_sk, int attribs, unsigned long chtype)
{
int i;
char *p, *q;
char *type;
CONF_VALUE *v;
X509_NAME *subj;
subj = X509_REQ_get_subject_name(req);
for (i = 0; i < sk_CONF_VALUE_num(dn_sk); i++) {
int mval;
v = sk_CONF_VALUE_value(dn_sk, i);
p = q = NULL;
type = v->name;
/*
* Skip past any leading X. X: X, etc to allow for multiple
* instances
*/
for (p = v->name; *p; p++)
if ((*p == ':') || (*p == ',') || (*p == '.')) {
p++;
if (*p)
type = p;
break;
}
if (*p == '+') {
p++;
mval = -1;
} else
mval = 0;
if (!X509_NAME_add_entry_by_txt(subj, type, chtype,
(unsigned char *) v->value, -1, -1, mval))
return 0;
}
if (!X509_NAME_entry_count(subj)) {
BIO_printf(bio_err, "error, no objects specified in config file\n");
return 0;
}
if (attribs) {
for (i = 0; i < sk_CONF_VALUE_num(attr_sk); i++) {
v = sk_CONF_VALUE_value(attr_sk, i);
if (!X509_REQ_add1_attr_by_txt(req, v->name, chtype,
(unsigned char *) v->value, -1))
return 0;
}
}
return 1;
}
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;
char buf[1024];
start:
if (!cfg.batch)
BIO_printf(bio_err, "%s [%s]:", text, def);
(void) BIO_flush(bio_err);
if (value != NULL) {
strlcpy(buf, value, sizeof buf);
strlcat(buf, "\n", sizeof buf);
BIO_printf(bio_err, "%s\n", value);
} else {
buf[0] = '\0';
if (!cfg.batch) {
if (!fgets(buf, sizeof buf, stdin))
return 0;
} else {
buf[0] = '\n';
buf[1] = '\0';
}
}
if (buf[0] == '\0')
return (0);
else if (buf[0] == '\n') {
if ((def == NULL) || (def[0] == '\0'))
return (1);
strlcpy(buf, def, sizeof buf);
strlcat(buf, "\n", sizeof buf);
} 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';
if (!req_check_len(i, n_min, n_max))
goto start;
if (!X509_NAME_add_entry_by_NID(n, nid, chtype,
(unsigned char *) buf, -1, -1, mval))
goto err;
ret = 1;
err:
return (ret);
}
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];
start:
if (!cfg.batch)
BIO_printf(bio_err, "%s [%s]:", text, def);
(void) BIO_flush(bio_err);
if (value != NULL) {
strlcpy(buf, value, sizeof buf);
strlcat(buf, "\n", sizeof buf);
BIO_printf(bio_err, "%s\n", value);
} else {
buf[0] = '\0';
if (!cfg.batch) {
if (!fgets(buf, sizeof buf, stdin))
return 0;
} else {
buf[0] = '\n';
buf[1] = '\0';
}
}
if (buf[0] == '\0')
return (0);
else if (buf[0] == '\n') {
if ((def == NULL) || (def[0] == '\0'))
return (1);
strlcpy(buf, def, sizeof buf);
strlcat(buf, "\n", sizeof buf);
} 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';
if (!req_check_len(i, n_min, n_max))
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);
}
static int
req_check_len(int len, int n_min, int n_max)
{
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);
return (0);
}
if ((n_max >= 0) && (len > n_max)) {
BIO_printf(bio_err, "string is too long, it needs to be less than %d bytes long\n", n_max);
return (0);
}
return (1);
}
/* Check if the end of a string matches 'end' */
static int
check_end(const char *str, const char *end)
{
int elen, slen;
const char *tmp;
elen = strlen(end);
slen = strlen(str);
if (elen > slen)
return 1;
tmp = str + slen - elen;
return strcmp(tmp, end);
}
static EVP_PKEY_CTX *
set_keygen_ctx(BIO * err, const char *gstr, int *pkey_type,
long *pkeylen, char **palgnam)
{
EVP_PKEY_CTX *gctx = NULL;
EVP_PKEY *param = NULL;
long keylen = -1;
BIO *pbio = NULL;
const char *paramfile = NULL;
const char *errstr;
if (gstr == NULL) {
*pkey_type = EVP_PKEY_RSA;
keylen = *pkeylen;
} else if (gstr[0] >= '0' && gstr[0] <= '9') {
*pkey_type = EVP_PKEY_RSA;
keylen = strtonum(gstr, 0, LONG_MAX, &errstr);
if (errstr) {
BIO_printf(err, "bad algorithm %s: %s\n", gstr, errstr);
return NULL;
}
*pkeylen = keylen;
} else if (!strncmp(gstr, "param:", 6))
paramfile = gstr + 6;
else {
const char *p = strchr(gstr, ':');
int len;
const EVP_PKEY_ASN1_METHOD *ameth;
if (p)
len = p - gstr;
else
len = strlen(gstr);
ameth = EVP_PKEY_asn1_find_str(NULL, gstr, len);
if (!ameth) {
BIO_printf(err, "Unknown algorithm %.*s\n", len, gstr);
return NULL;
}
EVP_PKEY_asn1_get0_info(NULL, pkey_type, NULL, NULL, NULL,
ameth);
if (*pkey_type == EVP_PKEY_RSA) {
if (p) {
keylen = strtonum(p + 1, 0, LONG_MAX, &errstr);
if (errstr) {
BIO_printf(err, "bad algorithm %s: %s\n",
p + 1, errstr);
return NULL;
}
*pkeylen = keylen;
} else
keylen = *pkeylen;
} else if (p)
paramfile = p + 1;
}
if (paramfile) {
pbio = BIO_new_file(paramfile, "r");
if (!pbio) {
BIO_printf(err, "Can't open parameter file %s\n",
paramfile);
return NULL;
}
param = PEM_read_bio_Parameters(pbio, NULL);
if (!param) {
X509 *x;
(void) BIO_reset(pbio);
x = PEM_read_bio_X509(pbio, NULL, NULL, NULL);
if (x) {
param = X509_get_pubkey(x);
X509_free(x);
}
}
BIO_free(pbio);
if (!param) {
BIO_printf(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(err, "Key Type does not match parameters\n");
EVP_PKEY_free(param);
return NULL;
}
}
if (palgnam) {
const EVP_PKEY_ASN1_METHOD *ameth;
const char *anam;
ameth = EVP_PKEY_asn1_find(NULL, *pkey_type);
if (!ameth) {
BIO_puts(err, "Internal error: can't find key algorithm\n");
return NULL;
}
EVP_PKEY_asn1_get0_info(NULL, NULL, NULL, NULL, &anam, ameth);
*palgnam = strdup(anam);
}
if (param) {
gctx = EVP_PKEY_CTX_new(param, NULL);
*pkeylen = EVP_PKEY_bits(param);
EVP_PKEY_free(param);
} else
gctx = EVP_PKEY_CTX_new_id(*pkey_type, NULL);
if (!gctx) {
BIO_puts(err, "Error allocating keygen context\n");
ERR_print_errors(err);
return NULL;
}
if (EVP_PKEY_keygen_init(gctx) <= 0) {
BIO_puts(err, "Error initializing keygen context\n");
ERR_print_errors(err);
EVP_PKEY_CTX_free(gctx);
return NULL;
}
if ((*pkey_type == EVP_PKEY_RSA) && (keylen != -1)) {
if (EVP_PKEY_CTX_set_rsa_keygen_bits(gctx, keylen) <= 0) {
BIO_puts(err, "Error setting RSA keysize\n");
ERR_print_errors(err);
EVP_PKEY_CTX_free(gctx);
return NULL;
}
}
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(BIO * err, EVP_MD_CTX * ctx, EVP_PKEY * pkey,
const EVP_MD * md, STACK_OF(OPENSSL_STRING) * sigopts)
{
EVP_PKEY_CTX *pkctx = NULL;
int default_nid;
int i;
if (EVP_PKEY_get_default_digest_nid(pkey, &default_nid) == 2 &&
default_nid == NID_undef) {
/* The digest is required to be EVP_md_null() (EdDSA). */
md = EVP_md_null();
}
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(err, "parameter error \"%s\"\n", sigopt);
ERR_print_errors(bio_err);
return 0;
}
}
return 1;
}
int
do_X509_sign(BIO * err, X509 * x, EVP_PKEY * pkey, const EVP_MD * md,
STACK_OF(OPENSSL_STRING) * sigopts)
{
EVP_MD_CTX *mctx;
int rv;
if ((mctx = EVP_MD_CTX_new()) == NULL)
return 0;
rv = do_sign_init(err, mctx, pkey, md, sigopts);
if (rv > 0)
rv = X509_sign_ctx(x, mctx);
EVP_MD_CTX_free(mctx);
return rv > 0;
}
int
do_X509_REQ_sign(BIO * err, X509_REQ * x, EVP_PKEY * pkey, const EVP_MD * md,
STACK_OF(OPENSSL_STRING) * sigopts)
{
EVP_MD_CTX *mctx;
int rv;
if ((mctx = EVP_MD_CTX_new()) == NULL)
return 0;
rv = do_sign_init(err, mctx, pkey, md, sigopts);
if (rv > 0)
rv = X509_REQ_sign_ctx(x, mctx);
EVP_MD_CTX_free(mctx);
return rv > 0;
}
int
do_X509_CRL_sign(BIO * err, X509_CRL * x, EVP_PKEY * pkey, const EVP_MD * md,
STACK_OF(OPENSSL_STRING) * sigopts)
{
int rv;
EVP_MD_CTX *mctx;
if ((mctx = EVP_MD_CTX_new()) == NULL)
return 0;
rv = do_sign_init(err, mctx, pkey, md, sigopts);
if (rv > 0)
rv = X509_CRL_sign_ctx(x, mctx);
EVP_MD_CTX_free(mctx);
return rv > 0;
}
static unsigned long
ext_name_hash(const OPENSSL_STRING *a)
{
return 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)
{
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 = 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) {
free(kv);
return 1;
}
*p = '\0';
/* See if "key" is there by attempting to add it. */
if ((p = (char *)lh_OPENSSL_STRING_insert(addexts, (OPENSSL_STRING*)kv))
!= NULL || lh_OPENSSL_STRING_error(addexts)) {
free(p != NULL ? p : kv);
return -1;
}
return 0;
}