Add apps/x509 -set_issuer & -set_subject option to override issuer & subject
authorJob Snijders <job@sobornost.net>
Wed, 10 Jan 2024 17:15:52 +0000 (17:15 +0000)
committerTomas Mraz <tomas@openssl.org>
Mon, 15 Jan 2024 09:40:01 +0000 (10:40 +0100)
This changeset adds the counterpart to the '-subj' option to allow overriding
the Issuer. For consistency, the `-subj` option is aliased to `-set_subject`.

The issuer can be specified as following apps/openssl x509 -new -set_issuer
'/CN=example-nro-ta' -subj '/CN=2a7dd1d787d793e4c8af56e197d4eed92af6ba13' ...

This is useful in constructing specific test-cases or rechaining PKI trees

Joint work with George Michaelson (@geeohgeegeeoh)

Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23257)

CHANGES.md
apps/x509.c
doc/man1/openssl-x509.pod.in
test/recipes/25-test_x509.t

index eb16a6e24ed6e609d16708c7dbda6f2925f203cf..58d06ae49847c66ec1f869eee802d96ca2e7df68 100644 (file)
@@ -36,6 +36,12 @@ OpenSSL 3.3
 
     *Neil Horman*
 
+ * Added `-set_issuer` and `-set_subject` options to `openssl x509` to
+   override the Issuer and Subject when creating a certificate. The `-subj`
+   option now is an alias for `-set_subject`.
+
+    *Job Snijders, George Michaelson*
+
  * OPENSSL_sk_push() and sk_<TYPE>_push() functions now return 0 instead of -1
    if called with a NULL stack argument.
 
index 578af2364fc12c82ac570906d439440154e69564..ce3fda671665fbf17037c00722f9e1606d29ed47 100644 (file)
@@ -43,7 +43,7 @@ typedef enum OPTION_choice {
     OPT_INFORM, OPT_OUTFORM, OPT_KEYFORM, OPT_REQ, OPT_CAFORM,
     OPT_CAKEYFORM, OPT_VFYOPT, OPT_SIGOPT, OPT_DAYS, OPT_PASSIN, OPT_EXTFILE,
     OPT_EXTENSIONS, OPT_IN, OPT_OUT, OPT_KEY, OPT_SIGNKEY, OPT_CA, OPT_CAKEY,
-    OPT_CASERIAL, OPT_SET_SERIAL, OPT_NEW, OPT_FORCE_PUBKEY, OPT_SUBJ,
+    OPT_CASERIAL, OPT_SET_SERIAL, OPT_NEW, OPT_FORCE_PUBKEY, OPT_ISSU, OPT_SUBJ,
     OPT_ADDTRUST, OPT_ADDREJECT, OPT_SETALIAS, OPT_CERTOPT, OPT_DATEOPT, OPT_NAMEOPT,
     OPT_EMAIL, OPT_OCSP_URI, OPT_SERIAL, OPT_NEXT_SERIAL,
     OPT_MODULUS, OPT_PUBKEY, OPT_X509TOREQ, OPT_TEXT, OPT_HASH,
@@ -138,7 +138,9 @@ const OPTIONS x509_options[] = {
      "Number of days until newly generated certificate expires - default 30"},
     {"preserve_dates", OPT_PRESERVE_DATES, '-',
      "Preserve existing validity dates"},
-    {"subj", OPT_SUBJ, 's', "Set or override certificate subject (and issuer)"},
+    {"set_issuer", OPT_ISSU, 's', "Set or override certificate issuer"},
+    {"set_subject", OPT_SUBJ, 's', "Set or override certificate subject (and issuer)"},
+    {"subj", OPT_SUBJ, 's', "Alias for -set_subject"},
     {"force_pubkey", OPT_FORCE_PUBKEY, '<',
      "Key to be placed in new certificate or certificate request"},
     {"clrext", OPT_CLREXT, '-',
@@ -262,8 +264,8 @@ int x509_main(int argc, char **argv)
     EVP_PKEY *privkey = NULL, *CAkey = NULL, *pubkey = NULL;
     EVP_PKEY *pkey;
     int newcert = 0;
-    char *subj = NULL, *digest = NULL;
-    X509_NAME *fsubj = NULL;
+    char *issu = NULL, *subj = NULL, *digest = NULL;
+    X509_NAME *fissu = NULL, *fsubj = NULL;
     const unsigned long chtype = MBSTRING_ASC;
     const int multirdn = 1;
     STACK_OF(ASN1_OBJECT) *trust = NULL, *reject = NULL;
@@ -425,6 +427,9 @@ int x509_main(int argc, char **argv)
         case OPT_FORCE_PUBKEY:
             pubkeyfile = opt_arg();
             break;
+        case OPT_ISSU:
+            issu = opt_arg();
+            break;
         case OPT_SUBJ:
             subj = opt_arg();
             break;
@@ -651,6 +656,9 @@ int x509_main(int argc, char **argv)
             goto err;
         }
     }
+    if (issu != NULL
+            && (fissu = parse_name(issu, chtype, multirdn, "issuer")) == NULL)
+        goto end;
     if (subj != NULL
             && (fsubj = parse_name(subj, chtype, multirdn, "subject")) == NULL)
         goto end;
