Skip to content

Commit

Permalink
csr: support populating custom extensions from CSR.
Browse files Browse the repository at this point in the history
This commit updates the CSR parsing to populate unknown requested
extensions as custom extensions in the built CSR
`CertificateParams.custom_extensions`.
  • Loading branch information
cpu committed Sep 29, 2023
1 parent b32736b commit e8348ab
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 7 deletions.
38 changes: 33 additions & 5 deletions src/csr.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#[cfg(feature = "x509-parser")]
use crate::{DistinguishedName, SanType};
use crate::{CustomExtension, DistinguishedName, SanType};
#[cfg(feature = "pem")]
use pem::Pem;
use std::hash::Hash;
Expand Down Expand Up @@ -67,20 +67,48 @@ impl CertificateSigningRequest {
params.distinguished_name = DistinguishedName::from_name(&info.subject)?;
let raw = info.subject_pki.subject_public_key.data.to_vec();

if let Some(extensions) = csr.requested_extensions() {
for ext in extensions {
match ext {
// Pull out the extension requests attributes from the CSR.
// Note: we avoid using csr.requested_extensions() here because it maps to the parsed
// extension value and we want the raw extension value to handle unknown extensions
// ourselves.
let requested_exts = csr
.certification_request_info
.iter_attributes()
.filter_map(|attr| {
if let x509_parser::prelude::ParsedCriAttribute::ExtensionRequest(requested) =
&attr.parsed_attribute()
{
Some(requested.extensions.iter().collect::<Vec<_>>())
} else {
None
}
})
.flatten()
.collect::<Vec<_>>();

if !requested_exts.is_empty() {
for ext in requested_exts {
let supported = match ext.parsed_extension() {
x509_parser::extensions::ParsedExtension::SubjectAlternativeName(san) => {
for name in &san.general_names {
params
.subject_alt_names
.push(SanType::try_from_general(name)?);
}
true
},
x509_parser::extensions::ParsedExtension::SubjectKeyIdentifier(ski) => {
params.key_identifier = ski.0.to_vec();
true
},
_ => return Err(RcgenError::UnsupportedExtension),
_ => false,
};
if !supported {
params.custom_extensions.push(CustomExtension {
oid: ext.oid.iter().unwrap().collect(),
critical: ext.critical,
content: ext.value.to_vec(),
})
}
}
}
Expand Down
16 changes: 14 additions & 2 deletions tests/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ mod test_csr_exts {
mod test_x509_custom_ext {
use crate::util;

use rcgen::{Certificate, CustomExtension};
use rcgen::{Certificate, CertificateSigningRequest, CustomExtension};
use x509_parser::oid_registry::asn1_rs;
use x509_parser::prelude::{
FromDer, ParsedCriAttribute, X509Certificate, X509CertificationRequest,
Expand Down Expand Up @@ -396,8 +396,20 @@ mod test_x509_custom_ext {
.iter()
.find(|ext| ext.oid == test_oid)
.expect("missing requested custom extension");
assert_eq!(custom_ext.critical, true);
assert!(custom_ext.critical);
assert_eq!(custom_ext.value, test_ext);

// We should be able to create an rcgen CSR from the serialized CSR.
let rcgen_csr = CertificateSigningRequest::from_der(&test_cert_csr_der).unwrap();
// The custom extensions should be present in the CSR.
let custom_ext = rcgen_csr
.params
.custom_extensions
.iter()
.find(|ext| Iterator::eq(ext.oid_components(), test_oid.iter().unwrap()))
.expect("missing requested custom extension");
assert!(custom_ext.criticality());
assert_eq!(custom_ext.content(), test_ext);
}
}

Expand Down

0 comments on commit e8348ab

Please sign in to comment.