]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
x509: Accept 'contentCommitment' as alias master
authorClemens Lang <cllang@redhat.com>
Sun, 3 Aug 2025 18:47:36 +0000 (20:47 +0200)
committerDmitry Belyavskiy <beldmit@gmail.com>
Mon, 11 Aug 2025 10:00:38 +0000 (12:00 +0200)
ITU-T X.509 (10/2019) section 9.2.2.3 [1] defines 'contentCommitment' as
the current name for what had previously been called 'nonRepudiation',
and deprecates the old name:

> It is not incorrect to refer to this keyUsage bit using the identifier
> nonRepudiation. However, the use of this identifier has been
> deprecated.

Allow 'contentCommitment' as an alias wherever 'nonRepudiation' has been
accepted before, so that passing

    -addext keyUsage=critical,contentCommitment

works as expected.

Add a test that checks that contentCommitment sets the same keyUsage bit
as nonRepudiation. Adjust the docs to mention the available alias name.

[1]: https://www.itu.int/rec/T-REC-X.509-201910-I/en

Signed-off-by: Clemens Lang <cllang@redhat.com>
Reviewed-by: David von Oheimb <david.von.oheimb@siemens.com>
Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
(Merged from https://github.com/openssl/openssl/pull/28161)

crypto/asn1/t_bitst.c
crypto/x509/v3_bitst.c
doc/man1/openssl-verification-options.pod
doc/man5/x509v3_config.pod
test/recipes/25-test_req.t

index e7b817f78e15dbf4b6f2940f6dc9911fc7faa635..4c49283bed3e4e1336f9dd39e6de976ab87596e2 100644 (file)
@@ -17,8 +17,19 @@ int ASN1_BIT_STRING_name_print(BIO *out, ASN1_BIT_STRING *bs,
 {
     BIT_STRING_BITNAME *bnam;
     char first = 1;
+    int last_seen_bit = -1;
+
     BIO_printf(out, "%*s", indent, "");
     for (bnam = tbl; bnam->lname; bnam++) {
+        /*
+         * Skip duplicate entries for the same bit in the BIT_STRING_BITNAME
+         * table. Those are aliases, but we only want to print the first entry
+         * when converting to a string.
+         */
+        if (last_seen_bit == bnam->bitnum)
+            continue;
+        last_seen_bit = bnam->bitnum;
+
         if (ASN1_BIT_STRING_get_bit(bs, bnam->bitnum)) {
             if (!first)
                 BIO_puts(out, ", ");
index d41c95b5138f6e7b8f1d023d89d184b1119b7ee0..e487dc98e13fb17a882083a163606db66474a506 100644 (file)
@@ -28,6 +28,7 @@ static BIT_STRING_BITNAME ns_cert_type_table[] = {
 static BIT_STRING_BITNAME key_usage_type_table[] = {
     {0, "Digital Signature", "digitalSignature"},
     {1, "Non Repudiation", "nonRepudiation"},
+    {1, "Content Commitment", "contentCommitment"},
     {2, "Key Encipherment", "keyEncipherment"},
     {3, "Data Encipherment", "dataEncipherment"},
     {4, "Key Agreement", "keyAgreement"},
@@ -48,7 +49,17 @@ STACK_OF(CONF_VALUE) *i2v_ASN1_BIT_STRING(X509V3_EXT_METHOD *method,
                                           STACK_OF(CONF_VALUE) *ret)
 {
     BIT_STRING_BITNAME *bnam;
+    int last_seen_bit = -1;
+
     for (bnam = method->usr_data; bnam->lname; bnam++) {
+        /*
+         * If the bitnumber did not change from the last iteration, this entry
+         * is an an alias for the previous bit; treat the first result as
+         * canonical and ignore the rest.
+         */
+        if (last_seen_bit == bnam->bitnum)
+            continue;
+        last_seen_bit = bnam->bitnum;
         if (ASN1_BIT_STRING_get_bit(bits, bnam->bitnum))
             X509V3_add_value(bnam->lname, NULL, &ret);
     }
index 81a11c37f4c49d8bc66905508b4319d94bf2c978..6f78fcdcc5bacddd1c45bb2f36a3361010f4006a 100644 (file)
@@ -661,7 +661,8 @@ This is used as a workaround if the basicConstraints extension is absent.
 =item B<S/MIME Signing> (C<smimesign>)
 
 In addition to the common S/MIME checks, for target certificates
-the key usage must allow for C<digitalSignature> and/or B<nonRepudiation>.
+the key usage must allow for C<digitalSignature> and/or B<nonRepudiation> (or
+its alias name B<contentCommitment>).
 
 =item B<S/MIME Encryption> (C<smimeencrypt>)
 
@@ -685,7 +686,8 @@ For all other certificates the normal CA checks apply.
 =item B<Timestamp Signing> (C<timestampsign>)
 
 For target certificates, if the key usage extension is present, it must include
-C<digitalSignature> and/or C<nonRepudiation> and must not include other bits.
+C<digitalSignature> and/or C<nonRepudiation> (or its alias name
+C<contentCommitment>) and must not include other bits.
 The EKU extension must be present and contain C<timeStamping> only.
 Moreover, it must be marked as critical.
 
index ab33b7e7afe03ec589da0d94ab7ac844675f5dba..7aaa8d24045aaa48cda17acf2baf8cf607bede2f 100644 (file)
@@ -130,13 +130,16 @@ any sub-CA's, and can only sign end-entity certificates.
 
 Key usage is a multi-valued extension consisting of a list of names of
 the permitted key usages.  The defined values are: C<digitalSignature>,
-C<nonRepudiation>, C<keyEncipherment>, C<dataEncipherment>, C<keyAgreement>,
-C<keyCertSign>, C<cRLSign>, C<encipherOnly>, and C<decipherOnly>.
+C<nonRepudiation> (with an alternative name C<contentCommitment>),
+C<keyEncipherment>, C<dataEncipherment>, C<keyAgreement>, C<keyCertSign>,
+C<cRLSign>, C<encipherOnly>, and C<decipherOnly>.
 
 Examples:
 
  keyUsage = digitalSignature, nonRepudiation
 
+ keyUsage = digitalSignature, contentCommitment
+
  keyUsage = critical, keyCertSign
 
 =head2 Extended Key Usage
index 0b1c16aee05bdccb22d3298eb735df9abb20a1d7..7dfbe02778e76dbbd43b0895409b0405a6182684 100644 (file)
@@ -15,7 +15,7 @@ use OpenSSL::Test qw/:DEFAULT srctop_file/;
 
 setup("test_req");
 
-plan tests => 113;
+plan tests => 116;
 
 require_ok(srctop_file('test', 'recipes', 'tconversion.pl'));
 
@@ -748,6 +748,13 @@ generate_cert($cert, "-in", srctop_file(@certs, "ext-check.csr"),
     "-copy_extensions", "copy");
 has_keyUsage($cert, 1);
 
+# keyUsage=contentCommitment is an alias for nonRepudiation
+$cert = "self-issued-v3_CA_keyUsage_contentCommitment.pem";
+generate_cert($cert, "-addext", "keyUsage = contentCommitment",
+    "-in", srctop_file(@certs, "x509-check.csr"));
+cert_contains($cert, "Non Repudiation", 1);
+cert_contains($cert, "Content Commitment", 0);
+
 # Generate cert using req with '-modulus'
 ok(run(app(["openssl", "req", "-x509", "-new", "-days", "365",
             "-key", srctop_file("test", "testrsa.pem"),