x509_cert/builder.rs
1//! X509 Certificate builder
2
3use alloc::vec;
4use core::fmt;
5use der::{Encode, asn1::BitString, referenced::OwnedToRef};
6use signature::{
7 AsyncRandomizedSigner, AsyncSigner, Keypair, RandomizedSigner, Signer, rand_core::CryptoRng,
8};
9use spki::{
10 DynSignatureAlgorithmIdentifier, EncodePublicKey, ObjectIdentifier, SignatureBitStringEncoding,
11};
12
13use crate::{
14 AlgorithmIdentifier, SubjectPublicKeyInfo,
15 certificate::{self, Certificate, TbsCertificate, Version},
16 crl::{CertificateList, RevokedCert, TbsCertList},
17 ext::{
18 Extensions, ToExtension,
19 pkix::{AuthorityKeyIdentifier, CrlNumber, SubjectKeyIdentifier},
20 },
21 serial_number::SerialNumber,
22 time::{Time, Validity},
23};
24
25pub mod profile;
26
27use self::profile::BuilderProfile;
28
29#[deprecated(
30 since = "0.3.0",
31 note = "please use `x509_cert::builder::profile::BuilderProfile` instead"
32)]
33pub use self::profile::BuilderProfile as Profile;
34
35#[deprecated(
36 since = "0.3.0",
37 note = "please use `x509_cert::request::RequestBuilder` instead"
38)]
39pub use crate::request::RequestBuilder;
40
41pub(crate) const NULL_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("0.0.0");
42
43/// Error type
44#[derive(Debug)]
45#[non_exhaustive]
46pub enum Error {
47 /// ASN.1 DER-related errors.
48 Asn1(der::Error),
49
50 /// Public key errors propagated from the [`spki::Error`] type.
51 PublicKey(spki::Error),
52
53 /// Signing error propagated for the [`signature::Error`] type.
54 Signature(signature::Error),
55
56 /// Each RelativeDistinguishedName MUST contain exactly one AttributeTypeAndValue.
57 NonUniqueRdn,
58
59 /// Each Name MUST NOT contain more than one instance of a given
60 /// AttributeTypeAndValue across all RelativeDistinguishedNames unless explicitly
61 /// allowed in these Requirements
62 NonUniqueATV,
63
64 /// Non-ordered attribute or invalid attribute
65 InvalidAttribute {
66 /// Offending [`ObjectIdentifier`]
67 oid: ObjectIdentifier,
68 },
69
70 /// Not all required elements were specified
71 MissingAttributes,
72}
73
74impl core::error::Error for Error {}
75
76impl fmt::Display for Error {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 match self {
79 Error::Asn1(err) => write!(f, "ASN.1 error: {err}"),
80 Error::PublicKey(err) => write!(f, "public key error: {err}"),
81 Error::Signature(err) => write!(f, "signature error: {err}"),
82 Error::NonUniqueRdn => write!(
83 f,
84 "Each RelativeDistinguishedName MUST contain exactly one AttributeTypeAndValue."
85 ),
86 Error::NonUniqueATV => write!(
87 f,
88 "Each Name MUST NOT contain more than one instance of a given AttributeTypeAndValue"
89 ),
90 Error::InvalidAttribute { oid } => write!(
91 f,
92 "Non-ordered attribute or invalid attribute found (oid={oid})"
93 ),
94 Error::MissingAttributes => write!(f, "Not all required elements were specified"),
95 }
96 }
97}
98
99impl From<der::Error> for Error {
100 fn from(err: der::Error) -> Error {
101 Error::Asn1(err)
102 }
103}
104
105impl From<spki::Error> for Error {
106 fn from(err: spki::Error) -> Error {
107 Error::PublicKey(err)
108 }
109}
110
111impl From<signature::Error> for Error {
112 fn from(err: signature::Error) -> Error {
113 Error::Signature(err)
114 }
115}
116
117/// Result type
118pub type Result<T> = core::result::Result<T, Error>;
119
120/// X509 Certificate builder
121///
122#[cfg_attr(feature = "std", doc = "```")]
123#[cfg_attr(not(feature = "std"), doc = "```ignore")]
124/// use der::Decode;
125/// use x509_cert::spki::SubjectPublicKeyInfo;
126/// use x509_cert::builder::{CertificateBuilder, Builder, profile};
127/// use x509_cert::name::Name;
128/// use x509_cert::serial_number::SerialNumber;
129/// use x509_cert::time::Validity;
130/// use std::str::FromStr;
131///
132/// # const RSA_2048_DER: &[u8] = include_bytes!("../tests/examples/rsa2048-pub.der");
133/// # const RSA_2048_PRIV_DER: &[u8] = include_bytes!("../tests/examples/rsa2048-priv.der");
134/// # use rsa::{pkcs1v15::SigningKey, pkcs1::DecodeRsaPrivateKey};
135/// # use sha2::Sha256;
136/// # use std::time::Duration;
137/// # use der::referenced::RefToOwned;
138/// # fn rsa_signer() -> SigningKey<Sha256> {
139/// # let private_key = rsa::RsaPrivateKey::from_pkcs1_der(RSA_2048_PRIV_DER).unwrap();
140/// # let signing_key = SigningKey::<Sha256>::new(private_key);
141/// # signing_key
142/// # }
143///
144/// let serial_number = SerialNumber::from(42u32);
145/// let validity = Validity::from_now(Duration::new(5, 0)).unwrap();
146/// let subject = Name::from_str("CN=World domination corporation,O=World domination Inc,C=US").unwrap();
147/// let profile = profile::cabf::Root::new(false,subject).expect("Create root profile");
148///
149/// let pub_key = SubjectPublicKeyInfo::try_from(RSA_2048_DER).expect("get rsa pub key");
150///
151/// let mut signer = rsa_signer();
152/// let mut builder = CertificateBuilder::new(
153/// profile,
154/// serial_number,
155/// validity,
156/// pub_key,
157/// )
158/// .expect("Create certificate builder");
159///
160/// let cert = builder.build(&signer).expect("Create certificate");
161/// ```
162pub struct CertificateBuilder<P> {
163 tbs: TbsCertificate,
164 extensions: Extensions,
165 profile: P,
166}
167
168impl<P> CertificateBuilder<P>
169where
170 P: BuilderProfile,
171{
172 /// Creates a new certificate builder
173 pub fn new(
174 profile: P,
175 serial_number: SerialNumber,
176 mut validity: Validity,
177 subject_public_key_info: SubjectPublicKeyInfo,
178 ) -> Result<Self> {
179 let signature_alg = AlgorithmIdentifier {
180 oid: NULL_OID,
181 parameters: None,
182 };
183
184 let subject = profile.get_subject();
185 let issuer = profile.get_issuer(&subject);
186
187 validity.not_before.rfc5280_adjust_utc_time()?;
188 validity.not_after.rfc5280_adjust_utc_time()?;
189
190 let tbs = TbsCertificate {
191 version: Version::V3,
192 serial_number,
193 signature: signature_alg,
194 issuer,
195 validity,
196 subject,
197 subject_public_key_info,
198 extensions: None,
199
200 // We will not generate unique identifier because as per RFC5280 Section 4.1.2.8:
201 // CAs conforming to this profile MUST NOT generate
202 // certificates with unique identifiers.
203 //
204 // https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.8
205 issuer_unique_id: None,
206 subject_unique_id: None,
207 };
208
209 let extensions = Extensions::default();
210 Ok(Self {
211 tbs,
212 extensions,
213 profile,
214 })
215 }
216
217 /// Add an extension to this certificate
218 ///
219 /// Extensions need to implement [`ToExtension`], examples may be found in
220 /// in [`ToExtension` documentation](../ext/trait.ToExtension.html#examples) or
221 /// [the implementors](../ext/trait.ToExtension.html#implementors).
222 pub fn add_extension<E: ToExtension>(
223 &mut self,
224 extension: E,
225 ) -> core::result::Result<(), E::Error> {
226 let ext = extension.to_extension(&self.tbs.subject, &self.extensions)?;
227 self.extensions.push(ext);
228 Ok(())
229 }
230}
231
232/// Trait for X509 builders
233///
234/// This trait defines the interface between builder and the signers.
235pub trait Builder: Sized {
236 /// Type built by this builder
237 type Output: Sized;
238
239 /// Assemble the final object from signature.
240 fn assemble<S>(self, signature: BitString, signer: &S) -> Result<Self::Output>
241 where
242 S: Keypair + DynSignatureAlgorithmIdentifier,
243 S::VerifyingKey: EncodePublicKey;
244
245 /// Finalize and return a serialization of the object for signature.
246 fn finalize<S>(&mut self, signer: &S) -> Result<vec::Vec<u8>>
247 where
248 S: Keypair + DynSignatureAlgorithmIdentifier,
249 S::VerifyingKey: EncodePublicKey;
250
251 /// Run the object through the signer and build it.
252 ///
253 /// # Notes
254 ///
255 /// When using ECDSA signers, the `Signature` parameter will need to be explicit
256 /// as multiple implementation of [`signature::Signer`] with various signature
257 /// are available.
258 ///
259 /// This would look like:
260 #[cfg_attr(feature = "std", doc = "```no_run")]
261 #[cfg_attr(not(feature = "std"), doc = "```ignore")]
262 /// # use p256::elliptic_curve::Generate;
263 /// # use rand::rng;
264 /// # use std::{
265 /// # str::FromStr,
266 /// # time::Duration
267 /// # };
268 /// # use x509_cert::{
269 /// # builder::{self, CertificateBuilder, Builder},
270 /// # name::Name,
271 /// # serial_number::SerialNumber,
272 /// # spki::SubjectPublicKeyInfo,
273 /// # time::Validity
274 /// # };
275 /// #
276 /// # let mut rng = rng();
277 /// # let signer = p256::ecdsa::SigningKey::generate_from_rng(&mut rng);
278 /// # let builder = CertificateBuilder::new(
279 /// # builder::profile::cabf::Root::new(
280 /// # false,
281 /// # Name::from_str("CN=World domination corporation").unwrap()
282 /// # ).unwrap(),
283 /// # SerialNumber::from(42u32),
284 /// # Validity::from_now(Duration::new(5, 0)).unwrap(),
285 /// # SubjectPublicKeyInfo::from_key(signer.verifying_key()).unwrap()
286 /// # ).unwrap();
287 /// let certificate = builder.build::<_, ecdsa::der::Signature<_>>(&signer).unwrap();
288 /// ```
289 fn build<S, Signature>(mut self, signer: &S) -> Result<Self::Output>
290 where
291 S: Signer<Signature>,
292 S: Keypair + DynSignatureAlgorithmIdentifier,
293 S::VerifyingKey: EncodePublicKey,
294 Signature: SignatureBitStringEncoding,
295 {
296 let blob = self.finalize(signer)?;
297
298 let signature = signer.try_sign(&blob)?.to_bitstring()?;
299
300 self.assemble(signature, signer)
301 }
302
303 /// Run the object through the signer and build it.
304 ///
305 /// # Notes
306 ///
307 /// When using ECDSA signers, the `Signature` parameter will need to be explicit
308 /// as multiple implementation of [`signature::Signer`] with various signature
309 /// are available.
310 ///
311 /// This would look like:
312 #[cfg_attr(feature = "std", doc = "```no_run")]
313 #[cfg_attr(not(feature = "std"), doc = "```ignore")]
314 /// # use p256::elliptic_curve::Generate;
315 /// # use rand::rng;
316 /// # use std::{
317 /// # str::FromStr,
318 /// # time::Duration
319 /// # };
320 /// # use x509_cert::{
321 /// # builder::{self, CertificateBuilder, Builder},
322 /// # name::Name,
323 /// # serial_number::SerialNumber,
324 /// # spki::SubjectPublicKeyInfo,
325 /// # time::Validity
326 /// # };
327 /// #
328 /// # let mut rng = rng();
329 /// # let signer = p256::ecdsa::SigningKey::generate_from_rng(&mut rng);
330 /// # let builder = CertificateBuilder::new(
331 /// # builder::profile::cabf::Root::new(
332 /// # false,
333 /// # Name::from_str("CN=World domination corporation").unwrap()
334 /// # ).unwrap(),
335 /// # SerialNumber::from(42u32),
336 /// # Validity::from_now(Duration::new(5, 0)).unwrap(),
337 /// # SubjectPublicKeyInfo::from_key(signer.verifying_key()).unwrap()
338 /// # ).unwrap();
339 /// let certificate = builder.build_with_rng::<_, ecdsa::der::Signature<_>, _>(
340 /// &signer,
341 /// &mut rng
342 /// ).unwrap();
343 /// ```
344 fn build_with_rng<S, Signature, R>(mut self, signer: &S, rng: &mut R) -> Result<Self::Output>
345 where
346 S: RandomizedSigner<Signature>,
347 S: Keypair + DynSignatureAlgorithmIdentifier,
348 S::VerifyingKey: EncodePublicKey,
349 Signature: SignatureBitStringEncoding,
350 R: CryptoRng + ?Sized,
351 {
352 let blob = self.finalize(signer)?;
353
354 let signature = signer.try_sign_with_rng(rng, &blob)?.to_bitstring()?;
355
356 self.assemble(signature, signer)
357 }
358}
359
360impl<P> Builder for CertificateBuilder<P>
361where
362 P: BuilderProfile,
363{
364 type Output = Certificate;
365
366 fn finalize<S>(&mut self, cert_signer: &S) -> Result<vec::Vec<u8>>
367 where
368 S: Keypair + DynSignatureAlgorithmIdentifier,
369 S::VerifyingKey: EncodePublicKey,
370 {
371 let verifying_key = cert_signer.verifying_key();
372 let signer_pub = SubjectPublicKeyInfo::from_key(&verifying_key)?;
373
374 self.tbs.signature = cert_signer.signature_algorithm_identifier()?;
375
376 let mut default_extensions = self.profile.build_extensions(
377 self.tbs.subject_public_key_info.owned_to_ref(),
378 signer_pub.owned_to_ref(),
379 &self.tbs,
380 )?;
381
382 self.extensions.append(&mut default_extensions);
383
384 if !self.extensions.is_empty() {
385 self.tbs.extensions = Some(self.extensions.clone());
386 }
387
388 if self.tbs.extensions.is_none() {
389 if self.tbs.issuer_unique_id.is_some() || self.tbs.subject_unique_id.is_some() {
390 self.tbs.version = Version::V2;
391 } else {
392 self.tbs.version = Version::V1;
393 }
394 }
395
396 self.tbs.to_der().map_err(Error::from)
397 }
398
399 fn assemble<S>(self, signature: BitString, _signer: &S) -> Result<Self::Output>
400 where
401 S: Keypair + DynSignatureAlgorithmIdentifier,
402 S::VerifyingKey: EncodePublicKey,
403 {
404 let signature_algorithm = self.tbs.signature.clone();
405
406 Ok(Certificate {
407 tbs_certificate: self.tbs,
408 signature_algorithm,
409 signature,
410 })
411 }
412}
413
414/// Trait for async X509 builders
415///
416/// This trait defines the interface between builder and the signers.
417///
418/// This is the async counterpart of [`Builder`].
419#[allow(async_fn_in_trait)]
420pub trait AsyncBuilder: Sized {
421 /// Type built by this builder
422 type Output: Sized;
423
424 /// Assemble the final object from signature.
425 fn assemble<S>(self, signature: BitString, signer: &S) -> Result<Self::Output>
426 where
427 S: Keypair + DynSignatureAlgorithmIdentifier,
428 S::VerifyingKey: EncodePublicKey;
429
430 /// Finalize and return a serialization of the object for signature.
431 fn finalize<S>(&mut self, signer: &S) -> Result<vec::Vec<u8>>
432 where
433 S: Keypair + DynSignatureAlgorithmIdentifier,
434 S::VerifyingKey: EncodePublicKey;
435
436 /// Run the object through the signer and build it.
437 ///
438 /// # Notes
439 ///
440 /// When using ECDSA signers, the `Signature` parameter will need to be explicit
441 /// as multiple implementation of [`signature::AsyncSigner`] with various signature
442 /// are available.
443 ///
444 /// This would look like:
445 #[cfg_attr(feature = "std", doc = "```no_run")]
446 #[cfg_attr(not(feature = "std"), doc = "```ignore")]
447 /// # use p256::elliptic_curve::Generate;
448 /// # use rand::rng;
449 /// # use std::{
450 /// # str::FromStr,
451 /// # time::Duration
452 /// # };
453 /// # use x509_cert::{
454 /// # builder::{self, CertificateBuilder, AsyncBuilder},
455 /// # name::Name,
456 /// # serial_number::SerialNumber,
457 /// # spki::SubjectPublicKeyInfo,
458 /// # time::Validity
459 /// # };
460 /// #
461 /// # async fn build() -> builder::Result<()> {
462 /// # let mut rng = rng();
463 /// # let signer = p256::ecdsa::SigningKey::generate_from_rng(&mut rng);
464 /// # let builder = CertificateBuilder::new(
465 /// # builder::profile::cabf::Root::new(
466 /// # false,
467 /// # Name::from_str("CN=World domination corporation").unwrap()
468 /// # ).unwrap(),
469 /// # SerialNumber::from(42u32),
470 /// # Validity::from_now(Duration::new(5, 0)).unwrap(),
471 /// # SubjectPublicKeyInfo::from_key(signer.verifying_key()).unwrap()
472 /// # ).unwrap();
473 /// let certificate = builder.build_async::<_, ecdsa::der::Signature<_>>(&signer).await?;
474 /// # Ok(())
475 /// # }
476 /// ```
477 async fn build_async<S, Signature>(mut self, signer: &S) -> Result<Self::Output>
478 where
479 S: AsyncSigner<Signature>,
480 S: Keypair + DynSignatureAlgorithmIdentifier,
481 S::VerifyingKey: EncodePublicKey,
482 Signature: SignatureBitStringEncoding,
483 {
484 let blob = self.finalize(signer)?;
485
486 let signature = signer.sign_async(&blob).await?.to_bitstring()?;
487
488 self.assemble(signature, signer)
489 }
490
491 /// Run the object through the signer and build it.
492 ///
493 /// # Notes
494 ///
495 /// When using ECDSA signers, the `Signature` parameter will need to be explicit
496 /// as multiple implementation of [`signature::AsyncSigner`] with various signature
497 /// are available.
498 ///
499 /// This would look like:
500 #[cfg_attr(feature = "std", doc = "```no_run")]
501 #[cfg_attr(not(feature = "std"), doc = "```ignore")]
502 /// # use p256::elliptic_curve::Generate;
503 /// # use rand::rng;
504 /// # use std::{
505 /// # str::FromStr,
506 /// # time::Duration
507 /// # };
508 /// # use x509_cert::{
509 /// # builder::{self, CertificateBuilder, AsyncBuilder},
510 /// # name::Name,
511 /// # serial_number::SerialNumber,
512 /// # spki::SubjectPublicKeyInfo,
513 /// # time::Validity
514 /// # };
515 /// #
516 /// # async fn build() -> builder::Result<()> {
517 /// # let mut rng = rng();
518 /// # let signer = p256::ecdsa::SigningKey::generate_from_rng(&mut rng);
519 /// # let builder = CertificateBuilder::new(
520 /// # builder::profile::cabf::Root::new(
521 /// # false,
522 /// # Name::from_str("CN=World domination corporation").unwrap()
523 /// # ).unwrap(),
524 /// # SerialNumber::from(42u32),
525 /// # Validity::from_now(Duration::new(5, 0)).unwrap(),
526 /// # SubjectPublicKeyInfo::from_key(signer.verifying_key()).unwrap()
527 /// # ).unwrap();
528 /// let certificate = builder.build_with_rng_async::<_, ecdsa::der::Signature<_>, _>(&signer, &mut rng).await?;
529 /// # Ok(())
530 /// # }
531 /// ```
532 async fn build_with_rng_async<S, Signature, R>(
533 mut self,
534 signer: &S,
535 rng: &mut R,
536 ) -> Result<Self::Output>
537 where
538 S: AsyncRandomizedSigner<Signature>,
539 S: Keypair + DynSignatureAlgorithmIdentifier,
540 S::VerifyingKey: EncodePublicKey,
541 Signature: SignatureBitStringEncoding,
542 R: CryptoRng + ?Sized,
543 {
544 let blob = self.finalize(signer)?;
545
546 let signature = signer
547 .try_sign_with_rng_async(rng, &blob)
548 .await?
549 .to_bitstring()?;
550
551 self.assemble(signature, signer)
552 }
553}
554
555impl<T> AsyncBuilder for T
556where
557 T: Builder,
558{
559 type Output = <T as Builder>::Output;
560
561 fn assemble<S>(self, signature: BitString, signer: &S) -> Result<Self::Output>
562 where
563 S: Keypair + DynSignatureAlgorithmIdentifier,
564 S::VerifyingKey: EncodePublicKey,
565 {
566 <T as Builder>::assemble(self, signature, signer)
567 }
568
569 fn finalize<S>(&mut self, signer: &S) -> Result<vec::Vec<u8>>
570 where
571 S: Keypair + DynSignatureAlgorithmIdentifier,
572 S::VerifyingKey: EncodePublicKey,
573 {
574 <T as Builder>::finalize(self, signer)
575 }
576}
577
578/// X.509 CRL builder
579pub struct CrlBuilder<P = certificate::Rfc5280>
580where
581 P: certificate::Profile,
582{
583 tbs: TbsCertList<P>,
584}
585
586impl<P> CrlBuilder<P>
587where
588 P: certificate::Profile,
589{
590 /// Create a `CrlBuilder` with the given issuer and the given monotonic [`CrlNumber`]
591 #[cfg(feature = "std")]
592 pub fn new(issuer: &Certificate, crl_number: CrlNumber) -> der::Result<Self> {
593 let this_update = Time::now()?;
594 Self::new_with_this_update(issuer, crl_number, this_update)
595 }
596
597 /// Create a `CrlBuilder` with the given issuer, a given monotonic [`CrlNumber`], and valid
598 /// from the given `this_update` start validity date.
599 pub fn new_with_this_update(
600 issuer: &Certificate,
601 crl_number: CrlNumber,
602 this_update: Time,
603 ) -> der::Result<Self> {
604 // Replaced later when the finalize is called
605 let signature_alg = AlgorithmIdentifier {
606 oid: NULL_OID,
607 parameters: None,
608 };
609
610 let issuer_name = issuer.tbs_certificate.subject().clone();
611
612 let mut crl_extensions = Extensions::new();
613 crl_extensions.push(crl_number.to_extension(&issuer_name, &crl_extensions)?);
614 let aki = match issuer
615 .tbs_certificate
616 .get_extension::<AuthorityKeyIdentifier>()?
617 {
618 Some((_, aki)) => aki,
619 None => {
620 let ski = SubjectKeyIdentifier::try_from(
621 issuer
622 .tbs_certificate
623 .subject_public_key_info()
624 .owned_to_ref(),
625 )?;
626 AuthorityKeyIdentifier {
627 // KeyIdentifier must be the same as subjectKeyIdentifier
628 key_identifier: Some(ski.0.clone()),
629 // other fields must not be present.
630 ..Default::default()
631 }
632 }
633 };
634 crl_extensions.push(aki.to_extension(&issuer_name, &crl_extensions)?);
635
636 let tbs = TbsCertList {
637 version: Version::V2,
638 signature: signature_alg,
639 issuer: issuer_name,
640 this_update,
641 next_update: None,
642 revoked_certificates: None,
643 crl_extensions: Some(crl_extensions),
644 };
645
646 Ok(Self { tbs })
647 }
648
649 /// Make the CRL valid until the given `next_update`
650 pub fn with_next_update(mut self, next_update: Option<Time>) -> Self {
651 self.tbs.next_update = next_update;
652 self
653 }
654
655 /// Add certificates to the revocation list
656 pub fn with_certificates<I>(mut self, revoked: I) -> Self
657 where
658 I: Iterator<Item = RevokedCert<P>>,
659 {
660 let certificates = self
661 .tbs
662 .revoked_certificates
663 .get_or_insert_with(vec::Vec::new);
664
665 let mut revoked: vec::Vec<RevokedCert<P>> = revoked.collect();
666 certificates.append(&mut revoked);
667
668 self
669 }
670}
671
672impl<P> Builder for CrlBuilder<P>
673where
674 P: certificate::Profile,
675{
676 type Output = CertificateList<P>;
677
678 fn finalize<S>(&mut self, cert_signer: &S) -> Result<vec::Vec<u8>>
679 where
680 S: Keypair + DynSignatureAlgorithmIdentifier,
681 S::VerifyingKey: EncodePublicKey,
682 {
683 self.tbs.signature = cert_signer.signature_algorithm_identifier()?;
684
685 self.tbs.to_der().map_err(Error::from)
686 }
687
688 fn assemble<S>(self, signature: BitString, _signer: &S) -> Result<Self::Output>
689 where
690 S: Keypair + DynSignatureAlgorithmIdentifier,
691 S::VerifyingKey: EncodePublicKey,
692 {
693 let signature_algorithm = self.tbs.signature.clone();
694
695 Ok(CertificateList {
696 tbs_cert_list: self.tbs,
697 signature_algorithm,
698 signature,
699 })
700 }
701}