Skip to content

Commit 88243ef

Browse files
committed
csr: support populating custom extensions from CSR.
This commit updates the CSR parsing to populate unknown requested extensions as custom extensions in the built CSR `CertificateParams.custom_extensions`.
1 parent 2831ffc commit 88243ef

File tree

2 files changed

+48
-7
lines changed

2 files changed

+48
-7
lines changed

src/csr.rs

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use crate::{DistinguishedName, SanType};
44
use pem::Pem;
55
use std::hash::Hash;
66

7-
use crate::{Certificate, CertificateParams, PublicKeyData, RcgenError, SignatureAlgorithm};
7+
use crate::{
8+
Certificate, CertificateParams, CustomExtension, PublicKeyData, RcgenError, SignatureAlgorithm,
9+
};
810

911
/// A public key, extracted from a CSR
1012
#[derive(Debug, PartialEq, Eq, Hash)]
@@ -66,17 +68,44 @@ impl CertificateSigningRequest {
6668
params.distinguished_name = DistinguishedName::from_name(&info.subject)?;
6769
let raw = info.subject_pki.subject_public_key.data.to_vec();
6870

69-
if let Some(extensions) = csr.requested_extensions() {
70-
for ext in extensions {
71-
match ext {
71+
// Pull out the extension requests attributes from the CSR.
72+
// Note: we avoid using csr.requested_extensions() here because it maps to the parsed
73+
// extension value and we want the raw extension value to handle unknown extensions
74+
// ourselves.
75+
let requested_exts = csr
76+
.certification_request_info
77+
.iter_attributes()
78+
.filter_map(|attr| {
79+
if let x509_parser::prelude::ParsedCriAttribute::ExtensionRequest(requested) =
80+
&attr.parsed_attribute()
81+
{
82+
Some(requested.extensions.iter().collect::<Vec<_>>())
83+
} else {
84+
None
85+
}
86+
})
87+
.flatten()
88+
.collect::<Vec<_>>();
89+
90+
if !requested_exts.is_empty() {
91+
for ext in requested_exts {
92+
let supported = match ext.parsed_extension() {
7293
x509_parser::extensions::ParsedExtension::SubjectAlternativeName(san) => {
7394
for name in &san.general_names {
7495
params
7596
.subject_alt_names
7697
.push(SanType::try_from_general(name)?);
7798
}
99+
true
78100
},
79-
_ => return Err(RcgenError::UnsupportedExtension),
101+
_ => false,
102+
};
103+
if !supported {
104+
params.custom_extensions.push(CustomExtension {
105+
oid: ext.oid.iter().unwrap().collect(),
106+
critical: ext.critical,
107+
content: ext.value.to_vec(),
108+
})
80109
}
81110
}
82111
}

tests/generic.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ mod test_x509_custom_ext {
331331
use crate::util;
332332
use crate::Certificate;
333333

334-
use rcgen::CustomExtension;
334+
use rcgen::{CertificateSigningRequest, CustomExtension};
335335
use x509_parser::oid_registry::asn1_rs;
336336
use x509_parser::prelude::{
337337
FromDer, ParsedCriAttribute, X509Certificate, X509CertificationRequest,
@@ -392,8 +392,20 @@ mod test_x509_custom_ext {
392392
.iter()
393393
.find(|ext| ext.oid == test_oid)
394394
.expect("missing requested custom extension");
395-
assert_eq!(custom_ext.critical, true);
395+
assert!(custom_ext.critical);
396396
assert_eq!(custom_ext.value, test_ext);
397+
398+
// We should be able to create an rcgen CSR from the serialized CSR.
399+
let rcgen_csr = CertificateSigningRequest::from_der(&test_cert_csr_der).unwrap();
400+
// The custom extensions should be present in the CSR.
401+
let custom_ext = rcgen_csr
402+
.params
403+
.custom_extensions
404+
.iter()
405+
.find(|ext| Iterator::eq(ext.oid_components(), test_oid.iter().unwrap()))
406+
.expect("missing requested custom extension");
407+
assert!(custom_ext.criticality());
408+
assert_eq!(custom_ext.content(), test_ext);
397409
}
398410
}
399411

0 commit comments

Comments
 (0)