1 //! X509 Certificate builder 2 3 use alloc::vec; 4 use core::fmt; 5 use der::{asn1::BitString, referenced::OwnedToRef, Encode}; 6 use signature::{rand_core::CryptoRngCore, Keypair, RandomizedSigner, Signer}; 7 use spki::{ 8 DynSignatureAlgorithmIdentifier, EncodePublicKey, SignatureBitStringEncoding, 9 SubjectPublicKeyInfoOwned, SubjectPublicKeyInfoRef, 10 }; 11 12 use 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] 29 pub 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")] 41 impl std::error::Error for Error {} 42 43 impl fmt::Display for Error { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result44 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 53 impl From<der::Error> for Error { from(err: der::Error) -> Error54 fn from(err: der::Error) -> Error { 55 Error::Asn1(err) 56 } 57 } 58 59 impl From<spki::Error> for Error { from(err: spki::Error) -> Error60 fn from(err: spki::Error) -> Error { 61 Error::PublicKey(err) 62 } 63 } 64 65 impl From<signature::Error> for Error { from(err: signature::Error) -> Error66 fn from(err: signature::Error) -> Error { 67 Error::Signature(err) 68 } 69 } 70 71 type Result<T> = core::result::Result<T, Error>; 72 73 /// The type of certificate to build 74 #[derive(Clone, Debug, Eq, PartialEq)] 75 pub 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 115 impl Profile { get_issuer(&self, subject: &Name) -> Name116 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 build_extensions( &self, spk: SubjectPublicKeyInfoRef<'_>, issuer_spk: SubjectPublicKeyInfoRef<'_>, tbs: &TbsCertificate, ) -> Result<vec::Vec<Extension>>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 /// ``` 258 pub struct CertificateBuilder<'s, S> { 259 tbs: TbsCertificate, 260 extensions: Extensions, 261 cert_signer: &'s S, 262 } 263 264 impl<'s, S> CertificateBuilder<'s, S> 265 where 266 S: Keypair + DynSignatureAlgorithmIdentifier, 267 S::VerifyingKey: EncodePublicKey, 268 { 269 /// Creates a new certificate builder new( profile: Profile, serial_number: SerialNumber, mut validity: Validity, subject: Name, subject_public_key_info: SubjectPublicKeyInfoOwned, cert_signer: &'s S, ) -> Result<Self>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 add_extension<E: AsExtension>(&mut self, extension: &E) -> Result<()>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 /// ``` 356 pub struct RequestBuilder<'s, S> { 357 info: CertReqInfo, 358 extension_req: ExtensionReq, 359 req_signer: &'s S, 360 } 361 362 impl<'s, S> RequestBuilder<'s, S> 363 where 364 S: Keypair + DynSignatureAlgorithmIdentifier, 365 S::VerifyingKey: EncodePublicKey, 366 { 367 /// Creates a new certificate request builder new(subject: Name, req_signer: &'s S) -> Result<Self>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 add_extension<E: AsExtension>(&mut self, extension: &E) -> Result<()>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 add_attribute<A: AsAttribute>(&mut self, attribute: &A) -> Result<()>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. 408 pub 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. signer(&self) -> &Self::Signer416 fn signer(&self) -> &Self::Signer; 417 418 /// Assemble the final object from signature. assemble(self, signature: BitString) -> Result<Self::Output>419 fn assemble(self, signature: BitString) -> Result<Self::Output>; 420 421 /// Finalize and return a serialization of the object for signature. finalize(&mut self) -> der::Result<vec::Vec<u8>>422 fn finalize(&mut self) -> der::Result<vec::Vec<u8>>; 423 424 /// Run the object through the signer and build it. build<Signature>(mut self) -> Result<Self::Output> where Self::Signer: Signer<Signature>, Signature: SignatureBitStringEncoding,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. build_with_rng<Signature>(mut self, rng: &mut impl CryptoRngCore) -> Result<Self::Output> where Self::Signer: RandomizedSigner<Signature>, Signature: SignatureBitStringEncoding,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 454 impl<'s, S> Builder for CertificateBuilder<'s, S> 455 where 456 S: Keypair + DynSignatureAlgorithmIdentifier, 457 S::VerifyingKey: EncodePublicKey, 458 { 459 type Signer = S; 460 type Output = Certificate; 461 signer(&self) -> &Self::Signer462 fn signer(&self) -> &Self::Signer { 463 self.cert_signer 464 } 465 finalize(&mut self) -> der::Result<vec::Vec<u8>>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 assemble(self, signature: BitString) -> Result<Self::Output>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 493 impl<'s, S> Builder for RequestBuilder<'s, S> 494 where 495 S: Keypair + DynSignatureAlgorithmIdentifier, 496 S::VerifyingKey: EncodePublicKey, 497 { 498 type Signer = S; 499 type Output = CertReq; 500 signer(&self) -> &Self::Signer501 fn signer(&self) -> &Self::Signer { 502 self.req_signer 503 } 504 finalize(&mut self) -> der::Result<vec::Vec<u8>>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 assemble(self, signature: BitString) -> Result<Self::Output>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 } 523