From 486a5a66a2af7a8c006f4fb4855c3b0be1258a09 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Mon, 21 Oct 2024 17:54:08 +0000 Subject: [PATCH] x509-cert: move CSR builder to x509_cert::request (#1581) --- x509-cert/src/builder.rs | 128 ++---------------------------- x509-cert/src/request.rs | 6 ++ x509-cert/src/request/builder.rs | 132 +++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+), 121 deletions(-) create mode 100644 x509-cert/src/request/builder.rs diff --git a/x509-cert/src/builder.rs b/x509-cert/src/builder.rs index 493cfaf7a..809e1a538 100644 --- a/x509-cert/src/builder.rs +++ b/x509-cert/src/builder.rs @@ -12,8 +12,6 @@ use spki::{ use crate::{ certificate::{Certificate, TbsCertificate, Version}, ext::{AsExtension, Extensions}, - name::Name, - request::{attributes::AsAttribute, CertReq, CertReqInfo, ExtensionReq}, serial_number::SerialNumber, time::Validity, AlgorithmIdentifier, SubjectPublicKeyInfo, @@ -29,7 +27,13 @@ use self::profile::BuilderProfile; )] pub use self::profile::BuilderProfile as Profile; -const NULL_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("0.0.0"); +#[deprecated( + since = "0.3.0", + note = "please use `x509_cert::request::RequestBuilder` instead" +)] +pub use crate::request::RequestBuilder; + +pub(crate) const NULL_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("0.0.0"); /// Error type #[derive(Debug)] @@ -212,90 +216,6 @@ where } } -/// 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 { -/// # 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).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>(&signer).unwrap(); -/// ``` -pub struct RequestBuilder { - info: CertReqInfo, - extension_req: ExtensionReq, -} - -impl RequestBuilder { - /// Creates a new certificate request builder - pub fn new(subject: Name) -> Result { - let version = Default::default(); - - let algorithm = AlgorithmIdentifier { - oid: NULL_OID, - parameters: None, - }; - let public_key = SubjectPublicKeyInfo { - algorithm, - subject_public_key: BitString::from_bytes(&[]).expect("unable to parse empty object"), - }; - - let attributes = Default::default(); - let extension_req = Default::default(); - - Ok(Self { - info: CertReqInfo { - version, - subject, - public_key, - attributes, - }, - extension_req, - }) - } - - /// Add an extension to this certificate request - /// - /// Extensions need to implement [`AsExtension`], examples may be found in - /// in [`AsExtension` documentation](../ext/trait.AsExtension.html#examples) or - /// [the implementors](../ext/trait.AsExtension.html#implementors). - pub fn add_extension(&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(&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. @@ -404,40 +324,6 @@ where } } -impl Builder for RequestBuilder { - type Output = CertReq; - - fn finalize(&mut self, signer: &S) -> Result> - where - S: Keypair + DynSignatureAlgorithmIdentifier, - S::VerifyingKey: EncodePublicKey, - { - let verifying_key = signer.verifying_key(); - let public_key = SubjectPublicKeyInfo::from_key(&verifying_key)?; - self.info.public_key = public_key; - - self.info - .attributes - .insert(self.extension_req.clone().try_into()?)?; - - self.info.to_der().map_err(Error::from) - } - - fn assemble(self, signature: BitString, signer: &S) -> Result - where - S: Keypair + DynSignatureAlgorithmIdentifier, - S::VerifyingKey: EncodePublicKey, - { - let algorithm = signer.signature_algorithm_identifier()?; - - Ok(CertReq { - info: self.info, - algorithm, - signature, - }) - } -} - /// Trait for async X509 builders /// /// This trait defines the interface between builder and the signers. diff --git a/x509-cert/src/request.rs b/x509-cert/src/request.rs index 9681be12a..0f1bdb1a1 100644 --- a/x509-cert/src/request.rs +++ b/x509-cert/src/request.rs @@ -19,6 +19,12 @@ use der::{ #[cfg(feature = "pem")] use der::pem::PemLabel; +#[cfg(feature = "builder")] +mod builder; + +#[cfg(feature = "builder")] +pub use self::builder::RequestBuilder; + /// Version identifier for certification request information. /// /// (RFC 2986 designates `0` as the only valid version) diff --git a/x509-cert/src/request/builder.rs b/x509-cert/src/request/builder.rs new file mode 100644 index 000000000..a87bd02e5 --- /dev/null +++ b/x509-cert/src/request/builder.rs @@ -0,0 +1,132 @@ +use alloc::vec; + +use der::{asn1::BitString, Encode}; +use signature::Keypair; +use spki::{ + AlgorithmIdentifier, DynSignatureAlgorithmIdentifier, EncodePublicKey, SubjectPublicKeyInfo, +}; + +use crate::{ + builder::{Builder, Error, Result, NULL_OID}, + ext::AsExtension, + name::Name, + request::{attributes::AsAttribute, CertReq, CertReqInfo, ExtensionReq}, +}; + +/// Builder for X509 Certificate Requests (CSR) +/// +/// ``` +/// # 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 { +/// # 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).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>(&signer).unwrap(); +/// ``` +pub struct RequestBuilder { + info: CertReqInfo, + extension_req: ExtensionReq, +} + +impl RequestBuilder { + /// Creates a new certificate request builder + pub fn new(subject: Name) -> Result { + let version = Default::default(); + + let algorithm = AlgorithmIdentifier { + oid: NULL_OID, + parameters: None, + }; + let public_key = SubjectPublicKeyInfo { + algorithm, + subject_public_key: BitString::from_bytes(&[]).expect("unable to parse empty object"), + }; + + let attributes = Default::default(); + let extension_req = Default::default(); + + Ok(Self { + info: CertReqInfo { + version, + subject, + public_key, + attributes, + }, + extension_req, + }) + } + + /// Add an extension to this certificate request + /// + /// Extensions need to implement [`AsExtension`], examples may be found in + /// in [`AsExtension` documentation](../ext/trait.AsExtension.html#examples) or + /// [the implementors](../ext/trait.AsExtension.html#implementors). + pub fn add_extension(&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(&mut self, attribute: &A) -> Result<()> { + let attr = attribute.to_attribute()?; + + self.info.attributes.insert(attr)?; + Ok(()) + } +} + +impl Builder for RequestBuilder { + type Output = CertReq; + + fn finalize(&mut self, signer: &S) -> Result> + where + S: Keypair + DynSignatureAlgorithmIdentifier, + S::VerifyingKey: EncodePublicKey, + { + let verifying_key = signer.verifying_key(); + let public_key = SubjectPublicKeyInfo::from_key(&verifying_key)?; + self.info.public_key = public_key; + + self.info + .attributes + .insert(self.extension_req.clone().try_into()?)?; + + self.info.to_der().map_err(Error::from) + } + + fn assemble(self, signature: BitString, signer: &S) -> Result + where + S: Keypair + DynSignatureAlgorithmIdentifier, + S::VerifyingKey: EncodePublicKey, + { + let algorithm = signer.signature_algorithm_identifier()?; + + Ok(CertReq { + info: self.info, + algorithm, + signature, + }) + } +}