Skip to main content
This is unreleased documentation for the main (development) branch of crypto-glue.

x509_cert/request/
builder.rs

1use alloc::vec;
2
3use der::{Encode, asn1::BitString};
4use signature::Keypair;
5use spki::{
6    AlgorithmIdentifier, DynSignatureAlgorithmIdentifier, EncodePublicKey, SubjectPublicKeyInfo,
7};
8
9use crate::{
10    builder::{Builder, Error, NULL_OID, Result},
11    ext::ToExtension,
12    name::Name,
13    request::{CertReq, CertReqInfo, ExtensionReq, attributes::AsAttribute},
14};
15
16/// Builder for X509 Certificate Requests (CSR)
17///
18/// ```
19/// # use p256::{pkcs8::DecodePrivateKey, NistP256, ecdsa::DerSignature};
20/// # const PKCS8_PRIVATE_KEY_DER: &[u8] = include_bytes!("../../tests/examples/p256-priv.der");
21/// # fn ecdsa_signer() -> ecdsa::SigningKey<NistP256> {
22/// #     let secret_key = p256::SecretKey::from_pkcs8_der(PKCS8_PRIVATE_KEY_DER).unwrap();
23/// #     ecdsa::SigningKey::from(secret_key)
24/// # }
25/// use x509_cert::{
26///     builder::{Builder, RequestBuilder},
27///     ext::pkix::{name::GeneralName, SubjectAltName},
28///     name::Name,
29/// };
30/// use std::str::FromStr;
31///
32/// use std::net::{IpAddr, Ipv4Addr};
33/// let subject = Name::from_str("CN=service.domination.world").unwrap();
34///
35/// let signer = ecdsa_signer();
36/// let mut builder = RequestBuilder::new(subject).expect("Create certificate request");
37/// builder
38///     .add_extension(&SubjectAltName(vec![GeneralName::from(IpAddr::V4(
39///         Ipv4Addr::new(192, 0, 2, 0),
40///     ))]))
41///     .unwrap();
42///
43/// let cert_req = builder.build::<_, DerSignature>(&signer).unwrap();
44/// ```
45pub struct RequestBuilder {
46    info: CertReqInfo,
47    extension_req: ExtensionReq,
48}
49
50impl RequestBuilder {
51    /// Creates a new certificate request builder
52    pub fn new(subject: Name) -> Result<Self> {
53        let version = Default::default();
54
55        let algorithm = AlgorithmIdentifier {
56            oid: NULL_OID,
57            parameters: None,
58        };
59        let public_key = SubjectPublicKeyInfo {
60            algorithm,
61            subject_public_key: BitString::from_bytes(&[]).expect("unable to parse empty object"),
62        };
63
64        let attributes = Default::default();
65        let extension_req = Default::default();
66
67        Ok(Self {
68            info: CertReqInfo {
69                version,
70                subject,
71                public_key,
72                attributes,
73            },
74            extension_req,
75        })
76    }
77
78    /// Add an extension to this certificate request
79    ///
80    /// Extensions need to implement [`ToExtension`], examples may be found in
81    /// in [`ToExtension` documentation](../ext/trait.ToExtension.html#examples) or
82    /// [the implementors](../ext/trait.ToExtension.html#implementors).
83    pub fn add_extension<E: ToExtension>(
84        &mut self,
85        extension: E,
86    ) -> core::result::Result<(), E::Error> {
87        let ext = extension.to_extension(&self.info.subject, &self.extension_req.0)?;
88
89        self.extension_req.0.push(ext);
90
91        Ok(())
92    }
93
94    /// Add an attribute to this certificate request
95    pub fn add_attribute<A: AsAttribute>(&mut self, attribute: &A) -> Result<()> {
96        let attr = attribute.to_attribute()?;
97
98        self.info.attributes.insert(attr)?;
99        Ok(())
100    }
101}
102
103impl Builder for RequestBuilder {
104    type Output = CertReq;
105
106    fn finalize<S>(&mut self, signer: &S) -> Result<vec::Vec<u8>>
107    where
108        S: Keypair + DynSignatureAlgorithmIdentifier,
109        S::VerifyingKey: EncodePublicKey,
110    {
111        let verifying_key = signer.verifying_key();
112        let public_key = SubjectPublicKeyInfo::from_key(&verifying_key)?;
113        self.info.public_key = public_key;
114
115        self.info
116            .attributes
117            .insert(self.extension_req.clone().try_into()?)?;
118
119        self.info.to_der().map_err(Error::from)
120    }
121
122    fn assemble<S>(self, signature: BitString, signer: &S) -> Result<Self::Output>
123    where
124        S: Keypair + DynSignatureAlgorithmIdentifier,
125        S::VerifyingKey: EncodePublicKey,
126    {
127        let algorithm = signer.signature_algorithm_identifier()?;
128
129        Ok(CertReq {
130            info: self.info,
131            algorithm,
132            signature,
133        })
134    }
135}