diff --git a/src/lib.rs b/src/lib.rs index 4bc6a33a..55c4a9b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -826,14 +826,14 @@ impl CertificateParams { serial_number, subject_alt_names: _, distinguished_name, - is_ca, - key_usages, - extended_key_usages, - name_constraints, - crl_distribution_points, + is_ca: _, + key_usages: _, + extended_key_usages: _, + name_constraints: _, + crl_distribution_points: _, custom_extensions: _, key_pair, - use_authority_key_identifier_extension, + use_authority_key_identifier_extension: _, key_identifier_method, } = self; // - alg and key_pair will be used by the caller @@ -843,14 +843,7 @@ impl CertificateParams { // from the defaults so this is left for a later version if // needed. let _ = (alg, key_pair, not_before, not_after, key_identifier_method); - if serial_number.is_some() - || *is_ca != IsCa::NoCa - || !key_usages.is_empty() - || !extended_key_usages.is_empty() - || name_constraints.is_some() - || !crl_distribution_points.is_empty() - || *use_authority_key_identifier_extension - { + if serial_number.is_some() { return Err(RcgenError::UnsupportedInCsr); } writer.write_sequence(|writer| { diff --git a/tests/generic.rs b/tests/generic.rs index 2b158c7c..08dd02b5 100644 --- a/tests/generic.rs +++ b/tests/generic.rs @@ -84,6 +84,249 @@ mod test_convert_x509_subject_alternative_name { } } +#[cfg(feature = "x509-parser")] +mod test_csr_exts { + use crate::util; + use rcgen::{ + BasicConstraints, Certificate, CrlDistributionPoint, GeneralSubtree, KeyUsagePurpose, + SanType, + }; + use x509_parser::prelude::{ + FromDer, ParsedExtension, X509Certificate, X509CertificationRequest, + }; + + #[test] + fn test_rcgen_extensions() { + // Create a certificate that has several rcgen managed extensions (e.g. not custom extensions). + let mut params = util::default_params(); + params.subject_alt_names = vec![SanType::DnsName("san.example.com".into())]; + let path_len_constraint = 3; + params.is_ca = rcgen::IsCa::Ca(BasicConstraints::Constrained(path_len_constraint)); + params.key_usages = vec![ + KeyUsagePurpose::DigitalSignature, + KeyUsagePurpose::KeyEncipherment, + ]; + params.extended_key_usages = vec![ + rcgen::ExtendedKeyUsagePurpose::ServerAuth, + rcgen::ExtendedKeyUsagePurpose::ClientAuth, + ]; + params.name_constraints = Some(rcgen::NameConstraints { + permitted_subtrees: vec![GeneralSubtree::DnsName("example.com".into())], + excluded_subtrees: vec![GeneralSubtree::DnsName("example.org".into())], + }); + params.crl_distribution_points = vec![CrlDistributionPoint { + uris: vec!["http://example.com".into()], + }]; + let test_cert = Certificate::from_params(params).unwrap(); + let test_cert_der = test_cert.serialize_der().unwrap(); + + // We should be able to parse it and find the expected extensions present. + let (_, x509_test_cert) = X509Certificate::from_der(&test_cert_der).unwrap(); + + // Check SAN is present, not critical, and not empty. + let san_ext = x509_test_cert + .subject_alternative_name() + .expect("invalid SAN ext") + .expect("missing SAN ext"); + assert!(!san_ext.critical); + assert!(!san_ext.value.general_names.is_empty()); + + // Check basic constraints are present, critical, and have the expected path len constraint. + let basic_constraints = x509_test_cert + .basic_constraints() + .expect("invalid basic constraints") + .expect("missing basic constraints"); + assert!(basic_constraints.critical); + let assert_basic_constraints = + |basic_constraints: &x509_parser::prelude::BasicConstraints| { + assert!(basic_constraints.ca); + assert_eq!( + basic_constraints + .path_len_constraint + .expect("missing path len constraint"), + path_len_constraint as u32 + ); + }; + assert_basic_constraints(basic_constraints.value); + + // Check name constraints are present, critical and non-empty. + let name_constraints = x509_test_cert + .name_constraints() + .expect("invalid name constraints") + .expect("missing name constraints"); + assert!(name_constraints.critical); + let assert_name_constraints = |name_constraints: &x509_parser::prelude::NameConstraints| { + assert!(!name_constraints + .permitted_subtrees + .clone() + .expect("missing permitted subtrees") + .is_empty()); + assert!(!name_constraints + .excluded_subtrees + .clone() + .expect("missing excluded subtrees") + .is_empty()); + }; + assert_name_constraints(name_constraints.value); + + // Check CRL distribution point is present and non-critical. + let crl_distrib_point = x509_test_cert + .get_extension_unique(&x509_parser::oid_registry::OID_X509_EXT_CRL_DISTRIBUTION_POINTS) + .expect("malformed CRL distribution points extension") + .expect("missing CRL distribution points extension"); + assert!(!crl_distrib_point.critical); + // See parse_crl_dps for a test of the ext structure/value. + + // Check key usage is present, and critical. + let key_usage = x509_test_cert + .key_usage() + .expect("invalid key usage") + .expect("missing key usage"); + assert!(key_usage.critical); + + // Check extended key usage is present, and non-critical. + let extended_key_usage = x509_test_cert + .extended_key_usage() + .expect("invalid extended key usage") + .expect("missing extended key usage"); + assert!(!extended_key_usage.critical); + + // Check subject key identifier is present, non-critical, and matches the expected value. + let ski = x509_test_cert + .get_extension_unique(&x509_parser::oid_registry::OID_X509_EXT_SUBJECT_KEY_IDENTIFIER) + .expect("invalid SKI extension") + .expect("missing SKI extension"); + assert!(!ski.critical); + let ski = match ski.parsed_extension() { + ParsedExtension::SubjectKeyIdentifier(key_id) => key_id, + _ => panic!("unexpected parsed extension type"), + }; + assert_eq!(ski.0, &test_cert.get_key_identifier()); + + let aki = x509_test_cert + .get_extension_unique(&x509_parser::oid_registry::OID_X509_EXT_AUTHORITY_KEY_IDENTIFIER) + .expect("invalid AKI extension") + .expect("missing AKI extension"); + assert!(!aki.critical); + let aki = match aki.parsed_extension() { + ParsedExtension::AuthorityKeyIdentifier(key_id) => key_id, + _ => panic!("unexpected parsed extension type"), + }; + assert_eq!( + aki.key_identifier + .clone() + .expect("missing key identifier") + .0, + &test_cert.get_key_identifier() + ); + + // Generate a CSR from our cert, parse it with x509-parser. + let test_cert_csr_der = test_cert.serialize_request_der(); + let test_cert_csr_der = test_cert_csr_der.unwrap(); + let (_, x509_csr) = X509CertificationRequest::from_der(&test_cert_csr_der).unwrap(); + + let parsed_exts = x509_csr + .requested_extensions() + .expect("missing CSR requested extensions") + .collect::>(); + + // The basic constraints we specified should be there. + let basic_constraints = parsed_exts + .iter() + .find_map(|ext| { + if let ParsedExtension::BasicConstraints(bc) = ext { + Some(bc) + } else { + None + } + }) + .expect("missing basic constraints"); + assert_basic_constraints(basic_constraints); + + let ski = parsed_exts + .iter() + .find_map(|ext| { + if let ParsedExtension::SubjectKeyIdentifier(ski) = ext { + Some(ski) + } else { + None + } + }) + .expect("missing subject key identifier"); + assert_eq!(ski.0, &test_cert.get_key_identifier()); + + parsed_exts + .iter() + .find_map(|ext| { + if let ParsedExtension::CRLDistributionPoints(ski) = ext { + Some(ski) + } else { + None + } + }) + .expect("missing CRL distribution points"); + + let name_constraints = parsed_exts + .iter() + .find_map(|ext| { + if let ParsedExtension::NameConstraints(name_constraints) = ext { + Some(name_constraints) + } else { + None + } + }) + .expect("missing name constraints"); + assert_name_constraints(name_constraints); + + parsed_exts + .iter() + .find_map(|ext| { + if let ParsedExtension::ExtendedKeyUsage(eku) = ext { + Some(eku) + } else { + None + } + }) + .expect("missing extended key usage"); + + parsed_exts + .iter() + .find_map(|ext| { + if let ParsedExtension::KeyUsage(ku) = ext { + Some(ku) + } else { + None + } + }) + .expect("missing key usage"); + + parsed_exts + .iter() + .find_map(|ext| { + if let ParsedExtension::SubjectAlternativeName(san) = ext { + Some(san) + } else { + None + } + }) + .expect("missing subject alternative name"); + + // We should not find the AKI extension in the CSR. That's provided by an issuer + // when issuing the certificate (e.g. as we did above w/ serialize_der making a + // self signed cert that contained an AKI). + assert_eq!( + parsed_exts.iter().find_map(|ext| { + if let ParsedExtension::AuthorityKeyIdentifier(aki) = ext { + Some(aki) + } else { + None + } + }), + None + ); + } +} + #[cfg(feature = "x509-parser")] mod test_x509_custom_ext { use crate::util;