From c33ffd7527a4ce77010425fedfbeed27856c8aa8 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Fri, 25 Dec 2015 10:59:22 -0600 Subject: RevokedCertificateBuilder --- .../hazmat/backends/openssl/backend.py | 21 ++++++++++- src/cryptography/x509/__init__.py | 3 +- src/cryptography/x509/base.py | 44 ++++++++++++++++++++++ 3 files changed, 65 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index a60bf82b..81316da5 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -38,7 +38,7 @@ from cryptography.hazmat.backends.openssl.rsa import ( ) from cryptography.hazmat.backends.openssl.x509 import ( _Certificate, _CertificateRevocationList, _CertificateSigningRequest, - _DISTPOINT_TYPE_FULLNAME, _DISTPOINT_TYPE_RELATIVENAME + _DISTPOINT_TYPE_FULLNAME, _DISTPOINT_TYPE_RELATIVENAME, _RevokedCertificate ) from cryptography.hazmat.bindings._openssl import ffi as _ffi from cryptography.hazmat.bindings.openssl import binding @@ -1559,7 +1559,24 @@ class Backend(object): self.openssl_assert(res >= 1) def create_x509_revoked_certificate(self, builder): - raise NotImplementedError("Not yet implemented") + if not isinstance(builder, x509.RevokedCertificateBuilder): + raise TypeError('Builder type mismatch.') + + x509_revoked = self._lib.X509_REVOKED_new() + self.openssl_assert(x509_revoked != self._ffi.NULL) + x509_revoked = self._ffi.gc(x509_revoked, self._lib.X509_REVOKED_free) + serial_number = _encode_asn1_int_gc(self, builder._serial_number) + res = self._lib.X509_REVOKED_set_serialNumber( + x509_revoked, serial_number + ) + self.openssl_assert(res == 1) + res = self._lib.ASN1_TIME_set( + x509_revoked.revocationDate, + calendar.timegm(builder._revocation_date.timetuple()) + ) + self.openssl_assert(res != self._ffi.NULL) + # TODO: add crl entry extensions + return _RevokedCertificate(self, None, x509_revoked) def load_pem_private_key(self, data, password): return self._load_key( diff --git a/src/cryptography/x509/__init__.py b/src/cryptography/x509/__init__.py index 4978b199..5653144c 100644 --- a/src/cryptography/x509/__init__.py +++ b/src/cryptography/x509/__init__.py @@ -8,7 +8,7 @@ from cryptography.x509.base import ( Certificate, CertificateBuilder, CertificateRevocationList, CertificateRevocationListBuilder, CertificateSigningRequest, CertificateSigningRequestBuilder, - InvalidVersion, RevokedCertificate, + InvalidVersion, RevokedCertificate, RevokedCertificateBuilder, Version, load_der_x509_certificate, load_der_x509_crl, load_der_x509_csr, load_pem_x509_certificate, load_pem_x509_crl, load_pem_x509_csr, ) @@ -156,6 +156,7 @@ __all__ = [ "CertificateRevocationListBuilder", "CertificateSigningRequest", "RevokedCertificate", + "RevokedCertificateBuilder", "CertificateSigningRequestBuilder", "CertificateBuilder", "Version", diff --git a/src/cryptography/x509/base.py b/src/cryptography/x509/base.py index 49cbcf75..e29a3105 100644 --- a/src/cryptography/x509/base.py +++ b/src/cryptography/x509/base.py @@ -602,3 +602,47 @@ class CertificateRevocationListBuilder(object): raise ValueError("A CRL must have a next update time") return backend.create_x509_crl(self, private_key, algorithm) + + +class RevokedCertificateBuilder(object): + def __init__(self, serial_number=None, revocation_date=None, + extensions=[]): + self._serial_number = serial_number + self._revocation_date = revocation_date + self._extensions = extensions + + def serial_number(self, number): + if not isinstance(number, six.integer_types): + raise TypeError('Serial number must be of integral type.') + if self._serial_number is not None: + raise ValueError('The serial number may only be set once.') + if number < 0: + raise ValueError('The serial number should be non-negative.') + if utils.bit_length(number) > 160: # As defined in RFC 5280 + raise ValueError('The serial number should not be more than 160 ' + 'bits.') + return RevokedCertificateBuilder( + number, self._revocation_date, self._extensions + ) + + def revocation_date(self, time): + if not isinstance(time, datetime.datetime): + raise TypeError('Expecting datetime object.') + if self._revocation_date is not None: + raise ValueError('The revocation date may only be set once.') + if time <= _UNIX_EPOCH: + raise ValueError('The revocation date must be after the unix' + ' epoch (1970 January 1).') + return RevokedCertificateBuilder( + self._serial_number, time, self._extensions + ) + + def build(self, backend): + if self._serial_number is None: + raise ValueError("A revoked certificate must have a serial number") + if self._revocation_date is None: + raise ValueError( + "A revoked certificate must have a revocation date" + ) + + return backend.create_x509_revoked_certificate(self) -- cgit v1.2.3