blob: ec51a7e35fb466ae583faffd911ead5648806367 [file] [log] [blame]
Andrew Walbrand2a24d52023-12-15 16:04:54 +00001//! 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 = verifying_key
280 .to_public_key_der()?
281 .decode_msg::<SubjectPublicKeyInfoOwned>()?;
282
283 let signature_alg = cert_signer.signature_algorithm_identifier()?;
284 let issuer = profile.get_issuer(&subject);
285
286 validity.not_before.rfc5280_adjust_utc_time()?;
287 validity.not_after.rfc5280_adjust_utc_time()?;
288
289 let tbs = TbsCertificate {
290 version: Version::V3,
291 serial_number,
292 signature: signature_alg,
293 issuer,
294 validity,
295 subject,
296 subject_public_key_info,
297 extensions: None,
298
299 // We will not generate unique identifier because as per RFC5280 Section 4.1.2.8:
300 // CAs conforming to this profile MUST NOT generate
301 // certificates with unique identifiers.
302 //
303 // https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.8
304 issuer_unique_id: None,
305 subject_unique_id: None,
306 };
307
308 let extensions = profile.build_extensions(
309 tbs.subject_public_key_info.owned_to_ref(),
310 signer_pub.owned_to_ref(),
311 &tbs,
312 )?;
313 Ok(Self {
314 tbs,
315 extensions,
316 cert_signer,
317 })
318 }
319
320 /// Add an extension to this certificate
321 pub fn add_extension<E: AsExtension>(&mut self, extension: &E) -> Result<()> {
322 let ext = extension.to_extension(&self.tbs.subject, &self.extensions)?;
323 self.extensions.push(ext);
324
325 Ok(())
326 }
327}
328
329/// Builder for X509 Certificate Requests
330///
331/// ```
332/// # use p256::{pkcs8::DecodePrivateKey, NistP256, ecdsa::DerSignature};
333/// # const PKCS8_PRIVATE_KEY_DER: &[u8] = include_bytes!("../tests/examples/p256-priv.der");
334/// # fn ecdsa_signer() -> ecdsa::SigningKey<NistP256> {
335/// # let secret_key = p256::SecretKey::from_pkcs8_der(PKCS8_PRIVATE_KEY_DER).unwrap();
336/// # ecdsa::SigningKey::from(secret_key)
337/// # }
338/// use x509_cert::{
339/// builder::{Builder, RequestBuilder},
340/// ext::pkix::{name::GeneralName, SubjectAltName},
341/// name::Name,
342/// };
343/// use std::str::FromStr;
344///
345/// use std::net::{IpAddr, Ipv4Addr};
346/// let subject = Name::from_str("CN=service.domination.world").unwrap();
347///
348/// let signer = ecdsa_signer();
349/// let mut builder = RequestBuilder::new(subject, &signer).expect("Create certificate request");
350/// builder
351/// .add_extension(&SubjectAltName(vec![GeneralName::from(IpAddr::V4(
352/// Ipv4Addr::new(192, 0, 2, 0),
353/// ))]))
354/// .unwrap();
355///
356/// let cert_req = builder.build::<DerSignature>().unwrap();
357/// ```
358pub struct RequestBuilder<'s, S> {
359 info: CertReqInfo,
360 extension_req: ExtensionReq,
361 req_signer: &'s S,
362}
363
364impl<'s, S> RequestBuilder<'s, S>
365where
366 S: Keypair + DynSignatureAlgorithmIdentifier,
367 S::VerifyingKey: EncodePublicKey,
368{
369 /// Creates a new certificate request builder
370 pub fn new(subject: Name, req_signer: &'s S) -> Result<Self> {
371 let version = Default::default();
372 let verifying_key = req_signer.verifying_key();
373 let public_key = verifying_key
374 .to_public_key_der()?
375 .decode_msg::<SubjectPublicKeyInfoOwned>()?;
376 let attributes = Default::default();
377 let extension_req = Default::default();
378
379 Ok(Self {
380 info: CertReqInfo {
381 version,
382 subject,
383 public_key,
384 attributes,
385 },
386 extension_req,
387 req_signer,
388 })
389 }
390
391 /// Add an extension to this certificate request
392 pub fn add_extension<E: AsExtension>(&mut self, extension: &E) -> Result<()> {
393 let ext = extension.to_extension(&self.info.subject, &self.extension_req.0)?;
394
395 self.extension_req.0.push(ext);
396
397 Ok(())
398 }
399
400 /// Add an attribute to this certificate request
401 pub fn add_attribute<A: AsAttribute>(&mut self, attribute: &A) -> Result<()> {
402 let attr = attribute.to_attribute()?;
403
404 self.info.attributes.insert(attr)?;
405 Ok(())
406 }
407}
408
409/// Trait for X509 builders
410///
411/// This trait defines the interface between builder and the signers.
412pub trait Builder: Sized {
413 /// The builder's object signer
414 type Signer;
415
416 /// Type built by this builder
417 type Output: Sized;
418
419 /// Return a reference to the signer.
420 fn signer(&self) -> &Self::Signer;
421
422 /// Assemble the final object from signature.
423 fn assemble(self, signature: BitString) -> Result<Self::Output>;
424
425 /// Finalize and return a serialization of the object for signature.
426 fn finalize(&mut self) -> der::Result<vec::Vec<u8>>;
427
428 /// Run the object through the signer and build it.
429 fn build<Signature>(mut self) -> Result<Self::Output>
430 where
431 Self::Signer: Signer<Signature>,
432 Signature: SignatureBitStringEncoding,
433 {
434 let blob = self.finalize()?;
435
436 let signature = self.signer().try_sign(&blob)?.to_bitstring()?;
437
438 self.assemble(signature)
439 }
440
441 /// Run the object through the signer and build it.
442 fn build_with_rng<Signature>(mut self, rng: &mut impl CryptoRngCore) -> Result<Self::Output>
443 where
444 Self::Signer: RandomizedSigner<Signature>,
445 Signature: SignatureBitStringEncoding,
446 {
447 let blob = self.finalize()?;
448
449 let signature = self
450 .signer()
451 .try_sign_with_rng(rng, &blob)?
452 .to_bitstring()?;
453
454 self.assemble(signature)
455 }
456}
457
458impl<'s, S> Builder for CertificateBuilder<'s, S>
459where
460 S: Keypair + DynSignatureAlgorithmIdentifier,
461 S::VerifyingKey: EncodePublicKey,
462{
463 type Signer = S;
464 type Output = Certificate;
465
466 fn signer(&self) -> &Self::Signer {
467 self.cert_signer
468 }
469
470 fn finalize(&mut self) -> der::Result<vec::Vec<u8>> {
471 if !self.extensions.is_empty() {
472 self.tbs.extensions = Some(self.extensions.clone());
473 }
474
475 if self.tbs.extensions.is_none() {
476 if self.tbs.issuer_unique_id.is_some() || self.tbs.subject_unique_id.is_some() {
477 self.tbs.version = Version::V2;
478 } else {
479 self.tbs.version = Version::V1;
480 }
481 }
482
483 self.tbs.to_der()
484 }
485
486 fn assemble(self, signature: BitString) -> Result<Self::Output> {
487 let signature_algorithm = self.tbs.signature.clone();
488
489 Ok(Certificate {
490 tbs_certificate: self.tbs,
491 signature_algorithm,
492 signature,
493 })
494 }
495}
496
497impl<'s, S> Builder for RequestBuilder<'s, S>
498where
499 S: Keypair + DynSignatureAlgorithmIdentifier,
500 S::VerifyingKey: EncodePublicKey,
501{
502 type Signer = S;
503 type Output = CertReq;
504
505 fn signer(&self) -> &Self::Signer {
506 self.req_signer
507 }
508
509 fn finalize(&mut self) -> der::Result<vec::Vec<u8>> {
510 self.info
511 .attributes
512 .insert(self.extension_req.clone().try_into()?)?;
513
514 self.info.to_der()
515 }
516
517 fn assemble(self, signature: BitString) -> Result<Self::Output> {
518 let algorithm = self.req_signer.signature_algorithm_identifier()?;
519
520 Ok(CertReq {
521 info: self.info,
522 algorithm,
523 signature,
524 })
525 }
526}