x509_cert/
builder.rs

1//! X509 Certificate builder
2
3use alloc::vec;
4use core::fmt;
5use der::{asn1::BitString, referenced::OwnedToRef, Encode};
6use signature::{rand_core::CryptoRngCore, Keypair, RandomizedSigner, Signer};
7use spki::{
8    DynSignatureAlgorithmIdentifier, EncodePublicKey, SignatureBitStringEncoding,
9    SubjectPublicKeyInfoOwned, SubjectPublicKeyInfoRef,
10};
11
12use crate::{
13    certificate::{Certificate, TbsCertificate, Version},
14    ext::{
15        pkix::{
16            AuthorityKeyIdentifier, BasicConstraints, KeyUsage, KeyUsages, SubjectKeyIdentifier,
17        },
18        AsExtension, Extension, Extensions,
19    },
20    name::Name,
21    request::{attributes::AsAttribute, CertReq, CertReqInfo, ExtensionReq},
22    serial_number::SerialNumber,
23    time::Validity,
24};
25
26/// Error type
27#[derive(Debug)]
28#[non_exhaustive]
29pub enum Error {
30    /// ASN.1 DER-related errors.
31    Asn1(der::Error),
32
33    /// Public key errors propagated from the [`spki::Error`] type.
34    PublicKey(spki::Error),
35
36    /// Signing error propagated for the [`signature::Error`] type.
37    Signature(signature::Error),
38}
39
40#[cfg(feature = "std")]
41impl std::error::Error for Error {}
42
43impl fmt::Display for Error {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        match self {
46            Error::Asn1(err) => write!(f, "ASN.1 error: {}", err),
47            Error::PublicKey(err) => write!(f, "public key error: {}", err),
48            Error::Signature(err) => write!(f, "signature error: {}", err),
49        }
50    }
51}
52
53impl From<der::Error> for Error {
54    fn from(err: der::Error) -> Error {
55        Error::Asn1(err)
56    }
57}
58
59impl From<spki::Error> for Error {
60    fn from(err: spki::Error) -> Error {
61        Error::PublicKey(err)
62    }
63}
64
65impl From<signature::Error> for Error {
66    fn from(err: signature::Error) -> Error {
67        Error::Signature(err)
68    }
69}
70
71type Result<T> = core::result::Result<T, Error>;
72
73/// The type of certificate to build
74#[derive(Clone, Debug, Eq, PartialEq)]
75pub enum Profile {
76    /// Build a root CA certificate
77    Root,
78    /// Build an intermediate sub CA certificate
79    SubCA {
80        /// issuer   Name,
81        /// represents the name signing the certificate
82        issuer: Name,
83        /// pathLenConstraint       INTEGER (0..MAX) OPTIONAL
84        /// BasicConstraints as defined in [RFC 5280 Section 4.2.1.9].
85        path_len_constraint: Option<u8>,
86    },
87    /// Build an end certificate
88    Leaf {
89        /// issuer   Name,
90        /// represents the name signing the certificate
91        issuer: Name,
92        /// should the key agreement flag of KeyUsage be enabled
93        enable_key_agreement: bool,
94        /// should the key encipherment flag of KeyUsage be enabled
95        enable_key_encipherment: bool,
96        /// should the subject key identifier extension be included
97        ///
98        /// From [RFC 5280 Section 4.2.1.2]:
99        ///  For end entity certificates, subject key identifiers SHOULD be
100        ///  derived from the public key.  Two common methods for generating key
101        ///  identifiers from the public key are identified above.
102        #[cfg(feature = "hazmat")]
103        include_subject_key_identifier: bool,
104    },
105    #[cfg(feature = "hazmat")]
106    /// Opt-out of the default extensions
107    Manual {
108        /// issuer   Name,
109        /// represents the name signing the certificate
110        /// A `None` will make it a self-signed certificate
111        issuer: Option<Name>,
112    },
113}
114
115impl Profile {
116    fn get_issuer(&self, subject: &Name) -> Name {
117        match self {
118            Profile::Root => subject.clone(),
119            Profile::SubCA { issuer, .. } => issuer.clone(),
120            Profile::Leaf { issuer, .. } => issuer.clone(),
121            #[cfg(feature = "hazmat")]
122            Profile::Manual { issuer, .. } => issuer.as_ref().unwrap_or(subject).clone(),
123        }
124    }
125
126    fn build_extensions(
127        &self,
128        spk: SubjectPublicKeyInfoRef<'_>,
129        issuer_spk: SubjectPublicKeyInfoRef<'_>,
130        tbs: &TbsCertificate,
131    ) -> Result<vec::Vec<Extension>> {
132        #[cfg(feature = "hazmat")]
133        // User opted out of default extensions set.
134        if let Profile::Manual { .. } = self {
135            return Ok(vec::Vec::default());
136        }
137
138        let mut extensions: vec::Vec<Extension> = vec::Vec::new();
139
140        match self {
141            #[cfg(feature = "hazmat")]
142            Profile::Leaf {
143                include_subject_key_identifier: false,
144                ..
145            } => {}
146            _ => extensions.push(
147                SubjectKeyIdentifier::try_from(spk)?.to_extension(&tbs.subject, &extensions)?,
148            ),
149        }
150
151        // Build Authority Key Identifier
152        match self {
153            Profile::Root => {}
154            _ => {
155                extensions.push(
156                    AuthorityKeyIdentifier::try_from(issuer_spk.clone())?
157                        .to_extension(&tbs.subject, &extensions)?,
158                );
159            }
160        }
161
162        // Build Basic Contraints extensions
163        extensions.push(match self {
164            Profile::Root => BasicConstraints {
165                ca: true,
166                path_len_constraint: None,
167            }
168            .to_extension(&tbs.subject, &extensions)?,
169            Profile::SubCA {
170                path_len_constraint,
171                ..
172            } => BasicConstraints {
173                ca: true,
174                path_len_constraint: *path_len_constraint,
175            }
176            .to_extension(&tbs.subject, &extensions)?,
177            Profile::Leaf { .. } => BasicConstraints {
178                ca: false,
179                path_len_constraint: None,
180            }
181            .to_extension(&tbs.subject, &extensions)?,
182            #[cfg(feature = "hazmat")]
183            Profile::Manual { .. } => unreachable!(),
184        });
185
186        // Build Key Usage extension
187        match self {
188            Profile::Root | Profile::SubCA { .. } => {
189                extensions.push(
190                    KeyUsage(KeyUsages::KeyCertSign | KeyUsages::CRLSign)
191                        .to_extension(&tbs.subject, &extensions)?,
192                );
193            }
194            Profile::Leaf {
195                enable_key_agreement,
196                enable_key_encipherment,
197                ..
198            } => {
199                let mut key_usage = KeyUsages::DigitalSignature | KeyUsages::NonRepudiation;
200                if *enable_key_encipherment {
201                    key_usage |= KeyUsages::KeyEncipherment;
202                }
203                if *enable_key_agreement {
204                    key_usage |= KeyUsages::KeyAgreement;
205                }
206
207                extensions.push(KeyUsage(key_usage).to_extension(&tbs.subject, &extensions)?);
208            }
209            #[cfg(feature = "hazmat")]
210            Profile::Manual { .. } => unreachable!(),
211        }
212
213        Ok(extensions)
214    }
215}
216
217/// X509 Certificate builder
218///
219/// ```
220/// use der::Decode;
221/// use x509_cert::spki::SubjectPublicKeyInfoOwned;
222/// use x509_cert::builder::{CertificateBuilder, Profile};
223/// use x509_cert::name::Name;
224/// use x509_cert::serial_number::SerialNumber;
225/// use x509_cert::time::Validity;
226/// use std::str::FromStr;
227///
228/// # const RSA_2048_DER: &[u8] = include_bytes!("../tests/examples/rsa2048-pub.der");
229/// # const RSA_2048_PRIV_DER: &[u8] = include_bytes!("../tests/examples/rsa2048-priv.der");
230/// # use rsa::{pkcs1v15::SigningKey, pkcs1::DecodeRsaPrivateKey};
231/// # use sha2::Sha256;
232/// # use std::time::Duration;
233/// # use der::referenced::RefToOwned;
234/// # fn rsa_signer() -> SigningKey<Sha256> {
235/// #     let private_key = rsa::RsaPrivateKey::from_pkcs1_der(RSA_2048_PRIV_DER).unwrap();
236/// #     let signing_key = SigningKey::<Sha256>::new_with_prefix(private_key);
237/// #     signing_key
238/// # }
239///
240/// let serial_number = SerialNumber::from(42u32);
241/// let validity = Validity::from_now(Duration::new(5, 0)).unwrap();
242/// let profile = Profile::Root;
243/// let subject = Name::from_str("CN=World domination corporation,O=World domination Inc,C=US").unwrap();
244///
245/// let pub_key = SubjectPublicKeyInfoOwned::try_from(RSA_2048_DER).expect("get rsa pub key");
246///
247/// let mut signer = rsa_signer();
248/// let mut builder = CertificateBuilder::new(
249///     profile,
250///     serial_number,
251///     validity,
252///     subject,
253///     pub_key,
254///     &signer,
255/// )
256/// .expect("Create certificate");
257/// ```
258pub struct CertificateBuilder<'s, S> {
259    tbs: TbsCertificate,
260    extensions: Extensions,
261    cert_signer: &'s S,
262}
263
264impl<'s, S> CertificateBuilder<'s, S>
265where
266    S: Keypair + DynSignatureAlgorithmIdentifier,
267    S::VerifyingKey: EncodePublicKey,
268{
269    /// Creates a new certificate builder
270    pub fn new(
271        profile: Profile,
272        serial_number: SerialNumber,
273        mut validity: Validity,
274        subject: Name,
275        subject_public_key_info: SubjectPublicKeyInfoOwned,
276        cert_signer: &'s S,
277    ) -> Result<Self> {
278        let verifying_key = cert_signer.verifying_key();
279        let signer_pub = SubjectPublicKeyInfoOwned::from_key(verifying_key)?;
280
281        let signature_alg = cert_signer.signature_algorithm_identifier()?;
282        let issuer = profile.get_issuer(&subject);
283
284        validity.not_before.rfc5280_adjust_utc_time()?;
285        validity.not_after.rfc5280_adjust_utc_time()?;
286
287        let tbs = TbsCertificate {
288            version: Version::V3,
289            serial_number,
290            signature: signature_alg,
291            issuer,
292            validity,
293            subject,
294            subject_public_key_info,
295            extensions: None,
296
297            // We will not generate unique identifier because as per RFC5280 Section 4.1.2.8:
298            //   CAs conforming to this profile MUST NOT generate
299            //   certificates with unique identifiers.
300            //
301            // https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.8
302            issuer_unique_id: None,
303            subject_unique_id: None,
304        };
305
306        let extensions = profile.build_extensions(
307            tbs.subject_public_key_info.owned_to_ref(),
308            signer_pub.owned_to_ref(),
309            &tbs,
310        )?;
311        Ok(Self {
312            tbs,
313            extensions,
314            cert_signer,
315        })
316    }
317
318    /// Add an extension to this certificate
319    pub fn add_extension<E: AsExtension>(&mut self, extension: &E) -> Result<()> {
320        let ext = extension.to_extension(&self.tbs.subject, &self.extensions)?;
321        self.extensions.push(ext);
322
323        Ok(())
324    }
325}
326
327/// Builder for X509 Certificate Requests
328///
329/// ```
330/// # use p256::{pkcs8::DecodePrivateKey, NistP256, ecdsa::DerSignature};
331/// # const PKCS8_PRIVATE_KEY_DER: &[u8] = include_bytes!("../tests/examples/p256-priv.der");
332/// # fn ecdsa_signer() -> ecdsa::SigningKey<NistP256> {
333/// #     let secret_key = p256::SecretKey::from_pkcs8_der(PKCS8_PRIVATE_KEY_DER).unwrap();
334/// #     ecdsa::SigningKey::from(secret_key)
335/// # }
336/// use x509_cert::{
337///     builder::{Builder, RequestBuilder},
338///     ext::pkix::{name::GeneralName, SubjectAltName},
339///     name::Name,
340/// };
341/// use std::str::FromStr;
342///
343/// use std::net::{IpAddr, Ipv4Addr};
344/// let subject = Name::from_str("CN=service.domination.world").unwrap();
345///
346/// let signer = ecdsa_signer();
347/// let mut builder = RequestBuilder::new(subject, &signer).expect("Create certificate request");
348/// builder
349///     .add_extension(&SubjectAltName(vec![GeneralName::from(IpAddr::V4(
350///         Ipv4Addr::new(192, 0, 2, 0),
351///     ))]))
352///     .unwrap();
353///
354/// let cert_req = builder.build::<DerSignature>().unwrap();
355/// ```
356pub struct RequestBuilder<'s, S> {
357    info: CertReqInfo,
358    extension_req: ExtensionReq,
359    req_signer: &'s S,
360}
361
362impl<'s, S> RequestBuilder<'s, S>
363where
364    S: Keypair + DynSignatureAlgorithmIdentifier,
365    S::VerifyingKey: EncodePublicKey,
366{
367    /// Creates a new certificate request builder
368    pub fn new(subject: Name, req_signer: &'s S) -> Result<Self> {
369        let version = Default::default();
370        let verifying_key = req_signer.verifying_key();
371        let public_key = SubjectPublicKeyInfoOwned::from_key(verifying_key)?;
372        let attributes = Default::default();
373        let extension_req = Default::default();
374
375        Ok(Self {
376            info: CertReqInfo {
377                version,
378                subject,
379                public_key,
380                attributes,
381            },
382            extension_req,
383            req_signer,
384        })
385    }
386
387    /// Add an extension to this certificate request
388    pub fn add_extension<E: AsExtension>(&mut self, extension: &E) -> Result<()> {
389        let ext = extension.to_extension(&self.info.subject, &self.extension_req.0)?;
390
391        self.extension_req.0.push(ext);
392
393        Ok(())
394    }
395
396    /// Add an attribute to this certificate request
397    pub fn add_attribute<A: AsAttribute>(&mut self, attribute: &A) -> Result<()> {
398        let attr = attribute.to_attribute()?;
399
400        self.info.attributes.insert(attr)?;
401        Ok(())
402    }
403}
404
405/// Trait for X509 builders
406///
407/// This trait defines the interface between builder and the signers.
408pub trait Builder: Sized {
409    /// The builder's object signer
410    type Signer;
411
412    /// Type built by this builder
413    type Output: Sized;
414
415    /// Return a reference to the signer.
416    fn signer(&self) -> &Self::Signer;
417
418    /// Assemble the final object from signature.
419    fn assemble(self, signature: BitString) -> Result<Self::Output>;
420
421    /// Finalize and return a serialization of the object for signature.
422    fn finalize(&mut self) -> der::Result<vec::Vec<u8>>;
423
424    /// Run the object through the signer and build it.
425    fn build<Signature>(mut self) -> Result<Self::Output>
426    where
427        Self::Signer: Signer<Signature>,
428        Signature: SignatureBitStringEncoding,
429    {
430        let blob = self.finalize()?;
431
432        let signature = self.signer().try_sign(&blob)?.to_bitstring()?;
433
434        self.assemble(signature)
435    }
436
437    /// Run the object through the signer and build it.
438    fn build_with_rng<Signature>(mut self, rng: &mut impl CryptoRngCore) -> Result<Self::Output>
439    where
440        Self::Signer: RandomizedSigner<Signature>,
441        Signature: SignatureBitStringEncoding,
442    {
443        let blob = self.finalize()?;
444
445        let signature = self
446            .signer()
447            .try_sign_with_rng(rng, &blob)?
448            .to_bitstring()?;
449
450        self.assemble(signature)
451    }
452}
453
454impl<'s, S> Builder for CertificateBuilder<'s, S>
455where
456    S: Keypair + DynSignatureAlgorithmIdentifier,
457    S::VerifyingKey: EncodePublicKey,
458{
459    type Signer = S;
460    type Output = Certificate;
461
462    fn signer(&self) -> &Self::Signer {
463        self.cert_signer
464    }
465
466    fn finalize(&mut self) -> der::Result<vec::Vec<u8>> {
467        if !self.extensions.is_empty() {
468            self.tbs.extensions = Some(self.extensions.clone());
469        }
470
471        if self.tbs.extensions.is_none() {
472            if self.tbs.issuer_unique_id.is_some() || self.tbs.subject_unique_id.is_some() {
473                self.tbs.version = Version::V2;
474            } else {
475                self.tbs.version = Version::V1;
476            }
477        }
478
479        self.tbs.to_der()
480    }
481
482    fn assemble(self, signature: BitString) -> Result<Self::Output> {
483        let signature_algorithm = self.tbs.signature.clone();
484
485        Ok(Certificate {
486            tbs_certificate: self.tbs,
487            signature_algorithm,
488            signature,
489        })
490    }
491}
492
493impl<'s, S> Builder for RequestBuilder<'s, S>
494where
495    S: Keypair + DynSignatureAlgorithmIdentifier,
496    S::VerifyingKey: EncodePublicKey,
497{
498    type Signer = S;
499    type Output = CertReq;
500
501    fn signer(&self) -> &Self::Signer {
502        self.req_signer
503    }
504
505    fn finalize(&mut self) -> der::Result<vec::Vec<u8>> {
506        self.info
507            .attributes
508            .insert(self.extension_req.clone().try_into()?)?;
509
510        self.info.to_der()
511    }
512
513    fn assemble(self, signature: BitString) -> Result<Self::Output> {
514        let algorithm = self.req_signer.signature_algorithm_identifier()?;
515
516        Ok(CertReq {
517            info: self.info,
518            algorithm,
519            signature,
520        })
521    }
522}