Skip to content

Commit b327923

Browse files
committed
Allow multiple issuer items of the same king
There can be multiple of the same entry, e.g. DC (domain component) entries in an issuer. Using a HashMap is too restrictive.
1 parent 12d6533 commit b327923

File tree

5 files changed

+128
-50
lines changed

5 files changed

+128
-50
lines changed

rcgen/src/certificate.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,7 @@ impl CertificateParams {
601601
let der = subject_key.sign_der(|writer| {
602602
// Write version
603603
writer.next().write_u8(0);
604-
write_distinguished_name(writer.next(), distinguished_name);
604+
write_distinguished_name(writer.next(), distinguished_name.clone());
605605
serialize_public_key_der(subject_key, writer.next());
606606

607607
// According to the spec in RFC 2986, even if attributes are empty we need the empty attribute tag
@@ -663,7 +663,7 @@ impl CertificateParams {
663663
// Write signature algorithm
664664
issuer.key_pair.alg.write_alg_ident(writer.next());
665665
// Write issuer name
666-
write_distinguished_name(writer.next(), &issuer.distinguished_name);
666+
write_distinguished_name(writer.next(), issuer.distinguished_name.clone());
667667
// Write validity
668668
writer.next().write_sequence(|writer| {
669669
// Not before
@@ -673,7 +673,7 @@ impl CertificateParams {
673673
Ok::<(), Error>(())
674674
})?;
675675
// Write subject
676-
write_distinguished_name(writer.next(), &self.distinguished_name);
676+
write_distinguished_name(writer.next(), self.distinguished_name.clone());
677677
// Write subjectPublicKeyInfo
678678
serialize_public_key_der(pub_key, writer.next());
679679
// write extensions
@@ -856,7 +856,7 @@ fn write_general_subtrees(writer: DERWriter, tag: u64, general_subtrees: &[Gener
856856
GeneralSubtree::Rfc822Name(name)
857857
| GeneralSubtree::DnsName(name) => writer.write_ia5_string(name),
858858
GeneralSubtree::DirectoryName(name) => {
859-
write_distinguished_name(writer, name)
859+
write_distinguished_name(writer, name.clone())
860860
},
861861
GeneralSubtree::IpAddress(subnet) => {
862862
writer.write_bytes(&subnet.to_bytes())

rcgen/src/crl.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ impl CertificateRevocationListParams {
234234
// Write issuer.
235235
// RFC 5280 §5.1.2.3:
236236
// The issuer field MUST contain a non-empty X.500 distinguished name (DN).
237-
write_distinguished_name(writer.next(), &issuer.distinguished_name);
237+
write_distinguished_name(writer.next(), issuer.distinguished_name.clone());
238238

239239
// Write thisUpdate date.
240240
// RFC 5280 §5.1.2.4:

rcgen/src/lib.rs

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ println!("{}", key_pair.serialize_pem());
3333
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
3434
#![warn(unreachable_pub)]
3535

36-
use std::collections::HashMap;
36+
use std::collections::VecDeque;
3737
use std::fmt;
3838
use std::hash::Hash;
3939
use std::net::IpAddr;
@@ -299,8 +299,7 @@ See also the RFC 5280 sections on the [issuer](https://tools.ietf.org/html/rfc52
299299
and [subject](https://tools.ietf.org/html/rfc5280#section-4.1.2.6) fields.
300300
*/
301301
pub struct DistinguishedName {
302-
entries: HashMap<DnType, DnValue>,
303-
order: Vec<DnType>,
302+
entries: VecDeque<(DnType, DnValue)>,
304303
}
305304

306305
impl DistinguishedName {
@@ -309,20 +308,32 @@ impl DistinguishedName {
309308
Self::default()
310309
}
311310
/// Obtains the attribute value for the given attribute type
312-
pub fn get(&self, ty: &DnType) -> Option<&DnValue> {
313-
self.entries.get(ty)
311+
pub fn get(&self, ty: &DnType) -> Vec<&DnValue> {
312+
self.entries
313+
.iter()
314+
.filter_map(|(dn_type, dn_value)| if ty == dn_type { Some(dn_value) } else { None })
315+
.collect()
314316
}
315317
/// Removes the attribute with the specified DnType
316318
///
317319
/// Returns true when an actual removal happened, false
318320
/// when no attribute with the specified DnType was
319321
/// found.
320322
pub fn remove(&mut self, ty: DnType) -> bool {
321-
let removed = self.entries.remove(&ty).is_some();
322-
if removed {
323-
self.order.retain(|ty_o| &ty != ty_o);
323+
let mut remove_indices = vec![];
324+
for (index, (dn_type, _dn_val)) in self.entries.iter().enumerate() {
325+
if dn_type == &ty {
326+
remove_indices.push(index);
327+
}
328+
}
329+
330+
let is_remove_indices = !remove_indices.is_empty();
331+
332+
for index in remove_indices {
333+
self.entries.remove(index);
324334
}
325-
removed
335+
336+
is_remove_indices
326337
}
327338
/// Inserts or updates an attribute that consists of type and name
328339
///
@@ -331,21 +342,35 @@ impl DistinguishedName {
331342
/// let mut dn = DistinguishedName::new();
332343
/// dn.push(DnType::OrganizationName, "Crab widgits SE");
333344
/// dn.push(DnType::CommonName, DnValue::PrintableString("Master Cert".try_into().unwrap()));
334-
/// assert_eq!(dn.get(&DnType::OrganizationName), Some(&DnValue::Utf8String("Crab widgits SE".to_string())));
335-
/// assert_eq!(dn.get(&DnType::CommonName), Some(&DnValue::PrintableString("Master Cert".try_into().unwrap())));
345+
/// assert_eq!(dn.get(&DnType::OrganizationName).get(0), Some(&DnValue::Utf8String("Crab widgits SE".to_string())).as_ref());
346+
/// assert_eq!(dn.get(&DnType::CommonName).get(0), Some(&DnValue::PrintableString("Master Cert".try_into().unwrap())).as_ref());
336347
/// ```
337348
pub fn push(&mut self, ty: DnType, s: impl Into<DnValue>) {
338-
if !self.entries.contains_key(&ty) {
339-
self.order.push(ty.clone());
340-
}
341-
self.entries.insert(ty, s.into());
349+
self.entries.push_front((ty, s.into()));
342350
}
343-
/// Iterate over the entries
344-
pub fn iter(&self) -> DistinguishedNameIterator<'_> {
345-
DistinguishedNameIterator {
346-
distinguished_name: self,
347-
iter: self.order.iter(),
351+
352+
/// Replaces the *fist occurrence* of a type with a new value.
353+
/// This is a convenience function to avoid duplicating values.
354+
///
355+
/// If there are multiple occurrences of a type there is currently no way of changing the besides iterating over the types and values of an existing instance and creating a new instance.
356+
///
357+
/// ```
358+
/// # use rcgen::{DistinguishedName, DnType, DnValue};
359+
/// let mut dn = DistinguishedName::new();
360+
/// dn.push(DnType::CommonName, DnValue::PrintableString("Master Cert".try_into().unwrap()));
361+
/// assert_eq!(dn.get(&DnType::CommonName).get(0), Some(&DnValue::PrintableString("Master Cert".try_into().unwrap())).as_ref());
362+
/// dn.push(DnType::CommonName, DnValue::PrintableString("Other Master Cert".try_into().unwrap()));
363+
/// assert_eq!(dn.get(&DnType::CommonName).get(0), Some(&DnValue::PrintableString("Other Master Cert".try_into().unwrap())).as_ref());
364+
/// ```
365+
pub fn replace_or_push(&mut self, ty: DnType, s: impl Into<DnValue>) {
366+
for (dn_type, dn_value) in self.entries.iter_mut() {
367+
if *dn_type == ty {
368+
*dn_value = s.into();
369+
return;
370+
}
348371
}
372+
373+
self.push(ty, s)
349374
}
350375

351376
#[cfg(feature = "x509-parser")]
@@ -393,21 +418,15 @@ impl DistinguishedName {
393418
}
394419
}
395420

396-
/**
397-
Iterator over [`DistinguishedName`] entries
398-
*/
399-
pub struct DistinguishedNameIterator<'a> {
400-
distinguished_name: &'a DistinguishedName,
401-
iter: std::slice::Iter<'a, DnType>,
402-
}
403-
404-
impl<'a> Iterator for DistinguishedNameIterator<'a> {
405-
type Item = (&'a DnType, &'a DnValue);
421+
impl Iterator for DistinguishedName {
422+
type Item = (DnType, DnValue);
406423

407424
fn next(&mut self) -> Option<Self::Item> {
408-
self.iter
409-
.next()
410-
.and_then(|ty| self.distinguished_name.entries.get(ty).map(|v| (ty, v)))
425+
self.entries.pop_back()
426+
}
427+
428+
fn size_hint(&self) -> (usize, Option<usize>) {
429+
self.entries.iter().size_hint()
411430
}
412431
}
413432

@@ -568,9 +587,9 @@ fn write_dt_utc_or_generalized(writer: DERWriter, dt: OffsetDateTime) {
568587
}
569588
}
570589

571-
fn write_distinguished_name(writer: DERWriter, dn: &DistinguishedName) {
590+
fn write_distinguished_name(writer: DERWriter, dn: DistinguishedName) {
572591
writer.write_sequence(|writer| {
573-
for (ty, content) in dn.iter() {
592+
for (ty, content) in dn.into_iter() {
574593
writer.next().write_set(|writer| {
575594
writer.next().write_sequence(|writer| {
576595
writer.next().write_oid(&ty.to_oid());
@@ -596,7 +615,7 @@ fn write_distinguished_name(writer: DERWriter, dn: &DistinguishedName) {
596615
.write_tagged_implicit(TAG_UNIVERSALSTRING, |writer| {
597616
writer.write_bytes(s.as_bytes())
598617
}),
599-
DnValue::Utf8String(s) => writer.next().write_utf8_string(s),
618+
DnValue::Utf8String(s) => writer.next().write_utf8_string(s.as_str()),
600619
}
601620
});
602621
});

rcgen/tests/generic.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -374,11 +374,9 @@ mod test_parse_ia5string_subject {
374374
let params_from_cert = CertificateParams::from_ca_cert_der(cert_der).unwrap();
375375

376376
// We should find the expected distinguished name in the reconstituted params.
377-
let expected_names = &[(&email_address_dn_type, &email_address_dn_value)];
378-
let names = params_from_cert
379-
.distinguished_name
380-
.iter()
381-
.collect::<Vec<(_, _)>>();
377+
let expected_names = &[(email_address_dn_type, email_address_dn_value)];
378+
let names = params_from_cert.distinguished_name.collect::<Vec<(_, _)>>();
379+
382380
assert_eq!(names, expected_names);
383381
}
384382
}

rcgen/tests/openssl.rs

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
#![cfg(feature = "pem")]
22

3-
use std::cell::RefCell;
4-
use std::io::{Error, ErrorKind, Read, Result as ioResult, Write};
5-
use std::rc::Rc;
6-
73
use openssl::asn1::{Asn1Integer, Asn1Time};
84
use openssl::bn::BigNum;
95
use openssl::pkey::PKey;
106
use openssl::ssl::{HandshakeError, SslAcceptor, SslConnector, SslMethod};
117
use openssl::stack::Stack;
128
use openssl::x509::store::{X509Store, X509StoreBuilder};
139
use openssl::x509::{CrlStatus, X509Crl, X509Req, X509StoreContext, X509};
10+
use std::cell::RefCell;
11+
use std::io::{Error, ErrorKind, Read, Result as ioResult, Write};
12+
use std::rc::Rc;
1413

1514
use rcgen::{
1615
BasicConstraints, Certificate, CertificateParams, DnType, DnValue, GeneralSubtree, IsCa,
@@ -540,3 +539,65 @@ fn test_openssl_pkcs1_and_sec1_keys() {
540539
let pkcs8_ec_key_der = PrivateKeyDer::try_from(ec_key.private_key_to_pkcs8().unwrap()).unwrap();
541540
KeyPair::try_from(&pkcs8_ec_key_der).unwrap();
542541
}
542+
543+
#[test]
544+
#[cfg(feature = "x509-parser")]
545+
fn test_parse_certificate_with_multiple_domain_components() {
546+
use rcgen::Ia5String;
547+
use std::str::FromStr;
548+
549+
/// Command used to generate:
550+
/// `openssl req -x509 -newkey rsa:4096 -nodes -out mycert.pem -keyout mykey.pem -days 365 -subj "/C=US/ST=California/L=San Francisco/O=Example Company/OU=IT Department/CN=www.example.com/DC=example/DC=com"`
551+
/// Contains two distinct "DC" entries.
552+
const CERT_WITH_MULTI_DC: &str = r#"-----BEGIN CERTIFICATE-----
553+
MIIGSzCCBDOgAwIBAgIUECjoFzATY6rTCtu7HKjBtfXnB/owDQYJKoZIhvcNAQEL
554+
BQAwgbQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
555+
DA1TYW4gRnJhbmNpc2NvMRgwFgYDVQQKDA9FeGFtcGxlIENvbXBhbnkxFjAUBgNV
556+
BAsMDUlUIERlcGFydG1lbnQxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTEXMBUG
557+
CgmSJomT8ixkARkWB2V4YW1wbGUxEzARBgoJkiaJk/IsZAEZFgNjb20wHhcNMjQx
558+
MTIxMDkxNTE2WhcNMjUxMTIxMDkxNTE2WjCBtDELMAkGA1UEBhMCVVMxEzARBgNV
559+
BAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGDAWBgNVBAoM
560+
D0V4YW1wbGUgQ29tcGFueTEWMBQGA1UECwwNSVQgRGVwYXJ0bWVudDEYMBYGA1UE
561+
AwwPd3d3LmV4YW1wbGUuY29tMRcwFQYKCZImiZPyLGQBGRYHZXhhbXBsZTETMBEG
562+
CgmSJomT8ixkARkWA2NvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
563+
ANla4cBCTS+6JdEw6kVQHskanjtHbw7F25TZ2tZWC1f/UJuQnpF/JJqADdV6R3ta
564+
xjcGj2ubJnKS1npcdiVN6A95jYggbQqjfZV+Z0cxjL8L4dQ+UPDsNyP8W0+S6UnK
565+
+W813DG/QGXxEFrT8nZIfhyD4qZEtOSFGgp/ZA2f687Svx1+SKiutHeRovEf/OTb
566+
fK4NHhewa1IxiV7shYNy7hhJmDqcsRIhVfuiWn4TU++qB6JTiPATYmzFRALli7B6
567+
g5m8KhvWcdAssgb2+bNpbs3fTcytrqwiNnNYtZ5a7DV0WWH4+wfor7KlomPMviPg
568+
jiFwWWKW/N5dQ+f9vpo7SDOT9Jl26BWj0vJYTceLgkOGwYMXsg7pbWmPH4sL+GNv
569+
WpRG7fDmual98y4DFwD8vHp4Mvax2OWKxfxe6xPqdn7or7D3ZSSyBu//ZlhQ6yMd
570+
F6tLTl2/5VcWdJy0W+FDEnZIHnPm3zyCiplEP4bxY2Blpdnqf5Cx80mz8YSQhddn
571+
gVNrM7iaNnIvRLjFS88w4KMOKbYSPbxEt2eWO4ggVcn1akcifDFTpyInRKQxQkXa
572+
SXH/iu2dm7kuyGwSwrIW1l41vUkT+Lsm/9TFQ3a+UWWzut4oux9oGmcuUP5EiUZb
573+
rWw2GIP2DaluKsZNUh8QIWVccBmX6AaKw3+K0r/tFqShAgMBAAGjUzBRMB0GA1Ud
574+
DgQWBBTru/FFL1lBGB6d1a1xe3Tn3wV/RzAfBgNVHSMEGDAWgBTru/FFL1lBGB6d
575+
1a1xe3Tn3wV/RzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQCY
576+
dKu+CYxHb7drJqDhfMXUq2ogZiYI0fYyPEf+rpNpM5A8C0PyG9Um7WZwKAfp38IE
577+
a/McBidxI7TuNq9ojruIyY79LCThz64Z1Nc5rb3sthcygZLd1t98Zh8vaG07kk7s
578+
n2/BMLgHPvm47cUJ1VaQpLwx2tSBaFB+Osroq0ZXMqyO6s7Gyk+hrI+l6b+gqryA
579+
b8kHzbeslxPK6QkDz9Kt+qPkZVRgfKgyqyd0YGoe1LaAwctMdrTPZRzkFRDLYDls
580+
JK/PFi027oljJJzFZ07k9c8WJBeM3xiIHFlxIJ5XehVpLLFEhxX1ypnvku7GeINq
581+
I9356ueSmMPn1BIsLonTOYR3k1hue+giO5AiD6J3yl8OhJStouG3FOZbB5dDRae+
582+
9bdhU4npsmKTmBX/CDUFYJl4yqavEGfvw40p77gaqIOShEBB54ASKDaSyuLSeYbi
583+
3TQsa+JyWmJ5iNmqVsAy8YfioKveNmyl023hRTjtqJgKQY1UzY6M0bnHa0IlgZq/
584+
l4A7hDDsvi3rDFiqvKg/WTEZd5G87E9hwIcHF/bJPc+0+MjelRoxFTSty2bpbniR
585+
p3mmtsYxi+XCHdwUwRLhbBrdu93z5Iy3AWIb7vGeTKznnnDweJzYpfHCXuWZdr/d
586+
z6cbmudPzN1l99Op5eH9i1JikA+DQ8BQv1OgkNBw2A==
587+
-----END CERTIFICATE-----
588+
"#;
589+
590+
let param = CertificateParams::from_ca_cert_pem(CERT_WITH_MULTI_DC).unwrap();
591+
592+
let domain_component_values = param.distinguished_name.get(&DnType::CustomDnType(vec![
593+
0, 9, 2342, 19200300, 100, 1, 25,
594+
]));
595+
596+
assert_eq!(
597+
domain_component_values,
598+
vec![
599+
&DnValue::Ia5String(Ia5String::from_str("com").unwrap()),
600+
&DnValue::Ia5String(Ia5String::from_str("example").unwrap()),
601+
]
602+
)
603+
}

0 commit comments

Comments
 (0)