Add -force_pubkey -multivalue-rdn -set_issuer -set_subject -utf8 to x509 app
authorjob <job@openbsd.org>
Fri, 12 Jan 2024 11:24:02 +0000 (11:24 +0000)
committerjob <job@openbsd.org>
Fri, 12 Jan 2024 11:24:02 +0000 (11:24 +0000)
The -set_issuer, -set_subject, and -force_pubkey features can be used to
'rechain' PKIs, for more information see https://labs.apnic.net/nro-ta/
and https://blog.apnic.net/2023/12/14/models-of-trust-for-the-rpki/

OK tb@

regress/usr.bin/openssl/appstest.sh
usr.bin/openssl/openssl.1
usr.bin/openssl/x509.c

index 3125a42..ae93fd2 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 #
-# $OpenBSD: appstest.sh,v 1.58 2023/07/24 05:54:12 tb Exp $
+# $OpenBSD: appstest.sh,v 1.59 2024/01/12 11:24:02 job Exp $
 #
 # Copyright (c) 2016 Kinichiro Inoguchi <inoguchi@openbsd.org>
 #
@@ -834,13 +834,38 @@ __EOF__
 
        start_message "x509 ... issue cert for server csr#2"
 
+       $openssl_bin genrsa -out $server_dir/testkey.pem 2>&1
+       check_exit_status $?
+       $openssl_bin rsa -in $server_dir/testkey.pem -pubout \
+               -out $server_dir/testpubkey.pem 2>&1
+       check_exit_status $?
+
        revoke_cert=$server_dir/revoke_cert.pem
        $openssl_bin x509 -req -in $revoke_csr -CA $ca_cert -CAform pem \
                -CAkey $ca_key -CAkeyform pem \
                -CAserial $ca_dir/serial -set_serial 10 \
                -passin pass:$ca_pass -CAcreateserial -out $revoke_cert \
+               -set_issuer /CN=issuer -set_subject /CN=subject \
+               -force_pubkey $server_dir/testpubkey.pem
                > $revoke_cert.log 2>&1
        check_exit_status $?
+       
+       start_message "x509 ... check if csr#2 cert has proper issuer & subject"
+       if [ "$($openssl_bin x509 -in $revoke_cert -issuer -noout)" != \
+               "issuer= /CN=issuer" ]; then
+               exit 1
+       fi
+       if [ "$($openssl_bin x509 -in $revoke_cert -subject -noout)" != \
+               "subject= /CN=subject" ]; then
+               exit 1
+       fi
+       check_exit_status 0
+
+       start_message "x509 ... check if csr#2 cert pubkey was forced"
+       $openssl_bin x509 -in $revoke_cert -pubkey -noout > $revoke_cert.pub
+       check_exit_status $?
+       diff $server_dir/testpubkey.pem $revoke_cert.pub
+       check_exit_status $?
 
        start_message "ca ... issue cert for server csr#3"
 
index 3c376f4..b608b16 100644 (file)
@@ -1,4 +1,4 @@
-.\" $OpenBSD: openssl.1,v 1.153 2023/12/29 12:06:48 tb Exp $
+.\" $OpenBSD: openssl.1,v 1.154 2024/01/12 11:24:03 job Exp $
 .\" ====================================================================
 .\" Copyright (c) 1998-2002 The OpenSSL Project.  All rights reserved.
 .\"
 .\" copied and put under another distribution licence
 .\" [including the GNU Public Licence.]
 .\"
-.Dd $Mdocdate: December 29 2023 $
+.Dd $Mdocdate: January 12 2024 $
 .Dt OPENSSL 1
 .Os
 .Sh NAME
@@ -6100,6 +6100,7 @@ version.
 .Op Fl extensions Ar section
 .Op Fl extfile Ar file
 .Op Fl fingerprint
+.Op Fl force_pubkey Ar key
 .Op Fl hash
 .Op Fl in Ar file
 .Op Fl inform Cm der | net | pem
@@ -6109,6 +6110,7 @@ version.
 .Op Fl keyform Cm der | pem
 .Op Fl md5 | sha1
 .Op Fl modulus
+.Op Fl multivalue-rdn
 .Op Fl nameopt Ar option
 .Op Fl next_serial
 .Op Fl noout
