Commit 52958608 authored by David von Oheimb's avatar David von Oheimb Committed by Pauli
Browse files

Add -new and -subj options to x509 app for direct cert generation



Complete and improve error output of parse_name() in apps/apps.c

Reviewed-by: default avatarViktor Dukhovni <viktor@openssl.org>
Reviewed-by: default avatarPaul Dale <paul.dale@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/8193)
parent 8a74bb5c
Loading
Loading
Loading
Loading
+11 −6
Original line number Diff line number Diff line
@@ -1623,8 +1623,10 @@ X509_NAME *parse_name(const char *cp, long chtype, int canmulti)
    if (n == NULL)
        return NULL;
    work = OPENSSL_strdup(cp);
    if (work == NULL)
    if (work == NULL) {
        BIO_printf(bio_err, "%s: Error copying name input\n", opt_getprog());
        goto err;
    }

    while (*cp) {
        char *bp = work;
@@ -1639,7 +1641,7 @@ X509_NAME *parse_name(const char *cp, long chtype, int canmulti)
            *bp++ = *cp++;
        if (*cp == '\0') {
            BIO_printf(bio_err,
                    "%s: Hit end of string before finding the equals.\n",
                    "%s: Hit end of string before finding the '='\n",
                    opt_getprog());
            goto err;
        }
@@ -1681,9 +1683,12 @@ X509_NAME *parse_name(const char *cp, long chtype, int canmulti)
        }
        if (!X509_NAME_add_entry_by_NID(n, nid, chtype,
                                        valstr, strlen((char *)valstr),
                                        -1, ismulti ? -1 : 0))
                                        -1, ismulti ? -1 : 0)) {
            BIO_printf(bio_err, "%s: Error adding name attribute \"/%s=%s\"\n",
                       opt_getprog(), typestr ,valstr);
            goto err;
        }
    }

    OPENSSL_free(work);
    return n;
+56 −16
Original line number Diff line number Diff line
@@ -49,8 +49,8 @@ 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,
@@ -132,7 +132,9 @@ const OPTIONS x509_options[] = {
    {"CAform", OPT_CAFORM, 'F', "CA format - default PEM"},
    {"CAkeyform", OPT_CAKEYFORM, 'f', "CA key format - default PEM"},
    {"sigopt", OPT_SIGOPT, 's', "Signature parameter in n:v form"},
    {"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"},
@@ -158,6 +160,11 @@ int x509_main(int argc, char **argv)
    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;
@@ -281,9 +288,15 @@ int x509_main(int argc, char **argv)
            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,
@@ -477,18 +490,38 @@ int x509_main(int argc, char **argv)
        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) {
@@ -516,10 +549,6 @@ int x509_main(int argc, char **argv)
        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;
@@ -537,21 +566,30 @@ int x509_main(int argc, char **argv)
        }
        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;

@@ -567,9 +605,8 @@ int x509_main(int argc, char **argv)
            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;
@@ -582,6 +619,8 @@ int x509_main(int argc, char **argv)
            goto end;
        if (fkey != NULL && !X509_set_pubkey(x, fkey))
            goto end;
        if (fsubj != NULL && !X509_set_subject_name(x, fsubj))
            goto end;
    }

    if (CA_flag) {
@@ -654,7 +693,7 @@ int x509_main(int argc, char **argv)
                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)
@@ -788,7 +827,7 @@ int x509_main(int argc, char **argv)
            }

            /* 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,
@@ -890,6 +929,7 @@ int x509_main(int argc, char **argv)
    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);
+26 −0
Original line number Diff line number Diff line
@@ -52,7 +52,9 @@ B<openssl> B<x509>
[B<-CAkey filename>]
[B<-CAcreateserial>]
[B<-CAserial filename>]
[B<-new>]
[B<-force_pubkey filename>]
[B<-subj arg>]
[B<-text>]
[B<-ext extensions>]
[B<-certopt option>]
@@ -454,15 +456,39 @@ specified then the extensions should either be contained in the unnamed
L<x509v3_config(5)> manual page for details of the
extension section format.

=item B<-new>

Generate a certificate from scratch, not using an input certificate
or certificate request. So the B<-in> option must not be used in this case.
Instead, the B<-subj> and <-force_pubkey> options need to be given.

=item B<-force_pubkey filename>

When a certificate is created set its public key to the key in B<filename>
instead of the key contained in the input or given with the B<-signkey> option.

This option is useful for creating self-issued certificates that are not
self-signed, for instance when the key cannot be used for signing, such as DH.
It can also be used in conjunction with b<-new> and B<-subj> to directly
generate a certificate containing any desired public key.

The format of the key file can be specified using the B<-keyform> option.

=item B<-subj arg>

When a certificate is created set its subject name to the given value.
The arg must be formatted as I</type0=value0/type1=value1/type2=...>.
Keyword characters may be escaped by \ (backslash), and whitespace is retained.
Empty values are permitted, but the corresponding type will not be included
in the certificate. Giving a single I</> will lead to an empty sequence of RDNs
(a NULL subject DN).

Unless the B<-CA> option is given the issuer is set to the same value.

This option can be used in conjunction with the B<-force_pubkey> option
to create a certificate even without providing an input certificate
or certificate request.

=back

=head2 Name Options
+20 −1
Original line number Diff line number Diff line
@@ -15,7 +15,7 @@ use OpenSSL::Test qw/:DEFAULT srctop_file/;

setup("test_x509");

plan tests => 9;
plan tests => 10;

require_ok(srctop_file('test','recipes','tconversion.pl'));

@@ -34,6 +34,25 @@ is(cmp_text($out, srctop_file("test/certs", "cyrillic.utf8")),
   0, 'Comparing utf8 output');
unlink $out;

# producing and checking self-issued (but not self-signed) cert
my @path = qw(test certs);
my $subj = "/CN=CA"; # using same DN as in issuer of ee-cert.pem
my $pkey = srctop_file(@path, "ca-key.pem"); #  issuer private key
my $pubkey = "ca-pubkey.pem"; # the corresponding issuer public key
# use any (different) key for signing our self-issued cert:
my $signkey = srctop_file(@path, "ee-ecdsa-key.pem");
my $selfout = "self-issued.out";
my $testcert = srctop_file(@path, "ee-cert.pem");
ok(run(app(["openssl", "pkey", "-in", $pkey, "-pubout", "-out", $pubkey]))
   &&
   run(app(["openssl", "x509", "-new", "-force_pubkey", $pubkey,
            "-subj", $subj, "-signkey", $signkey, "-out", $selfout]))
   &&
   run(app(["openssl", "verify", "-no_check_time",
            "-trusted", $selfout, $testcert])));
unlink $pubkey;
unlink $selfout;

subtest 'x509 -- x.509 v1 certificate' => sub {
    tconversion("x509", srctop_file("test","testx509.pem"));
};