From 5d187402775bcb7bc8b0da1d972d36bf9ad9dbff Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Mon, 16 Jul 2018 20:49:51 +0530 Subject: add crl.get_revoked_certificate method (#4331) * add crl.get_revoked_certificate method * lexicographic is the best ographic * rename --- CHANGELOG.rst | 3 +++ docs/x509/reference.rst | 9 +++++++++ src/_cffi_src/openssl/x509.py | 2 ++ src/cryptography/hazmat/backends/openssl/x509.py | 19 +++++++++++++++++++ src/cryptography/x509/base.py | 7 +++++++ tests/x509/test_x509.py | 12 ++++++++++++ 6 files changed, 52 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8b4e9743..c8db7e7d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,6 +16,9 @@ Changelog ``cryptography`` release. * Fixed multiple issues preventing ``cryptography`` from compiling against LibreSSL 2.7.x. +* Added + :class:`~cryptography.x509.CertificateRevocationList.get_revoked_certificate_by_serial_number` + for quick serial number searches in CRLs. * The :class:`~cryptography.x509.RelativeDistinguishedName` class now preserves the order of attributes. Duplicate attributes now raise an error instead of silently discarding duplicates. diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst index 64097bf2..bc3dd556 100644 --- a/docs/x509/reference.rst +++ b/docs/x509/reference.rst @@ -463,6 +463,15 @@ X.509 CRL (Certificate Revocation List) Object >>> crl.fingerprint(hashes.SHA256()) b'e\xcf.\xc4:\x83?1\xdc\xf3\xfc\x95\xd7\xb3\x87\xb3\x8e\xf8\xb93!\x87\x07\x9d\x1b\xb4!\xb9\xe4W\xf4\x1f' + .. method:: get_revoked_certificate_by_serial_number(serial_number) + + .. versionadded:: 2.3 + + :param serial_number: The serial as a Python integer. + :returns: :class:`~cryptography.x509.RevokedCertificate` if the + ``serial_number`` is present in the CRL or ``None`` if it + is not. + .. attribute:: signature_hash_algorithm :type: :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` diff --git a/src/_cffi_src/openssl/x509.py b/src/_cffi_src/openssl/x509.py index 97ade5bc..59fdbf7e 100644 --- a/src/_cffi_src/openssl/x509.py +++ b/src/_cffi_src/openssl/x509.py @@ -238,6 +238,8 @@ X509_EXTENSION *X509_REVOKED_get_ext(X509_REVOKED *, int); X509_EXTENSION *X509_CRL_get_ext(X509_CRL *, int); int X509_CRL_get_ext_count(X509_CRL *); +int X509_CRL_get0_by_serial(X509_CRL *, X509_REVOKED **, ASN1_INTEGER *); + /* these CRYPTO_EX_DATA functions became macros in 1.1.0 */ int X509_get_ex_new_index(long, void *, CRYPTO_EX_new *, CRYPTO_EX_dup *, CRYPTO_EX_free *); diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 9637fc0e..b870eeb7 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -16,6 +16,9 @@ from cryptography.hazmat.backends.openssl.decode_asn1 import ( _REVOKED_CERTIFICATE_EXTENSION_PARSER, _asn1_integer_to_int, _asn1_string_to_bytes, _decode_x509_name, _obj2txt, _parse_asn1_time ) +from cryptography.hazmat.backends.openssl.encode_asn1 import ( + _encode_asn1_int_gc +) from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa @@ -235,6 +238,22 @@ class _CertificateRevocationList(object): h.update(der) return h.finalize() + def get_revoked_certificate_by_serial_number(self, serial_number): + revoked = self._backend._ffi.new("X509_REVOKED **") + asn1_int = _encode_asn1_int_gc(self._backend, serial_number) + res = self._backend._lib.X509_CRL_get0_by_serial( + self._x509_crl, revoked, asn1_int + ) + if res == 0: + return None + else: + self._backend.openssl_assert( + revoked[0] != self._backend._ffi.NULL + ) + return _RevokedCertificate( + self._backend, self._x509_crl, revoked[0] + ) + @property def signature_hash_algorithm(self): oid = self.signature_algorithm_oid diff --git a/src/cryptography/x509/base.py b/src/cryptography/x509/base.py index 45b603f0..b14499c9 100644 --- a/src/cryptography/x509/base.py +++ b/src/cryptography/x509/base.py @@ -189,6 +189,13 @@ class CertificateRevocationList(object): Returns bytes using digest passed. """ + @abc.abstractmethod + def get_revoked_certificate_by_serial_number(self, serial_number): + """ + Returns an instance of RevokedCertificate or None if the serial_number + is not in the CRL. + """ + @abc.abstractproperty def signature_hash_algorithm(self): """ diff --git a/tests/x509/test_x509.py b/tests/x509/test_x509.py index fe57784a..5905e335 100644 --- a/tests/x509/test_x509.py +++ b/tests/x509/test_x509.py @@ -181,6 +181,18 @@ class TestCertificateRevocationList(object): # Check that len() works for CRLs. assert len(crl) == 12 + def test_get_revoked_certificate_by_serial_number(self, backend): + crl = _load_cert( + os.path.join( + "x509", "PKITS_data", "crls", "LongSerialNumberCACRL.crl"), + x509.load_der_x509_crl, + backend + ) + serial_number = 725064303890588110203033396814564464046290047507 + revoked = crl.get_revoked_certificate_by_serial_number(serial_number) + assert revoked.serial_number == serial_number + assert crl.get_revoked_certificate_by_serial_number(500) is None + def test_revoked_cert_retrieval_retain_only_revoked(self, backend): """ This test attempts to trigger the crash condition described in -- cgit v1.2.3