diff --git a/Cargo.toml b/Cargo.toml index 8bce873b..a684517f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,9 @@ version = "0.11.3" description = "Rust X.509 certificate generator" repository = "https://github.com/rustls/rcgen" keywords = ["mkcert", "ca", "certificate"] + +# This greatly speeds up rsa key generation times +# (only applies to the dev-dependency of rcgen because cargo +# ignores profile overrides for non leaf packages) +[profile.dev.package.num-bigint-dig] +opt-level = 3 diff --git a/rcgen/Cargo.toml b/rcgen/Cargo.toml index 45d98cd0..07b5ca45 100644 --- a/rcgen/Cargo.toml +++ b/rcgen/Cargo.toml @@ -42,9 +42,3 @@ rustls-webpki = { version = "0.101.0", features = ["std"] } botan = { version = "0.10", features = ["vendored"] } rand = "0.8" rsa = "0.9" - -# This greatly speeds up rsa key generation times -# (only applies to the dev-dependency because cargo -# ignores profile overrides for non leaf packages) -[profile.dev.package.num-bigint-dig] -opt-level = 3 diff --git a/rcgen/src/lib.rs b/rcgen/src/lib.rs index 94d2639f..113e2318 100644 --- a/rcgen/src/lib.rs +++ b/rcgen/src/lib.rs @@ -361,16 +361,18 @@ impl DnType { #[derive(Debug, PartialEq, Eq, Hash, Clone)] #[non_exhaustive] pub enum DnValue { - /// A string of characters from the T.61 character set - TeletexString(Vec), + /// A string encoded using UCS-2 + BmpString(Vec), + /// An ASCII string. + Ia5String(String), /// An ASCII string containing only A-Z, a-z, 0-9, '()+,-./:=? and `` PrintableString(String), + /// A string of characters from the T.61 character set + TeletexString(Vec), /// A string encoded using UTF-32 UniversalString(Vec), /// A string encoded using UTF-8 Utf8String(String), - /// A string encoded using UCS-2 - BmpString(Vec), } impl From for DnValue @@ -471,20 +473,15 @@ impl DistinguishedName { .ok_or(Error::CouldNotParseCertificate)?; let dn_type = DnType::from_oid(&attr_type_oid.collect::>()); let data = attr.attr_value().data; + let try_str = + |data| std::str::from_utf8(data).map_err(|_| Error::CouldNotParseCertificate); let dn_value = match attr.attr_value().header.tag() { + Tag::BmpString => DnValue::BmpString(data.into()), + Tag::Ia5String => DnValue::Ia5String(try_str(data)?.to_owned()), + Tag::PrintableString => DnValue::PrintableString(try_str(data)?.to_owned()), Tag::T61String => DnValue::TeletexString(data.into()), - Tag::PrintableString => { - let data = - std::str::from_utf8(data).map_err(|_| Error::CouldNotParseCertificate)?; - DnValue::PrintableString(data.to_owned()) - }, Tag::UniversalString => DnValue::UniversalString(data.into()), - Tag::Utf8String => { - let data = - std::str::from_utf8(data).map_err(|_| Error::CouldNotParseCertificate)?; - DnValue::Utf8String(data.to_owned()) - }, - Tag::BmpString => DnValue::BmpString(data.into()), + Tag::Utf8String => DnValue::Utf8String(try_str(data)?.to_owned()), _ => return Err(Error::CouldNotParseCertificate), }; @@ -879,36 +876,7 @@ impl CertificateParams { // Write version writer.next().write_u8(0); // Write issuer - writer.next().write_sequence(|writer| { - for (ty, content) in distinguished_name.iter() { - writer.next().write_set(|writer| { - writer.next().write_sequence(|writer| { - writer.next().write_oid(&ty.to_oid()); - match content { - DnValue::TeletexString(s) => writer - .next() - .write_tagged_implicit(TAG_TELETEXSTRING, |writer| { - writer.write_bytes(s) - }), - DnValue::PrintableString(s) => { - writer.next().write_printable_string(s) - }, - DnValue::UniversalString(s) => writer - .next() - .write_tagged_implicit(TAG_UNIVERSALSTRING, |writer| { - writer.write_bytes(s) - }), - DnValue::Utf8String(s) => writer.next().write_utf8_string(s), - DnValue::BmpString(s) => writer - .next() - .write_tagged_implicit(TAG_BMPSTRING, |writer| { - writer.write_bytes(s) - }), - } - }); - }); - } - }); + write_distinguished_name(writer.next(), &distinguished_name); // Write subjectPublicKeyInfo pub_key.serialize_public_key_der(writer.next()); // Write extensions @@ -1440,21 +1408,22 @@ fn write_distinguished_name(writer: DERWriter, dn: &DistinguishedName) { writer.next().write_sequence(|writer| { writer.next().write_oid(&ty.to_oid()); match content { + DnValue::BmpString(s) => writer + .next() + .write_tagged_implicit(TAG_BMPSTRING, |writer| writer.write_bytes(s)), + DnValue::Ia5String(s) => writer.next().write_ia5_string(s), + DnValue::PrintableString(s) => writer.next().write_printable_string(s), DnValue::TeletexString(s) => writer .next() .write_tagged_implicit(TAG_TELETEXSTRING, |writer| { writer.write_bytes(s) }), - DnValue::PrintableString(s) => writer.next().write_printable_string(s), DnValue::UniversalString(s) => writer .next() .write_tagged_implicit(TAG_UNIVERSALSTRING, |writer| { writer.write_bytes(s) }), DnValue::Utf8String(s) => writer.next().write_utf8_string(s), - DnValue::BmpString(s) => writer - .next() - .write_tagged_implicit(TAG_BMPSTRING, |writer| writer.write_bytes(s)), } }); }); diff --git a/rcgen/tests/generic.rs b/rcgen/tests/generic.rs index 8b693415..9125c23c 100644 --- a/rcgen/tests/generic.rs +++ b/rcgen/tests/generic.rs @@ -299,3 +299,40 @@ mod test_parse_crl_dps { ); } } + +#[cfg(feature = "x509-parser")] +mod test_parse_ia5string_subject { + use crate::util; + use rcgen::DnType::CustomDnType; + use rcgen::{Certificate, CertificateParams, DistinguishedName, DnValue, KeyPair}; + + #[test] + fn parse_ia5string_subject() { + // Create and serialize a certificate with a subject containing an IA5String email address. + let email_address_dn_type = CustomDnType(vec![1, 2, 840, 113549, 1, 9, 1]); // id-emailAddress + let email_address_dn_value = DnValue::Ia5String("foo@bar.com".into()); + let mut params = util::default_params(); + params.distinguished_name = DistinguishedName::new(); + params.distinguished_name.push( + email_address_dn_type.clone(), + email_address_dn_value.clone(), + ); + let cert = Certificate::from_params(params).unwrap(); + let cert_der = cert.serialize_der().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 key_pair = KeyPair::generate(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap(); + let params_from_cert = CertificateParams::from_ca_cert_der(&cert_der, key_pair).unwrap(); + + // We should find the expected distinguished name in the reconstituted params. + let expected_names = &[(&email_address_dn_type, &email_address_dn_value)]; + let names = params_from_cert + .distinguished_name + .iter() + .collect::>(); + assert_eq!(names, expected_names); + } +}