blob: d31eb8c7e99215b398325fefeea094418508ce2e [file] [log] [blame] [edit]
#![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::<DerSignature>().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::<DerSignature>().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,
subject.clone(),
pub_key.clone(),
&signer,
)
.expect("Create certificate");
let certificate = builder.build::<DerSignature>().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::<DerSignature>().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::<rsa::pss::Signature>(&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<Sha256> {
let private_key = rsa::RsaPrivateKey::from_pkcs1_der(RSA_2048_PRIV_DER_EXAMPLE).unwrap();
SigningKey::<Sha256>::new(private_key)
}
fn rsa_pss_signer() -> rsa::pss::SigningKey<Sha256> {
let private_key = rsa::RsaPrivateKey::from_pkcs1_der(RSA_2048_PRIV_DER_EXAMPLE).unwrap();
rsa::pss::SigningKey::<Sha256>::new(private_key)
}
const PKCS8_PRIVATE_KEY_DER: &[u8] = include_bytes!("examples/p256-priv.der");
fn ecdsa_signer() -> ecdsa::SigningKey<NistP256> {
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::<DerSignature>().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::<DerSignature>().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()));
}