@@ -6121,7 +6123,9 @@ version.
 .Op Fl purpose
 .Op Fl req
 .Op Fl serial
+.Op Fl set_issuer Ar name
 .Op Fl set_serial Ar n
+.Op Fl set_subject Ar name
 .Op Fl setalias Ar arg
 .Op Fl signkey Ar file
 .Op Fl sigopt Ar nm:v
@@ -6131,6 +6135,7 @@ version.
 .Op Fl subject_hash_old
 .Op Fl text
 .Op Fl trustout
+.Op Fl utf8
 .Op Fl x509toreq
 .Ek
 .El
@@ -6254,6 +6259,16 @@ using the older algorithm as used by
 versions before 1.0.0.
 .It Fl modulus
 Print the value of the modulus of the public key contained in the certificate.
+.It Fl multivalue-rdn
+This option causes the
+.Fl subj
+argument to be interpreted with full support for multivalued RDNs,
+for example
+.Qq "/DC=org/DC=OpenSSL/DC=users/UID=123456+CN=John Doe" .
+If
+.Fl multivalue-rdn
+is not used, the UID value is set to
+.Qq "123456+CN=John Doe" .
 .It Fl nameopt Ar option
 Customise how the subject or issuer names are displayed,
 either using a list of comma-separated options or by specifying
@@ -6686,12 +6701,25 @@ which contains the section to use.
 .It Fl extfile Ar file
 File containing certificate extensions to use.
 If not specified, no extensions are added to the certificate.
+.It Fl force_pubkey Ar key
+Set the public key of the certificate to the public key contained in
+.Ar key .
 .It Fl keyform Cm der | pem
-The format of the private key file used in the
+The format of the key file used in the
+.Fl force_pubkey
+and
 .Fl signkey
-option.
+options.
 .It Fl req
 Expect a certificate request on input instead of a certificate.
+.It Fl set_issuer Ar name
+The issuer name to use.
+.Ar name
+must be formatted as /type0=value0/type1=value1/type2=...;
+characters may be escaped by
+.Sq \e
+(backslash);
+no spaces are skipped.
 .It Fl set_serial Ar n
 The serial number to use.
 This option can be used with either the
@@ -6710,6 +6738,14 @@ options) is not used.
 The serial number can be decimal or hex (if preceded by
 .Sq 0x ) .
 Negative serial numbers can also be specified but their use is not recommended.
+.It Fl set_subject Ar name
+The subject name to use.
+.Ar name
+must be formatted as /type0=value0/type1=value1/type2=...;
+characters may be escaped by
+.Sq \e
+(backslash);
+no spaces are skipped.
 .It Fl signkey Ar file
 Self-sign
 .Ar file
@@ -6730,6 +6766,10 @@ option is supplied.
 If the input is a certificate request, a self-signed certificate
 is created using the supplied private key using the subject name in
 the request.
+.It Fl utf8
+Interpret field values read from a terminal or obtained from a configuration
+file as UTF-8 strings.
+By default, they are interpreted as ASCII.
 .It Fl x509toreq
 Convert a certificate into a certificate request.
 The
