diff --git a/rcgen/src/certificate.rs b/rcgen/src/certificate.rs index e34abd3f..ce34495e 100644 --- a/rcgen/src/certificate.rs +++ b/rcgen/src/certificate.rs @@ -634,21 +634,19 @@ fn write_general_subtrees(writer: DERWriter, tag: u64, general_subtrees: &[Gener writer.write_sequence(|writer| { for subtree in general_subtrees.iter() { writer.next().write_sequence(|writer| { - writer - .next() - .write_tagged_implicit( - Tag::context(subtree.tag()), - |writer| match subtree { - GeneralSubtree::Rfc822Name(name) - | GeneralSubtree::DnsName(name) => writer.write_ia5_string(name), - GeneralSubtree::DirectoryName(name) => { - write_distinguished_name(writer, name) - }, - GeneralSubtree::IpAddress(subnet) => { - writer.write_bytes(&subnet.to_bytes()) - }, - }, - ); + let writer = writer.next(); + let tag = Tag::context(subtree.tag()); + match subtree { + GeneralSubtree::Rfc822Name(name) | GeneralSubtree::DnsName(name) => writer + .write_tagged_implicit(tag, |writer| writer.write_ia5_string(name)), + // `Name` is a CHOICE, so X.680 ยง31.2.7 requires explicit tagging. + GeneralSubtree::DirectoryName(name) => writer + .write_tagged(tag, |writer| write_distinguished_name(writer, name)), + GeneralSubtree::IpAddress(subnet) => writer + .write_tagged_implicit(tag, |writer| { + writer.write_bytes(&subnet.to_bytes()) + }), + } // minimum must be 0 (the default) and maximum must be absent }); } diff --git a/verify-tests/tests/openssl.rs b/verify-tests/tests/openssl.rs index a99cad49..bf736794 100644 --- a/verify-tests/tests/openssl.rs +++ b/verify-tests/tests/openssl.rs @@ -12,8 +12,8 @@ use openssl::stack::Stack; use openssl::x509::store::{X509Store, X509StoreBuilder}; use openssl::x509::{CrlStatus, X509Crl, X509Req, X509StoreContext, X509}; use rcgen::{ - BasicConstraints, Certificate, CertificateParams, DnType, DnValue, GeneralSubtree, IsCa, - Issuer, KeyPair, NameConstraints, + BasicConstraints, Certificate, CertificateParams, DistinguishedName, DnType, DnValue, + GeneralSubtree, IsCa, Issuer, KeyPair, NameConstraints, }; use verify_tests as util; @@ -404,6 +404,39 @@ fn test_openssl_separate_ca_name_constraints() { verify_cert_ca(&cert.pem(), &key, &ca_cert.pem()); } +#[test] +fn test_openssl_separate_ca_name_constraints_directory_name() { + let (mut ca_params, ca_key) = util::default_params(); + ca_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); + + let mut permitted = DistinguishedName::new(); + permitted.push(DnType::OrganizationName, "Crab widgits SE"); + + ca_params.name_constraints = Some(NameConstraints { + permitted_subtrees: vec![ + GeneralSubtree::DnsName("crabs.crabs".to_string()), + GeneralSubtree::DirectoryName(permitted), + ], + excluded_subtrees: Vec::new(), + }); + let ca_cert = ca_params.self_signed(&ca_key).unwrap(); + let ca = Issuer::new(ca_params, ca_key); + + let mut params = CertificateParams::new(vec!["crabs.crabs".to_string()]).unwrap(); + params.distinguished_name = DistinguishedName::new(); + params + .distinguished_name + .push(DnType::OrganizationName, "Crab widgits SE"); + params + .distinguished_name + .push(DnType::CommonName, "Dev domain"); + let cert_key = KeyPair::generate().unwrap(); + let cert = params.signed_by(&cert_key, &ca).unwrap(); + let key = cert_key.serialize_der(); + + verify_cert_ca(&cert.pem(), &key, &ca_cert.pem()); +} + #[test] fn test_openssl_crl_parse() { // Create a CRL with one revoked cert, and an issuer to sign the CRL.