1# This file is dual licensed under the terms of the Apache License, Version
2# 2.0, and the BSD License. See the LICENSE file in the root of this repository
3# for complete details.
4
5from __future__ import annotations
6
7import datetime
8import os
9import typing
10from collections.abc import Iterable
11
12from cryptography import utils
13from cryptography.hazmat.bindings._rust import x509 as rust_x509
14from cryptography.hazmat.primitives import hashes
15from cryptography.hazmat.primitives.asymmetric import (
16 dsa,
17 ec,
18 ed448,
19 ed25519,
20 mldsa,
21 padding,
22 rsa,
23 x448,
24 x25519,
25)
26from cryptography.hazmat.primitives.asymmetric.types import (
27 CertificateIssuerPrivateKeyTypes,
28 CertificatePublicKeyTypes,
29)
30from cryptography.x509.extensions import (
31 Extension,
32 ExtensionType,
33 _make_sequence_methods,
34)
35from cryptography.x509.name import Name, _ASN1Type
36from cryptography.x509.oid import ObjectIdentifier
37
38_EARLIEST_UTC_TIME = datetime.datetime(1950, 1, 1)
39
40# This must be kept in sync with sign.rs's list of allowable types in
41# identify_hash_type
42_AllowedHashTypes = typing.Union[
43 hashes.SHA224,
44 hashes.SHA256,
45 hashes.SHA384,
46 hashes.SHA512,
47 hashes.SHA3_224,
48 hashes.SHA3_256,
49 hashes.SHA3_384,
50 hashes.SHA3_512,
51]
52
53
54class AttributeNotFound(Exception):
55 def __init__(self, msg: str, oid: ObjectIdentifier) -> None:
56 super().__init__(msg)
57 self.oid = oid
58
59
60def _reject_duplicate_extension(
61 extension: Extension[ExtensionType],
62 extensions: list[Extension[ExtensionType]],
63) -> None:
64 # This is quadratic in the number of extensions
65 for e in extensions:
66 if e.oid == extension.oid:
67 raise ValueError("This extension has already been set.")
68
69
70def _reject_duplicate_attribute(
71 oid: ObjectIdentifier,
72 attributes: list[tuple[ObjectIdentifier, bytes, int | None]],
73) -> None:
74 # This is quadratic in the number of attributes
75 for attr_oid, _, _ in attributes:
76 if attr_oid == oid:
77 raise ValueError("This attribute has already been set.")
78
79
80def _convert_to_naive_utc_time(time: datetime.datetime) -> datetime.datetime:
81 """Normalizes a datetime to a naive datetime in UTC.
82
83 time -- datetime to normalize. Assumed to be in UTC if not timezone
84 aware.
85 """
86 if time.tzinfo is not None:
87 offset = time.utcoffset()
88 offset = offset if offset else datetime.timedelta()
89 return time.replace(tzinfo=None) - offset
90 else:
91 return time
92
93
94class Attribute:
95 def __init__(
96 self,
97 oid: ObjectIdentifier,
98 value: bytes,
99 _type: int = _ASN1Type.UTF8String.value,
100 ) -> None:
101 self._oid = oid
102 self._value = value
103 self._type = _type
104
105 @property
106 def oid(self) -> ObjectIdentifier:
107 return self._oid
108
109 @property
110 def value(self) -> bytes:
111 return self._value
112
113 def __repr__(self) -> str:
114 return f"<Attribute(oid={self.oid}, value={self.value!r})>"
115
116 def __eq__(self, other: object) -> bool:
117 if not isinstance(other, Attribute):
118 return NotImplemented
119
120 return (
121 self.oid == other.oid
122 and self.value == other.value
123 and self._type == other._type
124 )
125
126 def __hash__(self) -> int:
127 return hash((self.oid, self.value, self._type))
128
129
130class Attributes:
131 def __init__(
132 self,
133 attributes: Iterable[Attribute],
134 ) -> None:
135 self._attributes = list(attributes)
136
137 __len__, __iter__, __getitem__ = _make_sequence_methods("_attributes")
138
139 def __repr__(self) -> str:
140 return f"<Attributes({self._attributes})>"
141
142 def get_attribute_for_oid(self, oid: ObjectIdentifier) -> Attribute:
143 for attr in self:
144 if attr.oid == oid:
145 return attr
146
147 raise AttributeNotFound(f"No {oid} attribute was found", oid)
148
149
150class Version(utils.Enum):
151 v1 = 0
152 v3 = 2
153
154
155class InvalidVersion(Exception):
156 def __init__(self, msg: str, parsed_version: int) -> None:
157 super().__init__(msg)
158 self.parsed_version = parsed_version
159
160
161Certificate = rust_x509.Certificate
162RevokedCertificate = rust_x509.RevokedCertificate
163
164
165CertificateRevocationList = rust_x509.CertificateRevocationList
166CertificateSigningRequest = rust_x509.CertificateSigningRequest
167
168
169load_pem_x509_certificate = rust_x509.load_pem_x509_certificate
170load_der_x509_certificate = rust_x509.load_der_x509_certificate
171
172load_pem_x509_certificates = rust_x509.load_pem_x509_certificates
173
174load_pem_x509_csr = rust_x509.load_pem_x509_csr
175load_der_x509_csr = rust_x509.load_der_x509_csr
176
177load_pem_x509_crl = rust_x509.load_pem_x509_crl
178load_der_x509_crl = rust_x509.load_der_x509_crl
179
180
181class CertificateSigningRequestBuilder:
182 def __init__(
183 self,
184 subject_name: Name | None = None,
185 extensions: list[Extension[ExtensionType]] = [],
186 attributes: list[tuple[ObjectIdentifier, bytes, int | None]] = [],
187 ):
188 """
189 Creates an empty X.509 certificate request (v1).
190 """
191 self._subject_name = subject_name
192 self._extensions = extensions
193 self._attributes = attributes
194
195 def subject_name(self, name: Name) -> CertificateSigningRequestBuilder:
196 """
197 Sets the certificate requestor's distinguished name.
198 """
199 if not isinstance(name, Name):
200 raise TypeError("Expecting x509.Name object.")
201 if self._subject_name is not None:
202 raise ValueError("The subject name may only be set once.")
203 return CertificateSigningRequestBuilder(
204 name, self._extensions, self._attributes
205 )
206
207 def add_extension(
208 self, extval: ExtensionType, critical: bool
209 ) -> CertificateSigningRequestBuilder:
210 """
211 Adds an X.509 extension to the certificate request.
212 """
213 if not isinstance(extval, ExtensionType):
214 raise TypeError("extension must be an ExtensionType")
215
216 extension = Extension(extval.oid, critical, extval)
217 _reject_duplicate_extension(extension, self._extensions)
218
219 return CertificateSigningRequestBuilder(
220 self._subject_name,
221 [*self._extensions, extension],
222 self._attributes,
223 )
224
225 def add_attribute(
226 self,
227 oid: ObjectIdentifier,
228 value: bytes,
229 *,
230 _tag: _ASN1Type | None = None,
231 ) -> CertificateSigningRequestBuilder:
232 """
233 Adds an X.509 attribute with an OID and associated value.
234 """
235 if not isinstance(oid, ObjectIdentifier):
236 raise TypeError("oid must be an ObjectIdentifier")
237
238 if not isinstance(value, bytes):
239 raise TypeError("value must be bytes")
240
241 if _tag is not None and not isinstance(_tag, _ASN1Type):
242 raise TypeError("tag must be _ASN1Type")
243
244 _reject_duplicate_attribute(oid, self._attributes)
245
246 if _tag is not None:
247 tag = _tag.value
248 else:
249 tag = None
250
251 return CertificateSigningRequestBuilder(
252 self._subject_name,
253 self._extensions,
254 [*self._attributes, (oid, value, tag)],
255 )
256
257 def sign(
258 self,
259 private_key: CertificateIssuerPrivateKeyTypes,
260 algorithm: _AllowedHashTypes | None,
261 backend: typing.Any = None,
262 *,
263 rsa_padding: padding.PSS | padding.PKCS1v15 | None = None,
264 ecdsa_deterministic: bool | None = None,
265 ) -> CertificateSigningRequest:
266 """
267 Signs the request using the requestor's private key.
268 """
269 if self._subject_name is None:
270 raise ValueError("A CertificateSigningRequest must have a subject")
271
272 if rsa_padding is not None:
273 if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)):
274 raise TypeError("Padding must be PSS or PKCS1v15")
275 if not isinstance(private_key, rsa.RSAPrivateKey):
276 raise TypeError("Padding is only supported for RSA keys")
277
278 if ecdsa_deterministic is not None:
279 if not isinstance(private_key, ec.EllipticCurvePrivateKey):
280 raise TypeError(
281 "Deterministic ECDSA is only supported for EC keys"
282 )
283
284 return rust_x509.create_x509_csr(
285 self,
286 private_key,
287 algorithm,
288 rsa_padding,
289 ecdsa_deterministic,
290 )
291
292
293class CertificateBuilder:
294 _extensions: list[Extension[ExtensionType]]
295
296 def __init__(
297 self,
298 issuer_name: Name | None = None,
299 subject_name: Name | None = None,
300 public_key: CertificatePublicKeyTypes | None = None,
301 serial_number: int | None = None,
302 not_valid_before: datetime.datetime | None = None,
303 not_valid_after: datetime.datetime | None = None,
304 extensions: list[Extension[ExtensionType]] = [],
305 public_key_rsa_padding: type[padding.PSS] | None = None,
306 ) -> None:
307 self._version = Version.v3
308 self._issuer_name = issuer_name
309 self._subject_name = subject_name
310 self._public_key = public_key
311 self._serial_number = serial_number
312 self._not_valid_before = not_valid_before
313 self._not_valid_after = not_valid_after
314 self._extensions = extensions
315 self._public_key_rsa_padding = public_key_rsa_padding
316
317 def issuer_name(self, name: Name) -> CertificateBuilder:
318 """
319 Sets the CA's distinguished name.
320 """
321 if not isinstance(name, Name):
322 raise TypeError("Expecting x509.Name object.")
323 if self._issuer_name is not None:
324 raise ValueError("The issuer name may only be set once.")
325 return CertificateBuilder(
326 name,
327 self._subject_name,
328 self._public_key,
329 self._serial_number,
330 self._not_valid_before,
331 self._not_valid_after,
332 self._extensions,
333 self._public_key_rsa_padding,
334 )
335
336 def subject_name(self, name: Name) -> CertificateBuilder:
337 """
338 Sets the requestor's distinguished name.
339 """
340 if not isinstance(name, Name):
341 raise TypeError("Expecting x509.Name object.")
342 if self._subject_name is not None:
343 raise ValueError("The subject name may only be set once.")
344 return CertificateBuilder(
345 self._issuer_name,
346 name,
347 self._public_key,
348 self._serial_number,
349 self._not_valid_before,
350 self._not_valid_after,
351 self._extensions,
352 self._public_key_rsa_padding,
353 )
354
355 def public_key(
356 self,
357 key: CertificatePublicKeyTypes,
358 *,
359 rsa_padding: type[padding.PSS] | None = None,
360 ) -> CertificateBuilder:
361 """
362 Sets the requestor's public key (as found in the signing request).
363 """
364 if not isinstance(
365 key,
366 (
367 dsa.DSAPublicKey,
368 rsa.RSAPublicKey,
369 ec.EllipticCurvePublicKey,
370 ed25519.Ed25519PublicKey,
371 ed448.Ed448PublicKey,
372 mldsa.MLDSA44PublicKey,
373 mldsa.MLDSA65PublicKey,
374 mldsa.MLDSA87PublicKey,
375 x25519.X25519PublicKey,
376 x448.X448PublicKey,
377 ),
378 ):
379 raise TypeError(
380 "Expecting one of DSAPublicKey, RSAPublicKey,"
381 " EllipticCurvePublicKey, Ed25519PublicKey,"
382 " Ed448PublicKey, MLDSA44PublicKey, MLDSA65PublicKey,"
383 " MLDSA87PublicKey, X25519PublicKey, or "
384 "X448PublicKey."
385 )
386 if rsa_padding is not None:
387 if rsa_padding is not padding.PSS:
388 raise TypeError(
389 "rsa_padding must be the PSS class, not an instance"
390 )
391 if not isinstance(key, rsa.RSAPublicKey):
392 raise TypeError(
393 "rsa_padding is only supported with RSA public keys"
394 )
395 if self._public_key is not None:
396 raise ValueError("The public key may only be set once.")
397 return CertificateBuilder(
398 self._issuer_name,
399 self._subject_name,
400 key,
401 self._serial_number,
402 self._not_valid_before,
403 self._not_valid_after,
404 self._extensions,
405 rsa_padding,
406 )
407
408 def serial_number(self, number: int) -> CertificateBuilder:
409 """
410 Sets the certificate serial number.
411 """
412 if not isinstance(number, int):
413 raise TypeError("Serial number must be of integral type.")
414 if self._serial_number is not None:
415 raise ValueError("The serial number may only be set once.")
416 if number <= 0:
417 raise ValueError("The serial number should be positive.")
418
419 # ASN.1 integers are always signed, so most significant bit must be
420 # zero.
421 if number.bit_length() >= 160: # As defined in RFC 5280
422 raise ValueError(
423 "The serial number should not be more than 159 bits."
424 )
425 return CertificateBuilder(
426 self._issuer_name,
427 self._subject_name,
428 self._public_key,
429 number,
430 self._not_valid_before,
431 self._not_valid_after,
432 self._extensions,
433 self._public_key_rsa_padding,
434 )
435
436 def not_valid_before(self, time: datetime.datetime) -> CertificateBuilder:
437 """
438 Sets the certificate activation time.
439 """
440 if not isinstance(time, datetime.datetime):
441 raise TypeError("Expecting datetime object.")
442 if self._not_valid_before is not None:
443 raise ValueError("The not valid before may only be set once.")
444 time = _convert_to_naive_utc_time(time)
445 if time < _EARLIEST_UTC_TIME:
446 raise ValueError(
447 "The not valid before date must be on or after"
448 " 1950 January 1)."
449 )
450 if self._not_valid_after is not None and time > self._not_valid_after:
451 raise ValueError(
452 "The not valid before date must be before the not valid after "
453 "date."
454 )
455 return CertificateBuilder(
456 self._issuer_name,
457 self._subject_name,
458 self._public_key,
459 self._serial_number,
460 time,
461 self._not_valid_after,
462 self._extensions,
463 self._public_key_rsa_padding,
464 )
465
466 def not_valid_after(self, time: datetime.datetime) -> CertificateBuilder:
467 """
468 Sets the certificate expiration time.
469 """
470 if not isinstance(time, datetime.datetime):
471 raise TypeError("Expecting datetime object.")
472 if self._not_valid_after is not None:
473 raise ValueError("The not valid after may only be set once.")
474 time = _convert_to_naive_utc_time(time)
475 if time < _EARLIEST_UTC_TIME:
476 raise ValueError(
477 "The not valid after date must be on or after 1950 January 1."
478 )
479 if (
480 self._not_valid_before is not None
481 and time < self._not_valid_before
482 ):
483 raise ValueError(
484 "The not valid after date must be after the not valid before "
485 "date."
486 )
487 return CertificateBuilder(
488 self._issuer_name,
489 self._subject_name,
490 self._public_key,
491 self._serial_number,
492 self._not_valid_before,
493 time,
494 self._extensions,
495 self._public_key_rsa_padding,
496 )
497
498 def add_extension(
499 self, extval: ExtensionType, critical: bool
500 ) -> CertificateBuilder:
501 """
502 Adds an X.509 extension to the certificate.
503 """
504 if not isinstance(extval, ExtensionType):
505 raise TypeError("extension must be an ExtensionType")
506
507 extension = Extension(extval.oid, critical, extval)
508 _reject_duplicate_extension(extension, self._extensions)
509
510 return CertificateBuilder(
511 self._issuer_name,
512 self._subject_name,
513 self._public_key,
514 self._serial_number,
515 self._not_valid_before,
516 self._not_valid_after,
517 [*self._extensions, extension],
518 self._public_key_rsa_padding,
519 )
520
521 def sign(
522 self,
523 private_key: CertificateIssuerPrivateKeyTypes,
524 algorithm: _AllowedHashTypes | None,
525 backend: typing.Any = None,
526 *,
527 rsa_padding: padding.PSS | padding.PKCS1v15 | None = None,
528 ecdsa_deterministic: bool | None = None,
529 ) -> Certificate:
530 """
531 Signs the certificate using the CA's private key.
532 """
533 if self._subject_name is None:
534 raise ValueError("A certificate must have a subject name")
535
536 if self._issuer_name is None:
537 raise ValueError("A certificate must have an issuer name")
538
539 if self._serial_number is None:
540 raise ValueError("A certificate must have a serial number")
541
542 if self._not_valid_before is None:
543 raise ValueError("A certificate must have a not valid before time")
544
545 if self._not_valid_after is None:
546 raise ValueError("A certificate must have a not valid after time")
547
548 if self._public_key is None:
549 raise ValueError("A certificate must have a public key")
550
551 if rsa_padding is not None:
552 if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)):
553 raise TypeError("Padding must be PSS or PKCS1v15")
554 if not isinstance(private_key, rsa.RSAPrivateKey):
555 raise TypeError("Padding is only supported for RSA keys")
556
557 if ecdsa_deterministic is not None:
558 if not isinstance(private_key, ec.EllipticCurvePrivateKey):
559 raise TypeError(
560 "Deterministic ECDSA is only supported for EC keys"
561 )
562
563 return rust_x509.create_x509_certificate(
564 self,
565 private_key,
566 algorithm,
567 rsa_padding,
568 ecdsa_deterministic,
569 )
570
571
572class CertificateRevocationListBuilder:
573 _extensions: list[Extension[ExtensionType]]
574 _revoked_certificates: list[RevokedCertificate]
575
576 def __init__(
577 self,
578 issuer_name: Name | None = None,
579 last_update: datetime.datetime | None = None,
580 next_update: datetime.datetime | None = None,
581 extensions: list[Extension[ExtensionType]] = [],
582 revoked_certificates: list[RevokedCertificate] = [],
583 ):
584 self._issuer_name = issuer_name
585 self._last_update = last_update
586 self._next_update = next_update
587 self._extensions = extensions
588 self._revoked_certificates = revoked_certificates
589
590 def issuer_name(
591 self, issuer_name: Name
592 ) -> CertificateRevocationListBuilder:
593 if not isinstance(issuer_name, Name):
594 raise TypeError("Expecting x509.Name object.")
595 if self._issuer_name is not None:
596 raise ValueError("The issuer name may only be set once.")
597 return CertificateRevocationListBuilder(
598 issuer_name,
599 self._last_update,
600 self._next_update,
601 self._extensions,
602 self._revoked_certificates,
603 )
604
605 def last_update(
606 self, last_update: datetime.datetime
607 ) -> CertificateRevocationListBuilder:
608 if not isinstance(last_update, datetime.datetime):
609 raise TypeError("Expecting datetime object.")
610 if self._last_update is not None:
611 raise ValueError("Last update may only be set once.")
612 last_update = _convert_to_naive_utc_time(last_update)
613 if last_update < _EARLIEST_UTC_TIME:
614 raise ValueError(
615 "The last update date must be on or after 1950 January 1."
616 )
617 if self._next_update is not None and last_update > self._next_update:
618 raise ValueError(
619 "The last update date must be before the next update date."
620 )
621 return CertificateRevocationListBuilder(
622 self._issuer_name,
623 last_update,
624 self._next_update,
625 self._extensions,
626 self._revoked_certificates,
627 )
628
629 def next_update(
630 self, next_update: datetime.datetime
631 ) -> CertificateRevocationListBuilder:
632 if not isinstance(next_update, datetime.datetime):
633 raise TypeError("Expecting datetime object.")
634 if self._next_update is not None:
635 raise ValueError("Last update may only be set once.")
636 next_update = _convert_to_naive_utc_time(next_update)
637 if next_update < _EARLIEST_UTC_TIME:
638 raise ValueError(
639 "The last update date must be on or after 1950 January 1."
640 )
641 if self._last_update is not None and next_update < self._last_update:
642 raise ValueError(
643 "The next update date must be after the last update date."
644 )
645 return CertificateRevocationListBuilder(
646 self._issuer_name,
647 self._last_update,
648 next_update,
649 self._extensions,
650 self._revoked_certificates,
651 )
652
653 def add_extension(
654 self, extval: ExtensionType, critical: bool
655 ) -> CertificateRevocationListBuilder:
656 """
657 Adds an X.509 extension to the certificate revocation list.
658 """
659 if not isinstance(extval, ExtensionType):
660 raise TypeError("extension must be an ExtensionType")
661
662 extension = Extension(extval.oid, critical, extval)
663 _reject_duplicate_extension(extension, self._extensions)
664 return CertificateRevocationListBuilder(
665 self._issuer_name,
666 self._last_update,
667 self._next_update,
668 [*self._extensions, extension],
669 self._revoked_certificates,
670 )
671
672 def add_revoked_certificate(
673 self, revoked_certificate: RevokedCertificate
674 ) -> CertificateRevocationListBuilder:
675 """
676 Adds a revoked certificate to the CRL.
677 """
678 if not isinstance(revoked_certificate, RevokedCertificate):
679 raise TypeError("Must be an instance of RevokedCertificate")
680
681 return CertificateRevocationListBuilder(
682 self._issuer_name,
683 self._last_update,
684 self._next_update,
685 self._extensions,
686 [*self._revoked_certificates, revoked_certificate],
687 )
688
689 def sign(
690 self,
691 private_key: CertificateIssuerPrivateKeyTypes,
692 algorithm: _AllowedHashTypes | None,
693 backend: typing.Any = None,
694 *,
695 rsa_padding: padding.PSS | padding.PKCS1v15 | None = None,
696 ecdsa_deterministic: bool | None = None,
697 ) -> CertificateRevocationList:
698 if self._issuer_name is None:
699 raise ValueError("A CRL must have an issuer name")
700
701 if self._last_update is None:
702 raise ValueError("A CRL must have a last update time")
703
704 if self._next_update is None:
705 raise ValueError("A CRL must have a next update time")
706
707 if rsa_padding is not None:
708 if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)):
709 raise TypeError("Padding must be PSS or PKCS1v15")
710 if not isinstance(private_key, rsa.RSAPrivateKey):
711 raise TypeError("Padding is only supported for RSA keys")
712
713 if ecdsa_deterministic is not None:
714 if not isinstance(private_key, ec.EllipticCurvePrivateKey):
715 raise TypeError(
716 "Deterministic ECDSA is only supported for EC keys"
717 )
718
719 return rust_x509.create_x509_crl(
720 self,
721 private_key,
722 algorithm,
723 rsa_padding,
724 ecdsa_deterministic,
725 )
726
727
728class RevokedCertificateBuilder:
729 def __init__(
730 self,
731 serial_number: int | None = None,
732 revocation_date: datetime.datetime | None = None,
733 extensions: list[Extension[ExtensionType]] = [],
734 ):
735 self._serial_number = serial_number
736 self._revocation_date = revocation_date
737 self._extensions = extensions
738
739 def serial_number(self, number: int) -> RevokedCertificateBuilder:
740 if not isinstance(number, int):
741 raise TypeError("Serial number must be of integral type.")
742 if self._serial_number is not None:
743 raise ValueError("The serial number may only be set once.")
744 if number <= 0:
745 raise ValueError("The serial number should be positive")
746
747 # ASN.1 integers are always signed, so most significant bit must be
748 # zero.
749 if number.bit_length() >= 160: # As defined in RFC 5280
750 raise ValueError(
751 "The serial number should not be more than 159 bits."
752 )
753 return RevokedCertificateBuilder(
754 number, self._revocation_date, self._extensions
755 )
756
757 def revocation_date(
758 self, time: datetime.datetime
759 ) -> RevokedCertificateBuilder:
760 if not isinstance(time, datetime.datetime):
761 raise TypeError("Expecting datetime object.")
762 if self._revocation_date is not None:
763 raise ValueError("The revocation date may only be set once.")
764 time = _convert_to_naive_utc_time(time)
765 if time < _EARLIEST_UTC_TIME:
766 raise ValueError(
767 "The revocation date must be on or after 1950 January 1."
768 )
769 return RevokedCertificateBuilder(
770 self._serial_number, time, self._extensions
771 )
772
773 def add_extension(
774 self, extval: ExtensionType, critical: bool
775 ) -> RevokedCertificateBuilder:
776 if not isinstance(extval, ExtensionType):
777 raise TypeError("extension must be an ExtensionType")
778
779 extension = Extension(extval.oid, critical, extval)
780 _reject_duplicate_extension(extension, self._extensions)
781 return RevokedCertificateBuilder(
782 self._serial_number,
783 self._revocation_date,
784 [*self._extensions, extension],
785 )
786
787 def build(self, backend: typing.Any = None) -> RevokedCertificate:
788 if self._serial_number is None:
789 raise ValueError("A revoked certificate must have a serial number")
790 if self._revocation_date is None:
791 raise ValueError(
792 "A revoked certificate must have a revocation date"
793 )
794 return rust_x509.create_revoked_certificate(self)
795
796
797def random_serial_number() -> int:
798 return int.from_bytes(os.urandom(20), "big") >> 1