Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions rcgen/src/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1493,4 +1493,43 @@ PITGdT9dgN88nHPCle0B1+OY+OZ5
);
}
}

#[cfg(feature = "x509-parser")]
#[test]
fn test_certificate_with_multiple_domain_components_roundtrip() {
let domain_component_dn_type = DnType::CustomDnType(vec![0, 9, 2342, 19200300, 100, 1, 25]); // Domain Component (DC)

let dc_value_1 = DnValue::Ia5String("example".try_into().unwrap());
let dc_value_2 = DnValue::Ia5String("com".try_into().unwrap());

let mut params = CertificateParams::new(vec!["crabs".to_owned()]).unwrap();
params.distinguished_name = DistinguishedName::new();
params
.distinguished_name
.push(domain_component_dn_type.clone(), dc_value_1.clone());

params
.distinguished_name
.push(domain_component_dn_type.clone(), dc_value_2.clone());

let key_pair = KeyPair::generate().unwrap();
let cert = params.self_signed(&key_pair).unwrap();

// We should be able to parse the certificate with x509-parser.
assert!(x509_parser::parse_x509_certificate(cert.der()).is_ok());

// We should be able to reconstitute params from the DER using x509-parser.
let params_from_cert = CertificateParams::from_ca_cert_der(cert.der()).unwrap();

// We should find the expected distinguished name in the reconstituted params.
let expected_names = &[
(&domain_component_dn_type, &dc_value_1),
(&domain_component_dn_type, &dc_value_2),
];
let names = params_from_cert
.distinguished_name
.iter()
.collect::<Vec<(_, _)>>();
assert_eq!(names, expected_names);
}
}
57 changes: 45 additions & 12 deletions rcgen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ println!("{}", signing_key.serialize_pem());
#![warn(unreachable_pub)]

use std::borrow::Cow;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
Expand Down Expand Up @@ -470,7 +471,7 @@ See also the RFC 5280 sections on the [issuer](https://tools.ietf.org/html/rfc52
and [subject](https://tools.ietf.org/html/rfc5280#section-4.1.2.6) fields.
*/
pub struct DistinguishedName {
entries: HashMap<DnType, DnValue>,
entries: HashMap<DnType, Vec<DnValue>>,
order: Vec<DnType>,
}

Expand All @@ -479,10 +480,17 @@ impl DistinguishedName {
pub fn new() -> Self {
Self::default()
}
/// Obtains the attribute value for the given attribute type
pub fn get(&self, ty: &DnType) -> Option<&DnValue> {
self.entries.get(ty)

/// Obtains the first attribute value for the given attribute type
pub fn first(&self, ty: &DnType) -> Option<&DnValue> {
self.entries.get(ty)?.first()
}

/// Obtains all attribute values for the given attribute type
pub fn get(&self, ty: &DnType) -> Option<&[DnValue]> {
self.entries.get(ty).map(|v| v.as_slice())
}

/// Removes the attribute with the specified DnType
///
/// Returns true when an actual removal happened, false
Expand All @@ -502,20 +510,24 @@ impl DistinguishedName {
/// let mut dn = DistinguishedName::new();
/// dn.push(DnType::OrganizationName, "Crab widgits SE");
/// dn.push(DnType::CommonName, DnValue::PrintableString("Master Cert".try_into().unwrap()));
/// assert_eq!(dn.get(&DnType::OrganizationName), Some(&DnValue::Utf8String("Crab widgits SE".to_string())));
/// assert_eq!(dn.get(&DnType::CommonName), Some(&DnValue::PrintableString("Master Cert".try_into().unwrap())));
/// assert_eq!(dn.first(&DnType::OrganizationName), Some(&DnValue::Utf8String("Crab widgits SE".to_string())));
/// assert_eq!(dn.first(&DnType::CommonName), Some(&DnValue::PrintableString("Master Cert".try_into().unwrap())));
/// ```
pub fn push(&mut self, ty: DnType, s: impl Into<DnValue>) {
if !self.entries.contains_key(&ty) {
self.order.push(ty.clone());
match self.entries.entry(ty.clone()) {
Entry::Occupied(mut o) => o.get_mut().push(s.into()),
Entry::Vacant(v) => {
v.insert(Vec::new()).push(s.into());
self.order.push(ty);
},
}
self.entries.insert(ty, s.into());
}
/// Iterate over the entries
pub fn iter(&self) -> DistinguishedNameIterator<'_> {
DistinguishedNameIterator {
distinguished_name: self,
iter: self.order.iter(),
current: None,
}
}

Expand Down Expand Up @@ -571,15 +583,36 @@ Iterator over [`DistinguishedName`] entries
pub struct DistinguishedNameIterator<'a> {
distinguished_name: &'a DistinguishedName,
iter: std::slice::Iter<'a, DnType>,
current: Option<(&'a DnType, std::slice::Iter<'a, DnValue>)>,
}

impl<'a> Iterator for DistinguishedNameIterator<'a> {
type Item = (&'a DnType, &'a DnValue);

fn next(&mut self) -> Option<Self::Item> {
self.iter
.next()
.and_then(|ty| self.distinguished_name.entries.get(ty).map(|v| (ty, v)))
if let Some((ty, values)) = &mut self.current {
match values.next() {
Some(val) => return Some((ty, val)),
None => self.current = None,
}
}

for ty in &mut self.iter {
let Some(values) = self.distinguished_name.entries.get(ty) else {
continue;
};

match values.as_slice() {
[] => continue,
[first] => return Some((ty, first)),
[first, rest @ ..] => {
self.current = Some((ty, rest.iter()));
return Some((ty, first));
},
}
}

None
}
}

Expand Down
Loading