index 7f60110..332399e 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: x509.c,v 1.35 2023/11/21 17:56:19 tb Exp $ */
+/* $OpenBSD: x509.c,v 1.36 2024/01/12 11:24:03 job Exp $ */
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
 
 static int callb(int ok, X509_STORE_CTX *ctx);
 static int sign(X509 *x, EVP_PKEY *pkey, int days, int clrext,
-    const EVP_MD *digest, CONF *conf, char *section);
+    const EVP_MD *digest, CONF *conf, char *section, X509_NAME *issuer);
 static int x509_certify(X509_STORE *ctx, char *CAfile, const EVP_MD *digest,
     X509 *x, X509 *xca, EVP_PKEY *pkey, STACK_OF(OPENSSL_STRING) *sigopts,
     char *serial, int create, int days, int clrext, CONF *conf, char *section,
-    ASN1_INTEGER *sno);
+    ASN1_INTEGER *sno, X509_NAME *issuer);
 static int purpose_print(BIO *bio, X509 *cert, const X509_PURPOSE *pt);
 
 static struct {
@@ -103,6 +103,7 @@ static struct {
        unsigned long certflag;
        int checkend;
        int checkoffset;
+       unsigned long chtype;
        int clrext;
        int clrreject;
        int clrtrust;
@@ -113,6 +114,7 @@ static struct {
        char *extfile;
        char *extsect;
        int fingerprint;
+       char *force_pubkey;
        char *infile;
        int informat;
        int issuer;
@@ -124,6 +126,7 @@ static struct {
        int keyformat;
        const EVP_MD *md_alg;
        int modulus;
+       int multirdn;
        int next_serial;
        unsigned long nmflag;
        int noout;
@@ -139,6 +142,8 @@ static struct {
        STACK_OF(ASN1_OBJECT) *reject;
        int reqfile;
        int serial;
+       char *set_issuer;
+       char *set_subject;
        int sign_flag;
        STACK_OF(OPENSSL_STRING) *sigopts;
        ASN1_INTEGER *sno;
@@ -312,6 +317,13 @@ x509_opt_sigopt(char *arg)
        return (0);
 }
 
+static int
+x509_opt_utf8(void)
+{
+       cfg.chtype = MBSTRING_UTF8;
+       return (0);
+}
+
 static const struct option x509_options[] = {
        {
                .name = "C",
@@ -467,6 +479,13 @@ static const struct option x509_options[] = {
                .opt.order = &cfg.fingerprint,
                .order = &cfg.num,
        },
+       {
+               .name = "force_pubkey",
+               .argname = "key",
+               .desc = "Force the public key to be put in the certificate",
+               .type = OPTION_ARG,
+               .opt.arg = &cfg.force_pubkey,
+       },
        {
                .name = "hash",
                .desc = "Synonym for -subject_hash",
@@ -525,6 +544,12 @@ static const struct option x509_options[] = {
                .opt.order = &cfg.modulus,
                .order = &cfg.num,
        },
+       {
+               .name = "multivalue-rdn",
+               .desc = "Enable support for multivalued RDNs",
+               .type = OPTION_FLAG,
+               .opt.flag = &cfg.multirdn,
+       },
        {
                .name = "nameopt",
                .argname = "option",
@@ -608,6 +633,13 @@ static const struct option x509_options[] = {
                .opt.order = &cfg.serial,
                .order = &cfg.num,
        },
+       {
+               .name = "set_issuer",
+               .argname = "name",
+               .desc = "Set the issuer name",
+               .type = OPTION_ARG,
+               .opt.arg = &cfg.set_issuer,
+       },
        {
                .name = "set_serial",
                .argname = "n",
@@ -615,6 +647,13 @@ static const struct option x509_options[] = {
                .type = OPTION_ARG_FUNC,
                .opt.argfunc = x509_opt_set_serial,
        },
+       {
+               .name = "set_subject",
+               .argname = "name",
+               .desc = "Set the subject name",
+               .type = OPTION_ARG,
+               .opt.arg = &cfg.set_subject,
+       },
        {
                .name = "setalias",
                .argname = "arg",
@@ -643,6 +682,11 @@ static const struct option x509_options[] = {
                .opt.order = &cfg.startdate,
                .order = &cfg.num,
        },
+       {
+               .name = "subj",
+               .type = OPTION_ARG,
+               .opt.arg = &cfg.set_subject,
+       },
        {
                .name = "subject",
                .desc = "Print subject name",
@@ -679,6 +723,12 @@ static const struct option x509_options[] = {
                .type = OPTION_FLAG,
                .opt.flag = &cfg.trustout,
        },
+       {
+               .name = "utf8",
+               .desc = "Input characters are in UTF-8 (default ASCII)",
+               .type = OPTION_FUNC,
+               .opt.func = x509_opt_utf8,
+       },
        {
                .name = "x509toreq",
                .desc = "Output a certification request object",
@@ -704,16 +754,17 @@ x509_usage(void)
            "    [-CAkeyform der | pem] [-CAserial file] [-certopt option]\n"
            "    [-checkend arg] [-clrext] [-clrreject] [-clrtrust] [-dates]\n"
            "    [-days arg] [-email] [-enddate] [-extensions section]\n"
-           "    [-extfile file] [-fingerprint] [-hash] [-in file]\n"
-           "    [-inform der | net | pem] [-issuer] [-issuer_hash]\n"
-           "    [-issuer_hash_old] [-keyform der | pem] [-md5 | -sha1]\n"
-           "    [-modulus] [-nameopt option] [-next_serial] [-noout]\n"
-           "    [-ocsp_uri] [-ocspid] [-out file]\n"
-           "    [-outform der | net | pem] [-passin arg] [-pubkey]\n"
-           "    [-purpose] [-req] [-serial] [-set_serial n] [-setalias arg]\n"
-           "    [-signkey file] [-sigopt nm:v] [-startdate] [-subject]\n"
-           "    [-subject_hash] [-subject_hash_old] [-text] [-trustout]\n"
-           "    [-x509toreq]\n");
+           "    [-extfile file] [-fingerprint] [-force_pubkey key] [-hash]\n"
+           "    [-in file] [-inform der | net | pem] [-issuer]\n"
+           "    [-issuer_hash] [-issuer_hash_old] [-keyform der | pem]\n"
+           "    [-md5 | -sha1] [-modulus] [-multivalue-rdn]\n"
+           "    [-nameopt option] [-next_serial] [-noout] [-ocsp_uri]\n"
+           "    [-ocspid] [-out file] [-outform der | net | pem]\n"
+           "    [-passin arg] [-pubkey] [-purpose] [-req] [-serial]\n"
+           "    [-set_issuer name] [-set_serial n] [-set_subject name]\n"
+           "    [-setalias arg] [-signkey file] [-sigopt nm:v] [-startdate]\n"
+           "    [-subject] [-subject_hash] [-subject_hash_old] [-text]\n"
+           "    [-trustout] [-utf8] [-x509toreq]\n");
        fprintf(stderr, "\n");
        options_usage(x509_options);
        fprintf(stderr, "\n");
@@ -725,7 +776,8 @@ x509_main(int argc, char **argv)
        int ret = 1;
        X509_REQ *req = NULL;
        X509 *x = NULL, *xca = NULL;
-       EVP_PKEY *Upkey = NULL, *CApkey = NULL;
+       X509_NAME *iname = NULL, *sname = NULL;
+       EVP_PKEY *Fpkey = NULL, *Upkey = NULL, *CApkey = NULL;
        int i;
        BIO *out = NULL;
        BIO *STDout = NULL;
@@ -741,6 +793,7 @@ x509_main(int argc, char **argv)
        }
 
        memset(&cfg, 0, sizeof(cfg));
+       cfg.chtype = MBSTRING_ASC;
        cfg.days = DEF_DAYS;
        cfg.informat = FORMAT_PEM;
        cfg.outformat = FORMAT_PEM;
@@ -811,6 +864,11 @@ x509_main(int argc, char **argv)
                        goto end;
                }
        }
+       if (cfg.force_pubkey != NULL) {
+               if ((Fpkey = load_pubkey(bio_err, cfg.force_pubkey,
+                   cfg.keyformat, 0, NULL, "Forced key")) == NULL)
+                       goto end;
+       }
        if (cfg.reqfile) {
                EVP_PKEY *pkey;
                BIO *in;
@@ -875,9 +933,21 @@ x509_main(int argc, char **argv)
                } else if (!X509_set_serialNumber(x, cfg.sno))
                        goto end;
 
-               if (!X509_set_issuer_name(x, X509_REQ_get_subject_name(req)))
+               if (cfg.set_issuer != NULL) {
+                       iname = parse_name(cfg.set_issuer, cfg.chtype,
+                           cfg.multirdn);
+                       if (iname == NULL)
+                               goto end;
+               }
+
+               if (cfg.set_subject != NULL)
+                       sname = parse_name(cfg.set_subject, cfg.chtype,
+                           cfg.multirdn);
+               else
+                       sname = X509_NAME_dup(X509_REQ_get_subject_name(req));
+               if (sname == NULL)
                        goto end;
-               if (!X509_set_subject_name(x, X509_REQ_get_subject_name(req)))
+               if (!X509_set_subject_name(x, sname))
                        goto end;
 
                if (X509_gmtime_adj(X509_get_notBefore(x), 0) == NULL)
@@ -886,7 +956,9 @@ x509_main(int argc, char **argv)
                    NULL) == NULL)
                        goto end;
 
-               if ((pkey = X509_REQ_get0_pubkey(req)) == NULL)
+               if ((pkey = Fpkey) == NULL)
+                       pkey = X509_REQ_get0_pubkey(req);
+               if (pkey == NULL)
                        goto end;
                if (!X509_set_pubkey(x, pkey))
                        goto end;
@@ -1204,7 +1276,7 @@ x509_main(int argc, char **argv)
                                }
                                if (!sign(x, Upkey, cfg.days,
                                    cfg.clrext, cfg.digest,
-                                   extconf, cfg.extsect))
+                                   extconf, cfg.extsect, iname))
                                        goto end;
                        } else if (cfg.CA_flag == i) {
                                BIO_printf(bio_err, "Getting CA Private Key\n");
@@ -1218,7 +1290,7 @@ x509_main(int argc, char **argv)
                                if (!x509_certify(ctx, cfg.CAfile, cfg.digest,
                                    x, xca, CApkey, cfg.sigopts, cfg.CAserial,
                                    cfg.CA_createserial, cfg.days, cfg.clrext,
-                                   extconf, cfg.extsect, cfg.sno))
+                                   extconf, cfg.extsect, cfg.sno, iname))
                                        goto end;
                        } else if (cfg.x509req == i) {
                                EVP_PKEY *pk;
@@ -1302,10 +1374,13 @@ x509_main(int argc, char **argv)
        NCONF_free(extconf);
        BIO_free_all(out);
        BIO_free_all(STDout);
+       X509_NAME_free(iname);
+       X509_NAME_free(sname);
        X509_STORE_free(ctx);
        X509_REQ_free(req);
        X509_free(x);
        X509_free(xca);
+       EVP_PKEY_free(Fpkey);
        EVP_PKEY_free(Upkey);
        EVP_PKEY_free(CApkey);
        sk_OPENSSL_STRING_free(cfg.sigopts);
@@ -1366,7 +1441,7 @@ static int
 x509_certify(X509_STORE *ctx, char *CAfile, const EVP_MD *digest, X509 *x,
     X509 *xca, EVP_PKEY *pkey, STACK_OF(OPENSSL_STRING) *sigopts,
     char *serialfile, int create, int days, int clrext, CONF *conf,
-    char *section, ASN1_INTEGER *sno)
+    char *section, ASN1_INTEGER *sno, X509_NAME *issuer)
 {
        int ret = 0;
        ASN1_INTEGER *bs = NULL;
@@ -1405,8 +1480,14 @@ x509_certify(X509_STORE *ctx, char *CAfile, const EVP_MD *digest, X509 *x,
                    "CA certificate and CA private key do not match\n");
                goto end;
        }
