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