//! X509 Certificate builder use alloc::vec; use core::fmt; use der::{asn1::BitString, referenced::OwnedToRef, Encode}; use signature::{rand_core::CryptoRngCore, Keypair, RandomizedSigner, Signer}; use spki::{ DynSignatureAlgorithmIdentifier, EncodePublicKey, SignatureBitStringEncoding, SubjectPublicKeyInfoOwned, SubjectPublicKeyInfoRef, }; use crate::{ certificate::{Certificate, TbsCertificate, Version}, ext::{ pkix::{ AuthorityKeyIdentifier, BasicConstraints, KeyUsage, KeyUsages, SubjectKeyIdentifier, }, AsExtension, Extension, Extensions, }, name::Name, request::{attributes::AsAttribute, CertReq, CertReqInfo, ExtensionReq}, serial_number::SerialNumber, time::Validity, }; /// Error type #[derive(Debug)] #[non_exhaustive] pub enum Error { /// ASN.1 DER-related errors. Asn1(der::Error), /// Public key errors propagated from the [`spki::Error`] type. PublicKey(spki::Error), /// Signing error propagated for the [`signature::Error`] type. Signature(signature::Error), } #[cfg(feature = "std")] impl std::error::Error for Error {} impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Error::Asn1(err) => write!(f, "ASN.1 error: {}", err), Error::PublicKey(err) => write!(f, "public key error: {}", err), Error::Signature(err) => write!(f, "signature error: {}", err), } } } impl From<der::Error> for Error { fn from(err: der::Error) -> Error { Error::Asn1(err) } } impl From<spki::Error> for Error { fn from(err: spki::Error) -> Error { Error::PublicKey(err) } } impl From<signature::Error> for Error { fn from(err: signature::Error) -> Error { Error::Signature(err) } } type Result<T> = core::result::Result<T, Error>; /// The type of certificate to build #[derive(Clone, Debug, Eq, PartialEq)] pub enum Profile { /// Build a root CA certificate Root, /// Build an intermediate sub CA certificate SubCA { /// issuer Name, /// represents the name signing the certificate issuer: Name, /// pathLenConstraint INTEGER (0..MAX) OPTIONAL /// BasicConstraints as defined in [RFC 5280 Section 4.2.1.9]. path_len_constraint: Option<u8>, }, /// Build an end certificate Leaf { /// issuer Name, /// represents the name signing the certificate issuer: Name, /// should the key agreement flag of KeyUsage be enabled enable_key_agreement: bool, /// should the key encipherment flag of KeyUsage be enabled enable_key_encipherment: bool, /// should the subject key identifier extension be included /// /// From [RFC 5280 Section 4.2.1.2]: /// For end entity certificates, subject key identifiers SHOULD be /// derived from the public key. Two common methods for generating key /// identifiers from the public key are identified above. #[cfg(feature = "hazmat")] include_subject_key_identifier: bool, }, #[cfg(feature = "hazmat")] /// Opt-out of the default extensions Manual { /// issuer Name, /// represents the name signing the certificate /// A `None` will make it a self-signed certificate issuer: Option<Name>, }, } impl Profile { fn get_issuer(&self, subject: &Name) -> Name { match self { Profile::Root => subject.clone(), Profile::SubCA { issuer, .. } => issuer.clone(), Profile::Leaf { issuer, .. } => issuer.clone(), #[cfg(feature = "hazmat")] Profile::Manual { issuer, .. } => issuer.as_ref().unwrap_or(subject).clone(), } } fn build_extensions( &self, spk: SubjectPublicKeyInfoRef<'_>, issuer_spk: SubjectPublicKeyInfoRef<'_>, tbs: &TbsCertificate, ) -> Result<vec::Vec<Extension>> { #[cfg(feature = "hazmat")] // User opted out of default extensions set. if let Profile::Manual { .. } = self { return Ok(vec::Vec::default()); } let mut extensions: vec::Vec<Extension> = vec::Vec::new(); match self { #[cfg(feature = "hazmat")] Profile::Leaf { include_subject_key_identifier: false, .. } => {} _ => extensions.push( SubjectKeyIdentifier::try_from(spk)?.to_extension(&tbs.subject, &extensions)?, ), } // Build Authority Key Identifier match self { Profile::Root => {} _ => { extensions.push( AuthorityKeyIdentifier::try_from(issuer_spk.clone())? .to_extension(&tbs.subject, &extensions)?, ); } } // Build Basic Contraints extensions extensions.push(match self { Profile::Root => BasicConstraints { ca: true, path_len_constraint: None, } .to_extension(&tbs.subject, &extensions)?, Profile::SubCA { path_len_constraint, .. } => BasicConstraints { ca: true, path_len_constraint: *path_len_constraint, } .to_extension(&tbs.subject, &extensions)?, Profile::Leaf { .. } => BasicConstraints { ca: false, path_len_constraint: None, } .to_extension(&tbs.subject, &extensions)?, #[cfg(feature = "hazmat")] Profile::Manual { .. } => unreachable!(), }); // Build Key Usage extension match self { Profile::Root | Profile::SubCA { .. } => { extensions.push( KeyUsage(KeyUsages::KeyCertSign | KeyUsages::CRLSign) .to_extension(&tbs.subject, &extensions)?, ); } Profile::Leaf { enable_key_agreement, enable_key_encipherment, .. } => { let mut key_usage = KeyUsages::DigitalSignature | KeyUsages::NonRepudiation; if *enable_key_encipherment { key_usage |= KeyUsages::KeyEncipherment; } if *enable_key_agreement { key_usage |= KeyUsages::KeyAgreement; } extensions.push(KeyUsage(key_usage).to_extension(&tbs.subject, &extensions)?); } #[cfg(feature = "hazmat")] Profile::Manual { .. } => unreachable!(), } Ok(extensions) } } /// X509 Certificate builder /// /// ``` /// use der::Decode; /// use x509_cert::spki::SubjectPublicKeyInfoOwned; /// use x509_cert::builder::{CertificateBuilder, Profile}; /// use x509_cert::name::Name; /// use x509_cert::serial_number::SerialNumber; /// use x509_cert::time::Validity; /// use std::str::FromStr; /// /// # const RSA_2048_DER: &[u8] = include_bytes!("../tests/examples/rsa2048-pub.der"); /// # const RSA_2048_PRIV_DER: &[u8] = include_bytes!("../tests/examples/rsa2048-priv.der"); /// # use rsa::{pkcs1v15::SigningKey, pkcs1::DecodeRsaPrivateKey}; /// # use sha2::Sha256; /// # use std::time::Duration; /// # use der::referenced::RefToOwned; /// # fn rsa_signer() -> SigningKey<Sha256> { /// # let private_key = rsa::RsaPrivateKey::from_pkcs1_der(RSA_2048_PRIV_DER).unwrap(); /// # let signing_key = SigningKey::<Sha256>::new_with_prefix(private_key); /// # signing_key /// # } /// /// 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(); /// /// let pub_key = SubjectPublicKeyInfoOwned::try_from(RSA_2048_DER).expect("get rsa pub key"); /// /// let mut signer = rsa_signer(); /// let mut builder = CertificateBuilder::new( /// profile, /// serial_number, /// validity, /// subject, /// pub_key, /// &signer, /// ) /// .expect("Create certificate"); /// ``` pub struct CertificateBuilder<'s, S> { tbs: TbsCertificate, extensions: Extensions, cert_signer: &'s S, } impl<'s, S> CertificateBuilder<'s, S> where S: Keypair + DynSignatureAlgorithmIdentifier, S::VerifyingKey: EncodePublicKey, { /// Creates a new certificate builder pub fn new( profile: Profile, serial_number: SerialNumber, mut validity: Validity, subject: Name, subject_public_key_info: SubjectPublicKeyInfoOwned, cert_signer: &'s S, ) -> Result<Self> { let verifying_key = cert_signer.verifying_key(); let signer_pub = verifying_key .to_public_key_der()? .decode_msg::<SubjectPublicKeyInfoOwned>()?; let signature_alg = cert_signer.signature_algorithm_identifier()?; let issuer = profile.get_issuer(&subject); validity.not_before.rfc5280_adjust_utc_time()?; validity.not_after.rfc5280_adjust_utc_time()?; let tbs = TbsCertificate { version: Version::V3, serial_number, signature: signature_alg, issuer, validity, subject, subject_public_key_info, extensions: None, // We will not generate unique identifier because as per RFC5280 Section 4.1.2.8: // CAs conforming to this profile MUST NOT generate // certificates with unique identifiers. // // https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.8 issuer_unique_id: None, subject_unique_id: None, }; let extensions = profile.build_extensions( tbs.subject_public_key_info.owned_to_ref(), signer_pub.owned_to_ref(), &tbs, )?; Ok(Self { tbs, extensions, cert_signer, }) } /// Add an extension to this certificate pub fn add_extension<E: AsExtension>(&mut self, extension: &E) -> Result<()> { let ext = extension.to_extension(&self.tbs.subject, &self.extensions)?; self.extensions.push(ext); Ok(()) } } /// Builder for X509 Certificate Requests /// /// ``` /// # use p256::{pkcs8::DecodePrivateKey, NistP256, ecdsa::DerSignature}; /// # const PKCS8_PRIVATE_KEY_DER: &[u8] = include_bytes!("../tests/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) /// # } /// use x509_cert::{ /// builder::{Builder, RequestBuilder}, /// ext::pkix::{name::GeneralName, SubjectAltName}, /// name::Name, /// }; /// use std::str::FromStr; /// /// 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(); /// ``` pub struct RequestBuilder<'s, S> { info: CertReqInfo, extension_req: ExtensionReq, req_signer: &'s S, } impl<'s, S> RequestBuilder<'s, S> where S: Keypair + DynSignatureAlgorithmIdentifier, S::VerifyingKey: EncodePublicKey, { /// Creates a new certificate request builder pub fn new(subject: Name, req_signer: &'s S) -> Result<Self> { let version = Default::default(); let verifying_key = req_signer.verifying_key(); let public_key = verifying_key .to_public_key_der()? .decode_msg::<SubjectPublicKeyInfoOwned>()?; let attributes = Default::default(); let extension_req = Default::default(); Ok(Self { info: CertReqInfo { version, subject, public_key, attributes, }, extension_req, req_signer, }) } /// Add an extension to this certificate request pub fn add_extension<E: AsExtension>(&mut self, extension: &E) -> Result<()> { let ext = extension.to_extension(&self.info.subject, &self.extension_req.0)?; self.extension_req.0.push(ext); Ok(()) } /// Add an attribute to this certificate request pub fn add_attribute<A: AsAttribute>(&mut self, attribute: &A) -> Result<()> { let attr = attribute.to_attribute()?; self.info.attributes.insert(attr)?; Ok(()) } } /// Trait for X509 builders /// /// This trait defines the interface between builder and the signers. pub trait Builder: Sized { /// The builder's object signer type Signer; /// Type built by this builder type Output: Sized; /// Return a reference to the signer. fn signer(&self) -> &Self::Signer; /// Assemble the final object from signature. fn assemble(self, signature: BitString) -> Result<Self::Output>; /// Finalize and return a serialization of the object for signature. fn finalize(&mut self) -> der::Result<vec::Vec<u8>>; /// Run the object through the signer and build it. fn build<Signature>(mut self) -> Result<Self::Output> where Self::Signer: Signer<Signature>, Signature: SignatureBitStringEncoding, { let blob = self.finalize()?; let signature = self.signer().try_sign(&blob)?.to_bitstring()?; self.assemble(signature) } /// Run the object through the signer and build it. fn build_with_rng<Signature>(mut self, rng: &mut impl CryptoRngCore) -> Result<Self::Output> where Self::Signer: RandomizedSigner<Signature>, Signature: SignatureBitStringEncoding, { let blob = self.finalize()?; let signature = self .signer() .try_sign_with_rng(rng, &blob)? .to_bitstring()?; self.assemble(signature) } } impl<'s, S> Builder for CertificateBuilder<'s, S> where S: Keypair + DynSignatureAlgorithmIdentifier, S::VerifyingKey: EncodePublicKey, { type Signer = S; type Output = Certificate; fn signer(&self) -> &Self::Signer { self.cert_signer } fn finalize(&mut self) -> der::Result<vec::Vec<u8>> { if !self.extensions.is_empty() { self.tbs.extensions = Some(self.extensions.clone()); } if self.tbs.extensions.is_none() { if self.tbs.issuer_unique_id.is_some() || self.tbs.subject_unique_id.is_some() { self.tbs.version = Version::V2; } else { self.tbs.version = Version::V1; } } self.tbs.to_der() } fn assemble(self, signature: BitString) -> Result<Self::Output> { let signature_algorithm = self.tbs.signature.clone(); Ok(Certificate { tbs_certificate: self.tbs, signature_algorithm, signature, }) } } impl<'s, S> Builder for RequestBuilder<'s, S> where S: Keypair + DynSignatureAlgorithmIdentifier, S::VerifyingKey: EncodePublicKey, { type Signer = S; type Output = CertReq; fn signer(&self) -> &Self::Signer { self.req_signer } fn finalize(&mut self) -> der::Result<vec::Vec<u8>> { self.info .attributes .insert(self.extension_req.clone().try_into()?)?; self.info.to_der() } fn assemble(self, signature: BitString) -> Result<Self::Output> { let algorithm = self.req_signer.signature_algorithm_identifier()?; Ok(CertReq { info: self.info, algorithm, signature, }) } }