use std::vec;
use der::Encode;
use der::flagset::FlagSet;
use sha1::Sha1;
use x509_cert::certificate::CertificateInner;
use crate::cert::Certificate;
use crate::cert::SignatureAlgorithm;
use crate::cert::extensions::AuthorityKeyIdentifier;
use crate::cert::extensions::BasicConstraints;
use crate::cert::extensions::ExtendedKeyUsage;
use crate::cert::extensions::ExtendedKeyUsageOption;
use crate::cert::extensions::KeyUsage;
use crate::cert::extensions::KeyUsages;
use crate::cert::extensions::SubjectKeyIdentifier;
use crate::cert::params::Validity;
use crate::cert::params::{CertificateParams, DistinguishedName, ExtensionParam};
use crate::key::KeyPair;
use crate::tbs_certificate::TbsCertificate;
pub trait Issuer {
fn issuer_name(&self) -> Result<DistinguishedName, crate::error::CertKitError>;
fn signing_key(&self) -> &KeyPair;
fn serial_number(&self) -> Vec<u8> {
use rand_core::RngCore;
let mut buf = [0u8; 20];
rand_core::OsRng.fill_bytes(&mut buf);
buf[0] &= 0x7F;
buf[19] |= 0x01;
buf.to_vec()
}
fn issue(
&self,
cert_request: &CertificateParams,
validity: Validity,
) -> Result<Certificate, crate::error::CertKitError> {
let signature_algo = match self.signing_key() {
#[cfg(feature = "rsa")]
KeyPair::Rsa { .. } => SignatureAlgorithm::Sha256WithRSA,
#[cfg(feature = "p256")]
KeyPair::EcdsaP256 { .. } => SignatureAlgorithm::Sha256WithECDSA,
#[cfg(feature = "p384")]
KeyPair::EcdsaP384 { .. } => SignatureAlgorithm::Sha384WithECDSA,
#[cfg(feature = "p521")]
KeyPair::EcdsaP521 { .. } => SignatureAlgorithm::Sha512WithECDSA,
#[cfg(feature = "ed25519")]
KeyPair::Ed25519 { .. } => SignatureAlgorithm::Ed25519,
};
log::debug!(
"issuing certificate for \"{}\" (CA: {}, signature algorithm: {:?})",
cert_request.subject.common_name,
cert_request.is_ca,
signature_algo
);
let issuer_spki = self.signing_key().as_spki();
let authority_key_id = AuthorityKeyIdentifier {
key_identifier: <Sha1 as sha1::Digest>::digest(
issuer_spki.subject_public_key.raw_bytes(),
)
.to_vec(),
authority_cert_issuer: None,
authority_cert_serial_number: None,
};
let subject_spki = cert_request.subject_public_key.as_spki();
let subject_key_id = SubjectKeyIdentifier {
key_identifier: <Sha1 as sha1::Digest>::digest(
subject_spki.subject_public_key.raw_bytes(),
)
.to_vec(),
};
let issuer_dn = self.issuer_name()?;
let basic_constraints = BasicConstraints {
is_ca: cert_request.is_ca,
max_path_length: if cert_request.is_ca {
cert_request.max_path_length
} else {
None
},
};
let mut extensions: Vec<ExtensionParam> = vec![
ExtensionParam::from_extension(basic_constraints, true)?,
ExtensionParam::from_extension(authority_key_id, false)?,
ExtensionParam::from_extension(subject_key_id, false)?,
];
let mut key_usage_flags: FlagSet<KeyUsages> = FlagSet::empty();
if cert_request.is_ca {
key_usage_flags |= KeyUsages::KeyCertSign;
key_usage_flags |= KeyUsages::CRLSign;
}
for usage in &cert_request.usages {
match usage {
ExtendedKeyUsageOption::ClientAuth
| ExtendedKeyUsageOption::ServerAuth
| ExtendedKeyUsageOption::EmailProtection => {
key_usage_flags |= KeyUsages::DigitalSignature;
#[cfg(feature = "rsa")]
if matches!(
cert_request.subject_public_key,
crate::key::PublicKey::Rsa(_)
) {
key_usage_flags |= KeyUsages::KeyEncipherment;
}
}
ExtendedKeyUsageOption::CodeSigning
| ExtendedKeyUsageOption::TimeStamping
| ExtendedKeyUsageOption::OcspSigning => {
key_usage_flags |= KeyUsages::DigitalSignature;
}
}
}
if !key_usage_flags.is_empty() {
let key_usage = KeyUsage(key_usage_flags);
extensions.push(ExtensionParam::from_extension(key_usage, true)?);
}
if !cert_request.usages.is_empty() {
let extended_key_usage = ExtendedKeyUsage {
usage: cert_request.usages.clone(),
};
extensions.push(ExtensionParam::from_extension(extended_key_usage, true)?);
}
let combined_extensions: Vec<ExtensionParam> = cert_request
.extensions
.iter()
.cloned()
.chain(extensions)
.collect();
log::trace!("certificate has {} extension(s)", combined_extensions.len());
let serial_number = self.serial_number();
if serial_number.is_empty() || serial_number.len() > 20 {
return Err(crate::error::CertKitError::InvalidInput(format!(
"serial number must be 1..=20 octets, got {}",
serial_number.len()
)));
}
let tbs_cert = TbsCertificate {
serial_number,
signature_algorithm: signature_algo.clone(),
issuer: issuer_dn,
not_before: validity.not_before(),
not_after: validity.not_after(),
subject: cert_request.subject.clone(),
subject_public_key: cert_request.subject_public_key.clone(),
extensions: combined_extensions,
};
let tbs_cert_inner = tbs_cert.to_tbs_certificate_inner()?;
let signature = self
.signing_key()
.sign_data(&tbs_cert_inner.to_der()?)
.map_err(|e| {
crate::error::CertKitError::CertificateError(format!("signing failed: {e}"))
})?;
log::trace!("certificate signed ({} byte signature)", signature.len());
let cert_inner = CertificateInner {
signature_algorithm: tbs_cert_inner.signature.clone(),
tbs_certificate: tbs_cert_inner,
signature: der::asn1::BitString::from_bytes(&signature)?,
};
Ok(Certificate::from_inner(cert_inner))
}
}
#[cfg(test)]
mod tests {
use super::Issuer;
use crate::cert::extensions::{
AuthorityKeyIdentifier, SubjectKeyIdentifier, ToAndFromX509Extension,
};
use crate::cert::params::{CertificateParams, DistinguishedName, Validity};
use crate::cert::{Certificate, CertificateWithPrivateKey};
use crate::key::{KeyPair, PublicKey};
fn request(common_name: &str, key: &KeyPair, is_ca: bool) -> CertificateParams {
crate::init_test_logger();
CertificateParams::builder()
.subject(
DistinguishedName::builder()
.common_name(common_name.to_string())
.build(),
)
.subject_public_key(PublicKey::from_key_pair(key))
.is_ca(is_ca)
.build()
}
fn extension<E: ToAndFromX509Extension>(cert: &Certificate) -> Option<E> {
let info = cert.params().unwrap();
info.extensions
.iter()
.find(|ext| ext.oid == E::OID)
.map(|ext| ext.to_extension::<E>().unwrap())
}
#[cfg(feature = "p256")]
#[test]
fn p256_signature_algorithm_is_ecdsa_with_sha256() {
let key = KeyPair::generate_ecdsa_p256();
let cert = Certificate::new_self_signed(&request("p256.ca", &key, true), &key).unwrap();
let expected = const_oid::db::rfc5912::ECDSA_WITH_SHA_256;
assert_eq!(cert.inner().signature_algorithm.oid, expected);
assert_eq!(cert.inner().tbs_certificate.signature.oid, expected);
}
#[cfg(feature = "p384")]
#[test]
fn p384_signature_algorithm_is_ecdsa_with_sha384() {
let key = KeyPair::generate_ecdsa_p384();
let cert = Certificate::new_self_signed(&request("p384.ca", &key, true), &key).unwrap();
let expected = const_oid::db::rfc5912::ECDSA_WITH_SHA_384;
assert_eq!(cert.inner().signature_algorithm.oid, expected);
assert_eq!(cert.inner().tbs_certificate.signature.oid, expected);
}
#[cfg(feature = "p521")]
#[test]
fn p521_signature_algorithm_is_ecdsa_with_sha512() {
let key = KeyPair::generate_ecdsa_p521();
let cert = Certificate::new_self_signed(&request("p521.ca", &key, true), &key).unwrap();
let expected = const_oid::db::rfc5912::ECDSA_WITH_SHA_512;
assert_eq!(cert.inner().signature_algorithm.oid, expected);
assert_eq!(cert.inner().tbs_certificate.signature.oid, expected);
}
#[cfg(feature = "p256")]
#[test]
fn issued_chain_links_aki_to_issuer_ski() {
let root_key = KeyPair::generate_ecdsa_p256();
let root =
Certificate::new_self_signed(&request("Root CA", &root_key, true), &root_key).unwrap();
let root_ca = CertificateWithPrivateKey::new(root, root_key);
let int_key = KeyPair::generate_ecdsa_p256();
let int = root_ca
.issue(
&request("Intermediate CA", &int_key, true),
Validity::for_days(365).unwrap(),
)
.unwrap();
let int_ca = CertificateWithPrivateKey::new(int, int_key);
let leaf_key = KeyPair::generate_ecdsa_p256();
let leaf = int_ca
.issue(
&request("leaf", &leaf_key, false),
Validity::for_days(365).unwrap(),
)
.unwrap();
let root_ski = extension::<SubjectKeyIdentifier>(root_ca.cert()).expect("root SKI");
let int_ski = extension::<SubjectKeyIdentifier>(int_ca.cert()).expect("intermediate SKI");
assert!(
extension::<SubjectKeyIdentifier>(&leaf).is_some(),
"leaf must carry a Subject Key Identifier"
);
let int_aki = extension::<AuthorityKeyIdentifier>(int_ca.cert()).expect("intermediate AKI");
let leaf_aki = extension::<AuthorityKeyIdentifier>(&leaf).expect("leaf AKI");
assert!(int_aki.authority_cert_issuer.is_none());
assert!(int_aki.authority_cert_serial_number.is_none());
assert!(leaf_aki.authority_cert_issuer.is_none());
assert!(leaf_aki.authority_cert_serial_number.is_none());
assert_eq!(int_aki.key_identifier, root_ski.key_identifier);
assert_eq!(leaf_aki.key_identifier, int_ski.key_identifier);
}
}