From 712484ef1cea67448d1d2090fa8c63931b789439 Mon Sep 17 00:00:00 2001 From: Taylor Thomas Date: Thu, 21 May 2020 16:47:01 -0600 Subject: [PATCH] fix(lib): Adds missing context block to CSR When comparing a CSR generated with the same parameters using openssl, I noticed that the CSR generated by rcgen was missing attributes data. OpenSSL: ``` Certificate Request: Data: Version: 0 (0x0) Subject: C=US, ST=., L=., O=., OU=., CN=krustlet Subject Public Key Info: Public Key Algorithm: id-ecPublicKey Public-Key: (256 bit) pub: 04:3c:2b:64:c2:0e:8f:fa:86:b2:5c:f1:66:82:a7: 73:48:37:3b:d7:f0:12:25:0e:29:c2:65:20:1f:a5: 60:36:52:70:ba:60:7e:00:e0:92:74:fe:dd:0f:8a: 28:0a:6e:1b:41:46:10:96:72:54:5f:3c:d1:e5:86: 59:9b:61:a1:e3 ASN1 OID: prime256v1 NIST CURVE: P-256 Attributes: a0:00 Signature Algorithm: ecdsa-with-SHA256 30:45:02:20:50:a9:e6:11:9c:46:4b:c2:85:74:f9:58:30:26: 4f:9c:d2:18:5f:33:fb:b6:74:44:3b:47:b1:2c:e8:cc:14:ac: 02:21:00:eb:92:eb:b8:98:89:2e:d2:0e:fe:9c:af:c7:fe:60: e5:56:af:92:06:37:0f:71:38:1d:19:ba:04:88:3d:b8:b3 ``` rcgen: ``` Certificate Request: Data: Version: 0 (0x0) Subject: C=US, ST=., L=., O=., OU=., CN=krustlet Subject Public Key Info: Public Key Algorithm: id-ecPublicKey Public-Key: (256 bit) pub: 04:02:79:50:53:29:21:16:25:a2:bf:f7:24:41:0d: 12:7b:bf:60:96:b2:b7:fc:c0:2a:8a:16:69:bc:7e: 2d:95:b1:c8:75:23:07:d5:35:89:14:be:56:d3:6b: 2f:b1:e5:1a:39:be:74:bb:25:c8:f8:4a:6f:cf:9a: 27:39:2c:96:66 ASN1 OID: prime256v1 NIST CURVE: P-256 Attributes: Signature Algorithm: ecdsa-with-SHA256 30:45:02:20:7e:96:16:a3:b3:8b:21:25:ce:a7:8a:e0:53:19: cb:3e:24:02:25:73:6a:c8:e7:a0:15:3f:96:c4:b6:bc:50:24: 02:21:00:b6:0e:d8:5d:1c:76:32:78:b7:0a:da:24:c3:58:3a: 4d:9c:8e:16:e8:5e:d3:13:61:ec:e0:2c:77:48:b7:d1:23 ``` This turned out to be missing the following section when looking at `openssl asn1parse`: ``` 183:d=2 hl=2 l= 0 cons: cont [ 0 ] ``` To fix this, I changed the writing of the CSR to always write the context tag, even if no extended attributes were given. NOTE: I found this while trying to use the generated CSR with Kubernetes, and apparently the Go library didn't like it. However, upon further investigation, empty attributes still expect an empty context to be set, so this appears to be compliant with the spec --- src/lib.rs | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6333a916..20763ef8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -573,21 +573,23 @@ impl Certificate { }); // Write subjectPublicKeyInfo self.key_pair.serialize_public_key_der(writer.next()); - // Write extensions - if !self.params.subject_alt_names.is_empty() { - writer.next().write_tagged(Tag::context(0), |writer| { - writer.write_sequence(|writer| { - let oid = ObjectIdentifier::from_slice(OID_PKCS_9_AT_EXTENSION_REQUEST); - writer.next().write_oid(&oid); - writer.next().write_set(|writer| { - writer.next().write_sequence(|writer| { - // Write subject_alt_names - self.write_subject_alt_names(writer.next()); - }); - }); - }); - }); - } + // Write extensions + // According to the spec in RFC 2986, even if attributes are empty we need the empty attribute tag + writer.next().write_tagged(Tag::context(0), |writer| { + if !self.params.subject_alt_names.is_empty() { + writer.write_sequence(|writer| { + let oid = ObjectIdentifier::from_slice(OID_PKCS_9_AT_EXTENSION_REQUEST); + writer.next().write_oid(&oid); + writer.next().write_set(|writer| { + writer.next().write_sequence(|writer| { + // Write subject_alt_names + self.write_subject_alt_names(writer.next()); + }); + }); + }); + } + }); + }); } fn write_cert(&self, writer :DERWriter, ca :&Certificate) -> Result<(), RcgenError> {