|
| 1 | +//! X509 Certificate builder |
| 2 | +
|
| 3 | +use alloc::vec::Vec; |
| 4 | +use der::asn1::{BitString, OctetString}; |
| 5 | +use sha1::{Digest, Sha1}; |
| 6 | +use spki::{AlgorithmIdentifierOwned, SubjectPublicKeyInfoOwned}; |
| 7 | + |
| 8 | +use crate::{ |
| 9 | + certificate::{Certificate, TbsCertificate, Version}, |
| 10 | + ext::{ |
| 11 | + pkix::{ |
| 12 | + AuthorityKeyIdentifier, BasicConstraints, KeyUsage, KeyUsages, SubjectKeyIdentifier, |
| 13 | + }, |
| 14 | + AsExtension, Extension, |
| 15 | + }, |
| 16 | + name::Name, |
| 17 | + serial_number::SerialNumber, |
| 18 | + time::Validity, |
| 19 | +}; |
| 20 | + |
| 21 | +type Result<T> = core::result::Result<T, der::Error>; |
| 22 | + |
| 23 | +/// UniqueIds holds the optional attributes `issuerUniqueID` and `subjectUniqueID` |
| 24 | +/// to be filled in the TBSCertificate if version v2 or v3. |
| 25 | +/// |
| 26 | +/// See X.509 `TbsCertificate` as defined in [RFC 5280 Section 4.1] |
| 27 | +pub struct UniqueIds { |
| 28 | + /// ```text |
| 29 | + /// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, |
| 30 | + /// -- If present, version MUST be v2 or v3 |
| 31 | + /// ``` |
| 32 | + pub issuer_unique_id: Option<BitString>, |
| 33 | + /// ```text |
| 34 | + /// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, |
| 35 | + /// -- If present, version MUST be v2 or v3 |
| 36 | + /// ``` |
| 37 | + pub subject_unique_id: Option<BitString>, |
| 38 | +} |
| 39 | + |
| 40 | +impl UniqueIds { |
| 41 | + fn get_unique_ids(&self) -> (Option<BitString>, Option<BitString>) { |
| 42 | + ( |
| 43 | + self.issuer_unique_id.clone(), |
| 44 | + self.subject_unique_id.clone(), |
| 45 | + ) |
| 46 | + } |
| 47 | +} |
| 48 | + |
| 49 | +/// The type of certificate to build |
| 50 | +#[derive(Clone, Debug, Eq, PartialEq)] |
| 51 | +pub enum Profile { |
| 52 | + /// Build a root CA certificate |
| 53 | + Root, |
| 54 | + /// Build an intermediate sub CA certificate |
| 55 | + SubCA { |
| 56 | + /// issuer Name, |
| 57 | + /// represents the name signing the certificate |
| 58 | + issuer: Name, |
| 59 | + /// pathLenConstraint INTEGER (0..MAX) OPTIONAL |
| 60 | + /// BasicConstraints as defined in [RFC 5280 Section 4.2.1.9]. |
| 61 | + path_len_constraint: Option<u8>, |
| 62 | + }, |
| 63 | + /// Build an end certificate |
| 64 | + Leaf { |
| 65 | + /// issuer Name, |
| 66 | + /// represents the name signing the certificate |
| 67 | + issuer: Name, |
| 68 | + }, |
| 69 | +} |
| 70 | + |
| 71 | +impl Profile { |
| 72 | + fn get_issuer(&self, subject: &Name) -> Name { |
| 73 | + match self { |
| 74 | + Profile::Root => subject.clone(), |
| 75 | + Profile::SubCA { issuer, .. } => issuer.clone(), |
| 76 | + Profile::Leaf { issuer } => issuer.clone(), |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | + fn build_extensions( |
| 81 | + &self, |
| 82 | + spk: &SubjectPublicKeyInfoOwned, |
| 83 | + issuer_spk: &SubjectPublicKeyInfoOwned, |
| 84 | + ) -> Result<Vec<Extension>> { |
| 85 | + let mut extensions: Vec<Extension> = Vec::new(); |
| 86 | + |
| 87 | + extensions.push({ |
| 88 | + let result = Sha1::digest(spk.subject_public_key.raw_bytes()); |
| 89 | + SubjectKeyIdentifier(OctetString::new(result.to_vec())?).to_extension()? |
| 90 | + }); |
| 91 | + |
| 92 | + // Build Authority Key Identifier |
| 93 | + match self { |
| 94 | + Profile::Root => {} |
| 95 | + _ => { |
| 96 | + let mut hasher = Sha1::new(); |
| 97 | + hasher.update(issuer_spk.subject_public_key.raw_bytes()); |
| 98 | + let result = hasher.finalize(); |
| 99 | + |
| 100 | + extensions.push( |
| 101 | + AuthorityKeyIdentifier { |
| 102 | + key_identifier: Some(OctetString::new(result.to_vec())?), |
| 103 | + authority_cert_issuer: None, |
| 104 | + authority_cert_serial_number: None, |
| 105 | + } |
| 106 | + .to_extension()?, |
| 107 | + ); |
| 108 | + } |
| 109 | + } |
| 110 | + |
| 111 | + // Build Basic Contraints extensions |
| 112 | + extensions.push(match self { |
| 113 | + Profile::Root => BasicConstraints { |
| 114 | + ca: true, |
| 115 | + path_len_constraint: None, |
| 116 | + } |
| 117 | + .to_extension()?, |
| 118 | + Profile::SubCA { |
| 119 | + path_len_constraint, |
| 120 | + .. |
| 121 | + } => BasicConstraints { |
| 122 | + ca: true, |
| 123 | + path_len_constraint: *path_len_constraint, |
| 124 | + } |
| 125 | + .to_extension()?, |
| 126 | + Profile::Leaf { .. } => BasicConstraints { |
| 127 | + ca: false, |
| 128 | + path_len_constraint: None, |
| 129 | + } |
| 130 | + .to_extension()?, |
| 131 | + }); |
| 132 | + |
| 133 | + // Build Key Usage extension |
| 134 | + match self { |
| 135 | + Profile::Root | Profile::SubCA { .. } => { |
| 136 | + extensions.push( |
| 137 | + KeyUsage( |
| 138 | + KeyUsages::DigitalSignature | KeyUsages::KeyCertSign | KeyUsages::CRLSign, |
| 139 | + ) |
| 140 | + .to_extension()?, |
| 141 | + ); |
| 142 | + } |
| 143 | + Profile::Leaf { .. } => { |
| 144 | + //todo!(); |
| 145 | + } |
| 146 | + } |
| 147 | + |
| 148 | + Ok(extensions) |
| 149 | + } |
| 150 | +} |
| 151 | + |
| 152 | +/// The version of the Certificate to build. |
| 153 | +/// All newly built certificate should use `CertificateVersion::V3` |
| 154 | +pub enum CertificateVersion { |
| 155 | + /// Generate a X509 version 1 |
| 156 | + V1, |
| 157 | + /// Generate a X509 version 2 |
| 158 | + V2(UniqueIds), |
| 159 | + /// Generate a X509 version 3 |
| 160 | + V3(UniqueIds), |
| 161 | +} |
| 162 | + |
| 163 | +impl From<CertificateVersion> for Version { |
| 164 | + fn from(cv: CertificateVersion) -> Version { |
| 165 | + use CertificateVersion::*; |
| 166 | + match cv { |
| 167 | + V1 => Version::V1, |
| 168 | + V2(_) => Version::V2, |
| 169 | + V3(_) => Version::V3, |
| 170 | + } |
| 171 | + } |
| 172 | +} |
| 173 | + |
| 174 | +/// X509 Certificate builder |
| 175 | +/// |
| 176 | +/// ``` |
| 177 | +/// use der::Decode; |
| 178 | +/// use x509_cert::spki::SubjectPublicKeyInfoOwned; |
| 179 | +/// use x509_cert::builder::{CertificateBuilder, CertificateVersion, Profile, UniqueIds}; |
| 180 | +/// use x509_cert::name::Name; |
| 181 | +/// use x509_cert::serial_number::SerialNumber; |
| 182 | +/// use x509_cert::time::Validity; |
| 183 | +/// |
| 184 | +/// # use std::time::Duration; |
| 185 | +/// # use x509_cert::constants; |
| 186 | +/// # use x509_cert::certificate::TbsCertificate; |
| 187 | +/// # use x509_cert::builder::Signer; |
| 188 | +/// # const RSA_2048_DER: &[u8] = include_bytes!("../tests/examples/rsa2048-pub.der"); |
| 189 | +/// |
| 190 | +/// # struct RsaCertSigner; |
| 191 | +/// # impl Signer for RsaCertSigner { |
| 192 | +/// # type Alg = constants::RsaWithSha256; |
| 193 | +/// |
| 194 | +/// # fn signature_algorithm(&self) -> Self::Alg { |
| 195 | +/// # constants::RsaWithSha256 |
| 196 | +/// # } |
| 197 | +/// |
| 198 | +/// # fn public_key(&self) -> SubjectPublicKeyInfoOwned { |
| 199 | +/// # SubjectPublicKeyInfoOwned::try_from(RSA_2048_DER).expect("get rsa pub key") |
| 200 | +/// # } |
| 201 | +/// |
| 202 | +/// # fn sign(&self, input: &TbsCertificate) -> Vec<u8> { |
| 203 | +/// # todo!(); |
| 204 | +/// # } |
| 205 | +/// # } |
| 206 | +/// |
| 207 | +/// let uids = UniqueIds { |
| 208 | +/// issuer_unique_id: None, |
| 209 | +/// subject_unique_id: None, |
| 210 | +/// }; |
| 211 | +/// |
| 212 | +/// let serial_number = SerialNumber::from(42u32); |
| 213 | +/// let validity = Validity::from_now(Duration::new(5, 0)).unwrap(); |
| 214 | +/// let profile = Profile::Root; |
| 215 | +/// let subject = |
| 216 | +/// Name::encode_from_string("CN=World domination corporation,O=World domination Inc,C=US") |
| 217 | +/// .unwrap(); |
| 218 | +/// let subject = Name::from_der(&subject).unwrap(); |
| 219 | +/// let pub_key = SubjectPublicKeyInfoOwned::try_from(RSA_2048_DER).expect("get rsa pub key"); |
| 220 | +/// |
| 221 | +/// let mut builder = CertificateBuilder::new( |
| 222 | +/// profile, |
| 223 | +/// CertificateVersion::V3(uids), |
| 224 | +/// serial_number, |
| 225 | +/// validity, |
| 226 | +/// subject, |
| 227 | +/// pub_key, |
| 228 | +/// &RsaCertSigner, |
| 229 | +/// ) |
| 230 | +/// .expect("Create certificate"); |
| 231 | +/// ``` |
| 232 | +pub struct CertificateBuilder<'s, S: Signer> { |
| 233 | + version: Version, |
| 234 | + serial_number: SerialNumber, |
| 235 | + signature_alg: AlgorithmIdentifierOwned, |
| 236 | + issuer: Name, |
| 237 | + validity: Validity, |
| 238 | + subject: Name, |
| 239 | + subject_public_key_info: SubjectPublicKeyInfoOwned, |
| 240 | + issuer_unique_id: Option<BitString>, |
| 241 | + subject_unique_id: Option<BitString>, |
| 242 | + |
| 243 | + extensions: Vec<Extension>, |
| 244 | + |
| 245 | + signer: &'s S, |
| 246 | +} |
| 247 | + |
| 248 | +impl<'s, S: Signer> CertificateBuilder<'s, S> { |
| 249 | + /// Creates a new certificate builder |
| 250 | + pub fn new( |
| 251 | + profile: Profile, |
| 252 | + version: CertificateVersion, |
| 253 | + serial_number: SerialNumber, |
| 254 | + mut validity: Validity, |
| 255 | + subject: Name, |
| 256 | + subject_public_key_info: SubjectPublicKeyInfoOwned, |
| 257 | + signer: &'s S, |
| 258 | + ) -> Result<Self> { |
| 259 | + let signer_pub = signer.public_key(); |
| 260 | + |
| 261 | + let signature_alg = signer.signature_algorithm().identifier(); |
| 262 | + let issuer = profile.get_issuer(&subject); |
| 263 | + |
| 264 | + validity.not_before.rfc5280_adjust_utc_time()?; |
| 265 | + validity.not_after.rfc5280_adjust_utc_time()?; |
| 266 | + |
| 267 | + let (version, (issuer_unique_id, subject_unique_id), extensions) = match version { |
| 268 | + CertificateVersion::V1 => (Version::V1, (None, None), Vec::new()), |
| 269 | + CertificateVersion::V2(uids) => (Version::V2, uids.get_unique_ids(), Vec::new()), |
| 270 | + CertificateVersion::V3(uids) => ( |
| 271 | + Version::V3, |
| 272 | + uids.get_unique_ids(), |
| 273 | + profile.build_extensions(&subject_public_key_info, &signer_pub)?, |
| 274 | + ), |
| 275 | + }; |
| 276 | + |
| 277 | + Ok(Self { |
| 278 | + version, |
| 279 | + issuer_unique_id, |
| 280 | + subject_unique_id, |
| 281 | + serial_number, |
| 282 | + issuer, |
| 283 | + subject, |
| 284 | + validity, |
| 285 | + subject_public_key_info, |
| 286 | + signature_alg, |
| 287 | + extensions, |
| 288 | + signer, |
| 289 | + }) |
| 290 | + } |
| 291 | + |
| 292 | + /// Add an extension to this certificate |
| 293 | + pub fn add_extension<E: AsExtension>(&mut self, extension: &E) -> Result<()> { |
| 294 | + self.extensions.push(extension.to_extension()?); |
| 295 | + Ok(()) |
| 296 | + } |
| 297 | + |
| 298 | + /// Run the certificate through the signer and build the end certificate. |
| 299 | + pub fn build(&mut self) -> Result<Certificate> { |
| 300 | + let extensions = if self.version == Version::V3 { |
| 301 | + Some(self.extensions.clone()) |
| 302 | + } else { |
| 303 | + None |
| 304 | + }; |
| 305 | + |
| 306 | + let tbs_certificate = TbsCertificate { |
| 307 | + version: self.version, |
| 308 | + serial_number: self.serial_number.clone(), |
| 309 | + signature: self.signature_alg.clone(), |
| 310 | + issuer: self.issuer.clone(), |
| 311 | + validity: self.validity, |
| 312 | + subject: self.subject.clone(), |
| 313 | + subject_public_key_info: self.subject_public_key_info.clone(), |
| 314 | + issuer_unique_id: self.issuer_unique_id.clone(), |
| 315 | + subject_unique_id: self.subject_unique_id.clone(), |
| 316 | + extensions, |
| 317 | + }; |
| 318 | + |
| 319 | + let signature = BitString::from_bytes(&self.signer.sign(&tbs_certificate))?; |
| 320 | + |
| 321 | + let cert = Certificate { |
| 322 | + tbs_certificate, |
| 323 | + signature_algorithm: self.signature_alg.clone(), |
| 324 | + signature, |
| 325 | + }; |
| 326 | + |
| 327 | + Ok(cert) |
| 328 | + } |
| 329 | +} |
| 330 | + |
| 331 | +/// The certificate algorithm used to sign the certificate |
| 332 | +pub trait CertSignatureAlgorithm { |
| 333 | + /// Returns the `AlgorithmIdentifierOwned` used to sign the certificate |
| 334 | + fn identifier(&self) -> AlgorithmIdentifierOwned; |
| 335 | +} |
| 336 | + |
| 337 | +/// Signer to be used to for signing the certificates |
| 338 | +pub trait Signer { |
| 339 | + /// The type of signature expected from this signer |
| 340 | + type Alg: CertSignatureAlgorithm; |
| 341 | + |
| 342 | + /// The signature expected from this signer |
| 343 | + fn signature_algorithm(&self) -> Self::Alg; |
| 344 | + |
| 345 | + /// The SPKI encoded public key used by this signer |
| 346 | + fn public_key(&self) -> SubjectPublicKeyInfoOwned; |
| 347 | + |
| 348 | + /// The sign method should return the signature of the payload. |
| 349 | + fn sign(&self, input: &TbsCertificate) -> Vec<u8>; |
| 350 | +} |
0 commit comments