-       if (!X509_set_issuer_name(x, X509_get_subject_name(xca)))
+
+       if (issuer == NULL)
+               issuer = X509_get_subject_name(xca);
+       if (issuer == NULL)
+               goto end;
+       if (!X509_set_issuer_name(x, issuer))
                goto end;
+
        if (!X509_set_serialNumber(x, bs))
                goto end;
 
@@ -1483,7 +1564,7 @@ callb(int ok, X509_STORE_CTX *ctx)
 /* self sign */
 static int
 sign(X509 *x, EVP_PKEY *pkey, int days, int clrext, const EVP_MD *digest,
-    CONF *conf, char *section)
+    CONF *conf, char *section, X509_NAME *issuer)
 {
        EVP_PKEY *pktmp;
 
@@ -1493,7 +1574,11 @@ sign(X509 *x, EVP_PKEY *pkey, int days, int clrext, const EVP_MD *digest,
        EVP_PKEY_copy_parameters(pktmp, pkey);
        EVP_PKEY_save_parameters(pktmp, 1);
 
-       if (!X509_set_issuer_name(x, X509_get_subject_name(x)))
+       if (issuer == NULL)
+               issuer = X509_get_subject_name(x);
+       if (issuer == NULL)
+               goto err;
+       if (!X509_set_issuer_name(x, issuer))
                goto err;
        if (X509_gmtime_adj(X509_get_notBefore(x), 0) == NULL)
                goto err;