#![cfg(all(feature = "builder", feature = "pem"))] use der::{asn1::PrintableString, pem::LineEnding, Decode, Encode, EncodePem}; use p256::{ecdsa::DerSignature, pkcs8::DecodePrivateKey, NistP256}; use rsa::pkcs1::DecodeRsaPrivateKey; use rsa::pkcs1v15::SigningKey; use sha2::Sha256; use spki::SubjectPublicKeyInfoOwned; use std::{str::FromStr, time::Duration}; use x509_cert::{ builder::{Builder, CertificateBuilder, Profile, RequestBuilder}, ext::pkix::{ name::{DirectoryString, GeneralName}, SubjectAltName, }, name::Name, request, serial_number::SerialNumber, time::Validity, }; use x509_cert_test_support::{openssl, zlint}; const RSA_2048_DER_EXAMPLE: &[u8] = include_bytes!("examples/rsa2048-pub.der"); const PKCS8_PUBLIC_KEY_DER: &[u8] = include_bytes!("examples/p256-pub.der"); #[test] fn root_ca_certificate() { let serial_number = SerialNumber::from(42u32); let validity = Validity::from_now(Duration::new(5, 0)).unwrap(); let profile = Profile::Root; let subject = Name::from_str("CN=World domination corporation,O=World domination Inc,C=US") .unwrap() .to_der() .unwrap(); let subject = Name::from_der(&subject).unwrap(); let pub_key = SubjectPublicKeyInfoOwned::try_from(RSA_2048_DER_EXAMPLE).expect("get rsa pub key"); let signer = rsa_signer(); let builder = CertificateBuilder::new(profile, serial_number, validity, subject, pub_key, &signer) .expect("Create certificate"); let certificate = builder.build().unwrap(); let pem = certificate.to_pem(LineEnding::LF).expect("generate pem"); println!("{}", openssl::check_certificate(pem.as_bytes())); let ignored = &[]; zlint::check_certificate(pem.as_bytes(), ignored); } #[test] fn root_ca_certificate_ecdsa() { let serial_number = SerialNumber::from(42u32); let validity = Validity::from_now(Duration::new(5, 0)).unwrap(); let profile = Profile::Root; let subject = Name::from_str("CN=World domination corporation,O=World domination Inc,C=US") .unwrap() .to_der() .unwrap(); let subject = Name::from_der(&subject).unwrap(); let pub_key = SubjectPublicKeyInfoOwned::try_from(PKCS8_PUBLIC_KEY_DER).expect("get ecdsa pub key"); let signer = ecdsa_signer(); let builder = CertificateBuilder::new(profile, serial_number, validity, subject, pub_key, &signer) .expect("Create certificate"); let certificate = builder.build::().unwrap(); let pem = certificate.to_pem(LineEnding::LF).expect("generate pem"); println!("{}", openssl::check_certificate(pem.as_bytes())); let ignored = &[]; zlint::check_certificate(pem.as_bytes(), ignored); } #[test] fn sub_ca_certificate() { let serial_number = SerialNumber::from(42u32); let validity = Validity::from_now(Duration::new(5, 0)).unwrap(); let issuer = Name::from_str("CN=World domination corporation,O=World domination Inc,C=US").unwrap(); let profile = Profile::SubCA { issuer, path_len_constraint: Some(0), }; let subject = Name::from_str("CN=World domination task force,O=World domination Inc,C=US").unwrap(); let pub_key = SubjectPublicKeyInfoOwned::try_from(RSA_2048_DER_EXAMPLE).expect("get rsa pub key"); let signer = ecdsa_signer(); let builder = CertificateBuilder::new(profile, serial_number, validity, subject, pub_key, &signer) .expect("Create certificate"); let certificate = builder.build::().unwrap(); let pem = certificate.to_pem(LineEnding::LF).expect("generate pem"); println!("{}", openssl::check_certificate(pem.as_bytes())); // TODO(baloo): not too sure we should tackle those in this API. let ignored = &[ "w_sub_ca_aia_missing", "e_sub_ca_crl_distribution_points_missing", "e_sub_ca_certificate_policies_missing", "w_sub_ca_aia_does_not_contain_issuing_ca_url", ]; zlint::check_certificate(pem.as_bytes(), ignored); } #[test] fn leaf_certificate() { let serial_number = SerialNumber::from(42u32); let validity = Validity::from_now(Duration::new(5, 0)).unwrap(); let issuer = Name::from_str("CN=World domination corporation,O=World domination Inc,C=US").unwrap(); let profile = Profile::Leaf { issuer: issuer.clone(), enable_key_agreement: false, enable_key_encipherment: false, #[cfg(feature = "hazmat")] include_subject_key_identifier: true, }; let subject = Name::from_str("CN=service.domination.world").unwrap(); let pub_key = SubjectPublicKeyInfoOwned::try_from(RSA_2048_DER_EXAMPLE).expect("get rsa pub key"); let signer = ecdsa_signer(); let builder = CertificateBuilder::new( profile, serial_number.clone(), validity.clone(), subject.clone(), pub_key.clone(), &signer, ) .expect("Create certificate"); let certificate = builder.build::().unwrap(); let pem = certificate.to_pem(LineEnding::LF).expect("generate pem"); println!("{}", openssl::check_certificate(pem.as_bytes())); // TODO(baloo): not too sure we should tackle those in this API. let ignored = vec![ "e_sub_cert_aia_missing", "e_sub_cert_crl_distribution_points_missing", "w_sub_cert_aia_does_not_contain_issuing_ca_url", // Missing policies "e_sub_cert_certificate_policies_missing", "e_sub_cert_cert_policy_empty", // Needs to be added by the end-user "e_sub_cert_aia_does_not_contain_ocsp_url", // SAN needs to include DNS name (if used) "e_ext_san_missing", "e_subject_common_name_not_exactly_from_san", // Extended key usage needs to be added by end-user and is use-case dependent "e_sub_cert_eku_missing", ]; zlint::check_certificate(pem.as_bytes(), &ignored); #[cfg(feature = "hazmat")] { let profile = Profile::Leaf { issuer, enable_key_agreement: false, enable_key_encipherment: false, include_subject_key_identifier: false, }; let builder = CertificateBuilder::new(profile, serial_number, validity, subject, pub_key, &signer) .expect("Create certificate"); let certificate = builder.build::().unwrap(); let pem = certificate.to_pem(LineEnding::LF).expect("generate pem"); println!("{}", openssl::check_certificate(pem.as_bytes())); // Ignore warning about leaf not having SKI extension (this is a warning not a fail, as // denoted by the `w_` prefix. let mut ignored = ignored; ignored.push("w_ext_subject_key_identifier_missing_sub_cert"); zlint::check_certificate(pem.as_bytes(), &ignored); } } #[test] fn pss_certificate() { let serial_number = SerialNumber::from(42u32); let validity = Validity::from_now(Duration::new(5, 0)).unwrap(); let issuer = Name::from_str("CN=World domination corporation,O=World domination Inc,C=US").unwrap(); let profile = Profile::Leaf { issuer, enable_key_agreement: false, enable_key_encipherment: false, #[cfg(feature = "hazmat")] include_subject_key_identifier: true, }; let subject = Name::from_str("CN=service.domination.world").unwrap(); let pub_key = SubjectPublicKeyInfoOwned::try_from(RSA_2048_DER_EXAMPLE).expect("get rsa pub key"); let signer = rsa_pss_signer(); let builder = CertificateBuilder::new(profile, serial_number, validity, subject, pub_key, &signer) .expect("Create certificate"); let certificate = builder .build_with_rng::(&mut rand::thread_rng()) .unwrap(); let pem = certificate.to_pem(LineEnding::LF).expect("generate pem"); println!("{}", openssl::check_certificate(pem.as_bytes())); // TODO(baloo): not too sure we should tackle those in this API. let ignored = &[ "e_sub_cert_aia_missing", "e_sub_cert_crl_distribution_points_missing", "w_sub_cert_aia_does_not_contain_issuing_ca_url", // Missing policies "e_sub_cert_certificate_policies_missing", "e_sub_cert_cert_policy_empty", // Needs to be added by the end-user "e_sub_cert_aia_does_not_contain_ocsp_url", // SAN needs to include DNS name (if used) "e_ext_san_missing", "e_subject_common_name_not_exactly_from_san", // Extended key usage needs to be added by end-user and is use-case dependent "e_sub_cert_eku_missing", // zlint warns on RSAPSS signature algorithms "e_signature_algorithm_not_supported", ]; zlint::check_certificate(pem.as_bytes(), ignored); } const RSA_2048_PRIV_DER_EXAMPLE: &[u8] = include_bytes!("examples/rsa2048-priv.der"); fn rsa_signer() -> SigningKey { let private_key = rsa::RsaPrivateKey::from_pkcs1_der(RSA_2048_PRIV_DER_EXAMPLE).unwrap(); let signing_key = SigningKey::::new(private_key); signing_key } fn rsa_pss_signer() -> rsa::pss::SigningKey { let private_key = rsa::RsaPrivateKey::from_pkcs1_der(RSA_2048_PRIV_DER_EXAMPLE).unwrap(); let signing_key = rsa::pss::SigningKey::::new(private_key); signing_key } const PKCS8_PRIVATE_KEY_DER: &[u8] = include_bytes!("examples/p256-priv.der"); fn ecdsa_signer() -> ecdsa::SigningKey { let secret_key = p256::SecretKey::from_pkcs8_der(PKCS8_PRIVATE_KEY_DER).unwrap(); ecdsa::SigningKey::from(secret_key) } #[test] fn certificate_request() { use std::net::{IpAddr, Ipv4Addr}; let subject = Name::from_str("CN=service.domination.world").unwrap(); let signer = ecdsa_signer(); let mut builder = RequestBuilder::new(subject, &signer).expect("Create certificate request"); builder .add_extension(&SubjectAltName(vec![GeneralName::from(IpAddr::V4( Ipv4Addr::new(192, 0, 2, 0), ))])) .unwrap(); let cert_req = builder.build::().unwrap(); let pem = cert_req.to_pem(LineEnding::LF).expect("generate pem"); use std::fs::File; use std::io::Write; let mut file = File::create("/tmp/ecdsa.csr").expect("create pem file"); file.write_all(pem.as_bytes()).expect("Create pem file"); println!("{}", openssl::check_request(pem.as_bytes())); } #[test] fn certificate_request_attributes() { let subject = Name::from_str("CN=service.domination.world").unwrap(); let signer = ecdsa_signer(); let mut builder = RequestBuilder::new(subject, &signer).expect("Create certificate request"); builder .add_attribute(&request::attributes::ChallengePassword( DirectoryString::PrintableString( PrintableString::new(b"password1234") .expect("create printable string with password"), ), )) .expect("unable to add attribute"); let cert_req = builder.build::().unwrap(); let pem = cert_req.to_pem(LineEnding::LF).expect("generate pem"); use std::fs::File; use std::io::Write; let mut file = File::create("/tmp/ecdsa.csr").expect("create pem file"); file.write_all(pem.as_bytes()).expect("Create pem file"); println!("{}", openssl::check_request(pem.as_bytes())); }