Download raw body.
usr.bin/openssl: x509 add -force_pubkey -utf8 -set_issuer -set_subject -multivalue-rdn
Dear all,
The below changeset adds support to the 'openssl x509' command line
utility for the following options: -force_pubkey, -multivalue-rdn,
-set_issuer, -set_subject, and -utf8.
This diff solves https://github.com/libressl/portable/issues/842
I proposed -set_issuer and -set_subject to OpenSSL as well:
https://github.com/openssl/openssl/pull/23257
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/ .
This brings us quite close, but there is a bit more work to be done
after this lands.
Kind regards,
Job
Index: regress/usr.bin/openssl/appstest.sh
===================================================================
RCS file: /cvs/src/regress/usr.bin/openssl/appstest.sh,v
diff -u -p -r1.58 appstest.sh
--- regress/usr.bin/openssl/appstest.sh 24 Jul 2023 05:54:12 -0000 1.58
+++ regress/usr.bin/openssl/appstest.sh 11 Jan 2024 20:28:26 -0000
@@ -834,12 +834,37 @@ __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: usr.bin/openssl/openssl.1
===================================================================
RCS file: /cvs/src/usr.bin/openssl/openssl.1,v
diff -u -p -r1.153 openssl.1
--- usr.bin/openssl/openssl.1 29 Dec 2023 12:06:48 -0000 1.153
+++ usr.bin/openssl/openssl.1 11 Jan 2024 20:28:27 -0000
@@ -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: usr.bin/openssl/x509.c
===================================================================
RCS file: /cvs/src/usr.bin/openssl/x509.c,v
diff -u -p -r1.35 x509.c
--- usr.bin/openssl/x509.c 21 Nov 2023 17:56:19 -0000 1.35
+++ usr.bin/openssl/x509.c 11 Jan 2024 20:28:27 -0000
@@ -81,11 +81,11 @@
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",
@@ -468,6 +480,13 @@ static const struct option x509_options[
.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",
.type = OPTION_ORDER,
@@ -526,6 +545,12 @@ static const struct option x509_options[
.order = &cfg.num,
},
{
+ .name = "multivalue-rdn",
+ .desc = "Enable support for multivalued RDNs",
+ .type = OPTION_FLAG,
+ .opt.flag = &cfg.multirdn,
+ },
+ {
.name = "nameopt",
.argname = "option",
.desc = "Various certificate name options",
@@ -609,6 +634,13 @@ static const struct option x509_options[
.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",
.desc = "Serial number to use",
@@ -616,6 +648,13 @@ static const struct option x509_options[
.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",
.desc = "Set certificate alias",
@@ -680,6 +719,12 @@ static const struct option x509_options[
.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",
.type = OPTION_ORDER,
@@ -704,16 +749,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 +771,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 +788,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 +859,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 +928,18 @@ 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 (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 +948,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 +1268,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 +1282,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 +1366,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 +1433,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 +1472,14 @@ x509_certify(X509_STORE *ctx, char *CAfi
"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 +1556,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 +1566,11 @@ sign(X509 *x, EVP_PKEY *pkey, int days,
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;
usr.bin/openssl: x509 add -force_pubkey -utf8 -set_issuer -set_subject -multivalue-rdn