@@ -830,8 +838,13 @@ int x509_main(int argc, char **argv)
     if (reqfile || newcert || privkey != NULL || CAfile != NULL) {
         if (!preserve_dates && !set_cert_times(x, NULL, NULL, days))
             goto end;
-        if (!X509_set_issuer_name(x, X509_get_subject_name(issuer_cert)))
-            goto end;
+        if (fissu != NULL) {
+            if (!X509_set_issuer_name(x, fissu))
+                goto end;
+        } else {
+            if (!X509_set_issuer_name(x, X509_get_subject_name(issuer_cert)))
+                goto end;
+        }
     }
 
     X509V3_set_ctx(&ext_ctx, issuer_cert, x, NULL, NULL, X509V3_CTX_REPLACE);
@@ -1079,6 +1092,7 @@ int x509_main(int argc, char **argv)
     NCONF_free(extconf);
     BIO_free_all(out);
     X509_STORE_free(ctx);
+    X509_NAME_free(fissu);
     X509_NAME_free(fsubj);
     X509_REQ_free(req);
     X509_free(x);
index 2d7a1b859ac4a52e8a1e945a9b428d5c6a7633e7..3a5bd25d56bec26d689f8c41e4950c3528e239e9 100644 (file)
@@ -56,6 +56,8 @@ B<openssl> B<x509>
 [B<-next_serial>]
 [B<-days> I<arg>]
 [B<-preserve_dates>]
+[B<-set_issuer> I<arg>]
+[B<-set_subject> I<arg>]
 [B<-subj> I<arg>]
 [B<-force_pubkey> I<filename>]
 [B<-clrext>]
@@ -123,7 +125,7 @@ see L<openssl-passphrase-options(1)>.
 Generate a certificate from scratch, not using an input certificate
 or certificate request.
 So this excludes the B<-in> and B<-req> options.
-Instead, the B<-subj> option needs to be given.
+Instead, the B<-set_subject> option needs to be given.
 The public key to include can be given with the B<-force_pubkey> option
 and defaults to the key given with the B<-key> (or B<-signkey>) option,
 which implies self-signature.
@@ -386,10 +388,17 @@ When signing a certificate, preserve "notBefore" and "notAfter" dates of any
 input certificate instead of adjusting them to current time and duration.
 Cannot be used together with the B<-days> option.
 
-=item B<-subj> I<arg>
+=item B<-set_issuer> I<arg>
+
+When a certificate is created set its issuer name to the given value.
+
+See B<-set_subject> on how the arg must be formatted.
+
+=item B<-set_subject> I<arg>
 
 When a certificate is created set its subject name to the given value.
-When the certificate is self-signed the issuer name is set to the same value.
+When the certificate is self-signed the issuer name is set to the same value,
+unless the B<-set_issuer> option is given.
 
 The arg must be formatted as C</type0=value0/type1=value1/type2=...>.
 Special characters may be escaped by C<\> (backslash), whitespace is retained.
@@ -405,6 +414,10 @@ C</DC=org/DC=OpenSSL/DC=users/UID=123456+CN=John Doe>
 This option can be used with the B<-new> and B<-force_pubkey> options to create
 a new certificate without providing an input certificate or certificate request.
 
+=item B<-subj> I<arg>
+
+This option is an alias of B<-set_subject>.
+
 =item B<-force_pubkey> I<filename>
 
 When a new certificate or certificate request is created
@@ -413,7 +426,7 @@ instead of the key contained in the input
 or given with the B<-key> (or B<-signkey>) option.
 If the input contains no public key but a private key, its public part is used.
 
-This option can be used in conjunction with b<-new> and B<-subj>
+This option can be used in conjunction with b<-new> and B<-set_subject>
 to directly generate a certificate containing any desired public key.
 
 This option is also useful for creating self-issued certificates that are not
index 9b11169a98269ac012725bac5d98c8b152cf9d54..eeb8083506e1ab36628549c2a0afb7767c7dc32b 100644 (file)
@@ -16,7 +16,7 @@ use OpenSSL::Test qw/:DEFAULT srctop_file/;
 
 setup("test_x509");
 
-plan tests => 44;
+plan tests => 46;
 
 # Prevent MSys2 filename munging for arguments that look like file paths but
 # aren't
@@ -81,6 +81,15 @@ ok(run(app(["openssl", "pkey", "-in", $pkey, "-pubout", "-out", $pubkey]))
 # not unlinking $pubkey
 # not unlinking $selfout
 
+# test -set_issuer option
+my $ca_issu = srctop_file(@certs, "ca-cert.pem"); # issuer cert
+my $caout_issu = "ca-issu.out";
+ok(run(app(["openssl", "x509", "-new", "-force_pubkey", $key, "-subj", "/CN=EE",
+            "-set_issuer", "/CN=TEST-CA", "-extfile", $extfile, "-CA", $ca_issu,
+            "-CAkey", $pkey, "-text", "-out", $caout_issu])));
+ok(get_issuer($caout_issu) =~ /CN=TEST-CA/);
+# not unlinking $caout
+
 # simple way of directly producing a CA-signed cert with private/pubkey input
 my $ca = srctop_file(@certs, "ca-cert.pem"); # issuer cert
 my $caout = "ca-issued.out";