Skip to content

Commit 062af97

Browse files
committed
Allow multiple issuer items of the same kind
There can be multiple of the same entry, e.g. DC (domain component) entries in an issuer.
1 parent 12d6533 commit 062af97

File tree

4 files changed

+121
-33
lines changed

4 files changed

+121
-33
lines changed

rcgen/src/certificate.rs

+4-4
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

+1-1
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

+51-23
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ 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;
3736
use std::fmt;
3837
use std::hash::Hash;
3938
use std::net::IpAddr;
@@ -299,8 +298,7 @@ See also the RFC 5280 sections on the [issuer](https://tools.ietf.org/html/rfc52
299298
and [subject](https://tools.ietf.org/html/rfc5280#section-4.1.2.6) fields.
300299
*/
301300
pub struct DistinguishedName {
302-
entries: HashMap<DnType, DnValue>,
303-
order: Vec<DnType>,
301+
entries: Vec<(DnType, DnValue)>,
304302
}
305303

306304
impl DistinguishedName {
@@ -309,20 +307,32 @@ impl DistinguishedName {
309307
Self::default()
310308
}
311309
/// Obtains the attribute value for the given attribute type
312-
pub fn get(&self, ty: &DnType) -> Option<&DnValue> {
313-
self.entries.get(ty)
310+
pub fn get(&self, ty: &DnType) -> Vec<&DnValue> {
311+
self.entries
312+
.iter()
313+
.filter_map(|(dn_type, dn_value)| if ty == dn_type { Some(dn_value) } else { None })
314+
.collect()
314315
}
315316
/// Removes the attribute with the specified DnType
316317
///
317318
/// Returns true when an actual removal happened, false
318319
/// when no attribute with the specified DnType was
319320
/// found.
320321
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);
322+
let mut remove_indices = vec![];
323+
for (index, (dn_type, _dn_val)) in self.entries.iter().enumerate() {
324+
if dn_type == &ty {
325+
remove_indices.push(index);
326+
}
324327
}
325-
removed
328+
329+
let is_remove_indices = !remove_indices.is_empty();
330+
331+
for index in remove_indices {
332+
self.entries.remove(index);
333+
}
334+
335+
is_remove_indices
326336
}
327337
/// Inserts or updates an attribute that consists of type and name
328338
///
@@ -331,20 +341,41 @@ impl DistinguishedName {
331341
/// let mut dn = DistinguishedName::new();
332342
/// dn.push(DnType::OrganizationName, "Crab widgits SE");
333343
/// 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())));
344+
/// assert_eq!(dn.get(&DnType::OrganizationName).get(0), Some(&DnValue::Utf8String("Crab widgits SE".to_string())).as_ref());
345+
/// assert_eq!(dn.get(&DnType::CommonName).get(0), Some(&DnValue::PrintableString("Master Cert".try_into().unwrap())).as_ref());
336346
/// ```
337347
pub fn push(&mut self, ty: DnType, s: impl Into<DnValue>) {
338-
if !self.entries.contains_key(&ty) {
339-
self.order.push(ty.clone());
348+
self.entries.push((ty, s.into()));
349+
}
350+
351+
/// Replaces the *fist occurrence* of a type with a new value.
352+
/// This is a convenience function to avoid duplicating values.
353+
///
354+
/// 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.
355+
///
356+
/// ```
357+
/// # use rcgen::{DistinguishedName, DnType, DnValue};
358+
/// let mut dn = DistinguishedName::new();
359+
/// dn.push(DnType::CommonName, DnValue::PrintableString("Master Cert".try_into().unwrap()));
360+
/// assert_eq!(dn.get(&DnType::CommonName).get(0), Some(&DnValue::PrintableString("Master Cert".try_into().unwrap())).as_ref());
361+
/// dn.push(DnType::CommonName, DnValue::PrintableString("Other Master Cert".try_into().unwrap()));
362+
/// assert_eq!(dn.get(&DnType::CommonName).get(1), Some(&DnValue::PrintableString("Other Master Cert".try_into().unwrap())).as_ref());
363+
/// ```
364+
pub fn replace_or_push(&mut self, ty: DnType, s: impl Into<DnValue>) {
365+
for (dn_type, dn_value) in self.entries.iter_mut() {
366+
if *dn_type == ty {
367+
*dn_value = s.into();
368+
return;
369+
}
340370
}
341-
self.entries.insert(ty, s.into());
371+
372+
self.push(ty, s)
342373
}
374+
343375
/// Iterate over the entries
344376
pub fn iter(&self) -> DistinguishedNameIterator<'_> {
345377
DistinguishedNameIterator {
346-
distinguished_name: self,
347-
iter: self.order.iter(),
378+
iter: self.entries.iter(),
348379
}
349380
}
350381

@@ -397,17 +428,14 @@ impl DistinguishedName {
397428
Iterator over [`DistinguishedName`] entries
398429
*/
399430
pub struct DistinguishedNameIterator<'a> {
400-
distinguished_name: &'a DistinguishedName,
401-
iter: std::slice::Iter<'a, DnType>,
431+
iter: std::slice::Iter<'a, (DnType, DnValue)>,
402432
}
403433

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

407437
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)))
438+
self.iter.next().map(|(key, value)| (key, value))
411439
}
412440
}
413441

@@ -568,7 +596,7 @@ fn write_dt_utc_or_generalized(writer: DERWriter, dt: OffsetDateTime) {
568596
}
569597
}
570598

571-
fn write_distinguished_name(writer: DERWriter, dn: &DistinguishedName) {
599+
fn write_distinguished_name(writer: DERWriter, dn: DistinguishedName) {
572600
writer.write_sequence(|writer| {
573601
for (ty, content) in dn.iter() {
574602
writer.next().write_set(|writer| {
@@ -596,7 +624,7 @@ fn write_distinguished_name(writer: DERWriter, dn: &DistinguishedName) {
596624
.write_tagged_implicit(TAG_UNIVERSALSTRING, |writer| {
597625
writer.write_bytes(s.as_bytes())
598626
}),
599-
DnValue::Utf8String(s) => writer.next().write_utf8_string(s),
627+
DnValue::Utf8String(s) => writer.next().write_utf8_string(s.as_str()),
600628
}
601629
});
602630
});

rcgen/tests/openssl.rs

+65-5
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
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};
14-
1510
use rcgen::{
1611
BasicConstraints, Certificate, CertificateParams, DnType, DnValue, GeneralSubtree, IsCa,
1712
KeyPair, NameConstraints,
1813
};
14+
use std::cell::RefCell;
15+
use std::io::{Error, ErrorKind, Read, Result as ioResult, Write};
16+
use std::rc::Rc;
1917

2018
mod util;
2119

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

0 commit comments

Comments
 (0)