/*
- * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2018 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
#include <stdlib.h>
#include <string.h>
#include "apps.h"
+#include "progs.h"
#include <openssl/bio.h>
#include <openssl/asn1.h>
#include <openssl/err.h>
#define DEF_DAYS 30
static int callb(int ok, X509_STORE_CTX *ctx);
-static int sign(X509 *x, EVP_PKEY *pkey, int days, int clrext,
+static int sign(X509 *x, EVP_PKEY *pkey, EVP_PKEY *fkey, int days, int clrext,
const EVP_MD *digest, CONF *conf, const char *section,
int preserve_dates);
static int x509_certify(X509_STORE *ctx, const char *CAfile, const EVP_MD *digest,
const char *section, ASN1_INTEGER *sno, int reqfile,
int preserve_dates);
static int purpose_print(BIO *bio, X509 *cert, X509_PURPOSE *pt);
+static int print_x509v3_exts(BIO *bio, X509 *x, const char *exts);
typedef enum OPTION_choice {
OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
OPT_INFORM, OPT_OUTFORM, OPT_KEYFORM, OPT_REQ, OPT_CAFORM,
OPT_CAKEYFORM, OPT_SIGOPT, OPT_DAYS, OPT_PASSIN, OPT_EXTFILE,
- OPT_EXTENSIONS, OPT_IN, OPT_OUT, OPT_SIGNKEY, OPT_CA,
- OPT_CAKEY, OPT_CASERIAL, OPT_SET_SERIAL, OPT_FORCE_PUBKEY,
+ OPT_EXTENSIONS, OPT_IN, OPT_OUT, OPT_SIGNKEY, OPT_CA, OPT_CAKEY,
+ OPT_CASERIAL, OPT_SET_SERIAL, OPT_NEW, OPT_FORCE_PUBKEY, OPT_SUBJ,
OPT_ADDTRUST, OPT_ADDREJECT, OPT_SETALIAS, OPT_CERTOPT, OPT_NAMEOPT,
OPT_C, OPT_EMAIL, OPT_OCSP_URI, OPT_SERIAL, OPT_NEXT_SERIAL,
OPT_MODULUS, OPT_PUBKEY, OPT_X509TOREQ, OPT_TEXT, OPT_HASH,
OPT_SUBJECT_HASH_OLD,
OPT_ISSUER_HASH_OLD,
OPT_BADSIG, OPT_MD, OPT_ENGINE, OPT_NOCERT, OPT_PRESERVE_DATES,
- OPT_R_ENUM
+ OPT_R_ENUM, OPT_EXT
} OPTION_CHOICE;
const OPTIONS x509_options[] = {
{"help", OPT_HELP, '-', "Display this summary"},
{"inform", OPT_INFORM, 'f',
- "Input format - default PEM (one of DER, NET or PEM)"},
+ "Input format - default PEM (one of DER or PEM)"},
{"in", OPT_IN, '<', "Input file - default stdin"},
{"outform", OPT_OUTFORM, 'f',
- "Output format - default PEM (one of DER, NET or PEM)"},
+ "Output format - default PEM (one of DER or PEM)"},
{"out", OPT_OUT, '>', "Output file - default stdout"},
{"keyform", OPT_KEYFORM, 'F', "Private key format - default PEM"},
{"passin", OPT_PASSIN, 's', "Private key password/pass-phrase source"},
{"CAserial", OPT_CASERIAL, 's', "Serial file"},
{"set_serial", OPT_SET_SERIAL, 's', "Serial number to use"},
{"text", OPT_TEXT, '-', "Print the certificate in text form"},
+ {"ext", OPT_EXT, 's', "Print various X509V3 extensions"},
{"C", OPT_C, '-', "Print out C code forms"},
{"extfile", OPT_EXTFILE, '<', "File with X509V3 extensions to add"},
OPT_R_OPTIONS,
{"checkemail", OPT_CHECKEMAIL, 's', "Check certificate matches email"},
{"checkip", OPT_CHECKIP, 's', "Check certificate matches ipaddr"},
{"CAform", OPT_CAFORM, 'F', "CA format - default PEM"},
- {"CAkeyform", OPT_CAKEYFORM, 'F', "CA key format - default PEM"},
+ {"CAkeyform", OPT_CAKEYFORM, 'f', "CA key format - default PEM"},
{"sigopt", OPT_SIGOPT, 's', "Signature parameter in n:v form"},
- {"force_pubkey", OPT_FORCE_PUBKEY, '<', "Force the Key to put inside certificate"},
+ {"new", OPT_NEW, '-', "Generate a certificate from scratch"},
+ {"force_pubkey", OPT_FORCE_PUBKEY, '<', "Force the key to put inside certificate"},
+ {"subj", OPT_SUBJ, 's', "Set or override certificate subject (and issuer)"},
{"next_serial", OPT_NEXT_SERIAL, '-', "Increment current certificate serial number"},
{"clrreject", OPT_CLRREJECT, '-',
"Clears all the prohibited or rejected uses of the certificate"},
BIO *out = NULL;
CONF *extconf = NULL;
EVP_PKEY *Upkey = NULL, *CApkey = NULL, *fkey = NULL;
+ int newcert = 0;
+ char *subj = NULL;
+ X509_NAME *fsubj = NULL;
+ const unsigned long chtype = MBSTRING_ASC;
+ const int multirdn = 0;
STACK_OF(ASN1_OBJECT) *trust = NULL, *reject = NULL;
STACK_OF(OPENSSL_STRING) *sigopts = NULL;
X509 *x = NULL, *xca = NULL;
X509_STORE *ctx = NULL;
const EVP_MD *digest = NULL;
char *CAkeyfile = NULL, *CAserial = NULL, *fkeyfile = NULL, *alias = NULL;
- char *checkhost = NULL, *checkemail = NULL, *checkip = NULL;
+ char *checkhost = NULL, *checkemail = NULL, *checkip = NULL, *exts = NULL;
char *extsect = NULL, *extfile = NULL, *passin = NULL, *passinarg = NULL;
char *infile = NULL, *outfile = NULL, *keyfile = NULL, *CAfile = NULL;
char *prog;
int noout = 0, sign_flag = 0, CA_flag = 0, CA_createserial = 0, email = 0;
int ocsp_uri = 0, trustout = 0, clrtrust = 0, clrreject = 0, aliasout = 0;
int ret = 1, i, num = 0, badsig = 0, clrext = 0, nocert = 0;
- int text = 0, serial = 0, subject = 0, issuer = 0, startdate = 0;
+ int text = 0, serial = 0, subject = 0, issuer = 0, startdate = 0, ext = 0;
int enddate = 0;
time_t checkoffset = 0;
unsigned long certflag = 0;
if ((sno = s2i_ASN1_INTEGER(NULL, opt_arg())) == NULL)
goto opthelp;
break;
+ case OPT_NEW:
+ newcert = 1;
+ break;
case OPT_FORCE_PUBKEY:
fkeyfile = opt_arg();
break;
+ case OPT_SUBJ:
+ subj = opt_arg();
+ break;
case OPT_ADDTRUST:
if ((objtmp = OBJ_txt2obj(opt_arg(), 0)) == NULL) {
BIO_printf(bio_err,
case OPT_NOOUT:
noout = ++num;
break;
+ case OPT_EXT:
+ ext = ++num;
+ exts = opt_arg();
+ break;
case OPT_NOCERT:
nocert = 1;
break;
goto opthelp;
}
- out = bio_open_default(outfile, 'w', outformat);
- if (out == NULL)
- goto end;
-
if (!app_passwd(passinarg, NULL, &passin, NULL)) {
BIO_printf(bio_err, "Error getting password\n");
goto end;
goto end;
}
+ if (newcert && infile != NULL) {
+ BIO_printf(bio_err, "The -in option must not be used since -new is set\n");
+ goto end;
+ }
+ if (newcert && fkeyfile == NULL) {
+ BIO_printf(bio_err,
+ "The -new option requires a public key to be set using -force_pubkey\n");
+ goto end;
+ }
if (fkeyfile != NULL) {
fkey = load_pubkey(fkeyfile, keyformat, 0, NULL, e, "Forced key");
if (fkey == NULL)
goto end;
}
- if ((CAkeyfile == NULL) && (CA_flag) && (CAformat == FORMAT_PEM)) {
+ if (newcert && subj == NULL) {
+ BIO_printf(bio_err,
+ "The -new option requires a subject to be set using -subj\n");
+ goto end;
+ }
+ if (subj != NULL && (fsubj = parse_name(subj, chtype, multirdn)) == NULL)
+ goto end;
+
+ if (CAkeyfile == NULL && CA_flag && CAformat == FORMAT_PEM) {
CAkeyfile = CAfile;
- } else if ((CA_flag) && (CAkeyfile == NULL)) {
+ } else if (CA_flag && CAkeyfile == NULL) {
BIO_printf(bio_err,
"need to specify a CAkey if using the CA command\n");
goto end;
+ } else if (!CA_flag && CAkeyfile != NULL) {
+ BIO_printf(bio_err,
+ "ignoring -CAkey option since no -CA option is given\n");
}
if (extfile != NULL) {
EVP_PKEY *pkey;
BIO *in;
- if (!sign_flag && !CA_flag) {
- BIO_printf(bio_err, "We need a private key to sign with\n");
- goto end;
- }
in = bio_open_default(infile, 'r', informat);
if (in == NULL)
goto end;
}
i = X509_REQ_verify(req, pkey);
if (i < 0) {
- BIO_printf(bio_err, "Signature verification error\n");
+ BIO_printf(bio_err, "Request self-signature verification error\n");
ERR_print_errors(bio_err);
goto end;
}
if (i == 0) {
BIO_printf(bio_err,
- "Signature did not match the certificate request\n");
+ "Request self-signature did not match the certificate request\n");
goto end;
} else {
- BIO_printf(bio_err, "Signature ok\n");
+ BIO_printf(bio_err, "Request self-signature ok\n");
}
print_name(bio_err, "subject=", X509_REQ_get_subject_name(req),
get_nameopt());
+ }
+ if (reqfile || newcert) {
+ X509_NAME *n;
+
+ if (!sign_flag && CAkeyfile == NULL) {
+ BIO_printf(bio_err,
+ "We need a private key to sign with, use -signkey or -CAkey or -CA <file> with private key\n");
+ goto end;
+ }
if ((x = X509_new()) == NULL)
goto end;
goto end;
}
- if (!X509_set_issuer_name(x, X509_REQ_get_subject_name(req)))
- goto end;
- if (!X509_set_subject_name(x, X509_REQ_get_subject_name(req)))
+ n = req == NULL ? fsubj : X509_REQ_get_subject_name(req);
+ if (!X509_set_issuer_name(x, n) || !X509_set_subject_name(x, n))
goto end;
if (!set_cert_times(x, NULL, NULL, days))
goto end;
- if (fkey != NULL) {
- X509_set_pubkey(x, fkey);
- } else {
- pkey = X509_REQ_get0_pubkey(req);
- X509_set_pubkey(x, pkey);
- }
+ if (!X509_set_pubkey(x, fkey != NULL ? fkey : X509_REQ_get0_pubkey(req)))
+ goto end;
} else {
x = load_cert(infile, informat, "Certificate");
+ if (x == NULL)
+ goto end;
+ if (fkey != NULL && !X509_set_pubkey(x, fkey))
+ goto end;
+ if (fsubj != NULL && !X509_set_subject_name(x, fsubj))
+ goto end;
}
- if (x == NULL)
- goto end;
if (CA_flag) {
xca = load_cert(CAfile, CAformat, "CA Certificate");
if (xca == NULL)
goto end;
}
- if (!noout || text || next_serial) {
- OBJ_create("2.99999.3", "SET.ex3", "SET x509v3 extension 3");
+ out = bio_open_default(outfile, 'w', outformat);
+ if (out == NULL)
+ goto end;
- }
+ if (!noout || text || next_serial)
+ OBJ_create("2.99999.3", "SET.ex3", "SET x509v3 extension 3");
if (alias)
X509_alias_set1(x, (unsigned char *)alias, -1);
i2a_ASN1_INTEGER(out, ser);
ASN1_INTEGER_free(ser);
BIO_puts(out, "\n");
- } else if ((email == i) || (ocsp_uri == i)) {
+ } else if (email == i || ocsp_uri == i) {
int j;
STACK_OF(OPENSSL_STRING) *emlst;
if (email == i)
}
/* should be in the library */
- else if ((sign_flag == i) && (x509req == 0)) {
+ else if (sign_flag == i && x509req == 0) {
BIO_printf(bio_err, "Getting Private key\n");
if (Upkey == NULL) {
Upkey = load_key(keyfile, keyformat, 0,
goto end;
}
- if (!sign(x, Upkey, days, clrext, digest, extconf, extsect, preserve_dates))
+ if (!sign(x, Upkey, fkey, days, clrext, digest, extconf,
+ extsect, preserve_dates))
goto end;
} else if (CA_flag == i) {
BIO_printf(bio_err, "Getting CA Private Key\n");
noout = 1;
} else if (ocspid == i) {
X509_ocspid_print(out, x);
+ } else if (ext == i) {
+ print_x509v3_exts(out, x, exts);
}
}
}
NCONF_free(extconf);
BIO_free_all(out);
X509_STORE_free(ctx);
+ X509_NAME_free(fsubj);
X509_REQ_free(req);
X509_free(x);
X509_free(xca);
BIGNUM *serial = NULL;
if (serialfile == NULL) {
- const char *p = strchr(CAfile, '.');
+ const char *p = strrchr(CAfile, '.');
size_t len = p != NULL ? (size_t)(p - CAfile) : strlen(CAfile);
buf = app_malloc(len + sizeof(POSTFIX), "serial# buffer");
}
}
-/* self sign */
-static int sign(X509 *x, EVP_PKEY *pkey, int days, int clrext,
+/* self-issue; self-sign unless a forced public key (fkey) is given */
+static int sign(X509 *x, EVP_PKEY *pkey, EVP_PKEY *fkey, int days, int clrext,
const EVP_MD *digest, CONF *conf, const char *section,
int preserve_dates)
{
goto err;
if (!preserve_dates && !set_cert_times(x, NULL, NULL, days))
goto err;
- if (!X509_set_pubkey(x, pkey))
+ if (fkey == NULL && !X509_set_pubkey(x, pkey))
goto err;
if (clrext) {
while (X509_get_ext_count(x) > 0)
}
return 1;
}
+
+static int parse_ext_names(char *names, const char **result)
+{
+ char *p, *q;
+ int cnt = 0, len = 0;
+
+ p = q = names;
+ len = strlen(names);
+
+ while (q - names <= len) {
+ if (*q != ',' && *q != '\0') {
+ q++;
+ continue;
+ }
+ if (p != q) {
+ /* found */
+ if (result != NULL) {
+ result[cnt] = p;
+ *q = '\0';
+ }
+ cnt++;
+ }
+ p = ++q;
+ }
+
+ return cnt;
+}
+
+static int print_x509v3_exts(BIO *bio, X509 *x, const char *ext_names)
+{
+ const STACK_OF(X509_EXTENSION) *exts = NULL;
+ STACK_OF(X509_EXTENSION) *exts2 = NULL;
+ X509_EXTENSION *ext = NULL;
+ ASN1_OBJECT *obj;
+ int i, j, ret = 0, num, nn = 0;
+ const char *sn, **names = NULL;
+ char *tmp_ext_names = NULL;
+
+ exts = X509_get0_extensions(x);
+ if ((num = sk_X509_EXTENSION_num(exts)) <= 0) {
+ BIO_printf(bio, "No extensions in certificate\n");
+ ret = 1;
+ goto end;
+ }
+
+ /* parse comma separated ext name string */
+ if ((tmp_ext_names = OPENSSL_strdup(ext_names)) == NULL)
+ goto end;
+ if ((nn = parse_ext_names(tmp_ext_names, NULL)) == 0) {
+ BIO_printf(bio, "Invalid extension names: %s\n", ext_names);
+ goto end;
+ }
+ if ((names = OPENSSL_malloc(sizeof(char *) * nn)) == NULL)
+ goto end;
+ parse_ext_names(tmp_ext_names, names);
+
+ for (i = 0; i < num; i++) {
+ ext = sk_X509_EXTENSION_value(exts, i);
+
+ /* check if this ext is what we want */
+ obj = X509_EXTENSION_get_object(ext);
+ sn = OBJ_nid2sn(OBJ_obj2nid(obj));
+ if (sn == NULL || strcmp(sn, "UNDEF") == 0)
+ continue;
+
+ for (j = 0; j < nn; j++) {
+ if (strcmp(sn, names[j]) == 0) {
+ /* push the extension into a new stack */
+ if (exts2 == NULL
+ && (exts2 = sk_X509_EXTENSION_new_null()) == NULL)
+ goto end;
+ if (!sk_X509_EXTENSION_push(exts2, ext))
+ goto end;
+ }
+ }
+ }
+
+ if (!sk_X509_EXTENSION_num(exts2)) {
+ BIO_printf(bio, "No extensions matched with %s\n", ext_names);
+ ret = 1;
+ goto end;
+ }
+
+ ret = X509V3_extensions_print(bio, NULL, exts2, 0, 0);
+ end:
+ sk_X509_EXTENSION_free(exts2);
+ OPENSSL_free(names);
+ OPENSSL_free(tmp_ext_names);
+ return ret;
+}