Skip to content

Commit

Permalink
OpenSSL backend code for CRLs
Browse files Browse the repository at this point in the history
  • Loading branch information
etrauschke committed Sep 3, 2015
1 parent cad58d7 commit 76902e8
Show file tree
Hide file tree
Showing 8 changed files with 734 additions and 63 deletions.
111 changes: 111 additions & 0 deletions docs/x509/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@ X.509 Reference

.. testsetup::

pem_crl_data = b"""
-----BEGIN X509 CRL-----
MIIBtDCBnQIBAjANBgkqhkiG9w0BAQsFADAnMQswCQYDVQQGEwJVUzEYMBYGA1UE
AwwPY3J5cHRvZ3JhcGh5LmlvGA8yMDE1MDEwMTAwMDAwMFoYDzIwMTYwMTAxMDAw
MDAwWjA+MDwCAQAYDzIwMTUwMTAxMDAwMDAwWjAmMBgGA1UdGAQRGA8yMDE1MDEw
MTAwMDAwMFowCgYDVR0VBAMKAQEwDQYJKoZIhvcNAQELBQADggEBABRA4ww50Lz5
zk1j2+aluC4HPHqb7o06h4pTDcCGeXUKXIGeP5ntGGmIoxa26sNoLeOr8+5b43Gf
yWraHertllOwaOpNFEe+YZFaE9femtoDbf+GLMvRx/0wDfd3KxPoXnXKMXb2d1w4
RCLgmkYx6JyvS+5ciuLQVIKC+l7jwIUeZFLJMUJ8msM4pFYoGameeZmtjMbd/TNg
cVBfmZxNMHuLladJxvSo2esARo0TYPhYsgrREKoHwhpzSxdynjn4bOVkILfguwsN
qtEEMZFEv5Kb0GqRp2+Iagv2S6dg9JGvxVdsoGjaB6EbYSZ3Psx4aODasIn11uwo
X4B9vUQNXqc=
-----END X509 CRL-----
""".strip()

pem_req_data = b"""
-----BEGIN CERTIFICATE REQUEST-----
MIIC0zCCAbsCAQAwWTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCElsbGlub2lzMRAw
Expand Down Expand Up @@ -129,6 +144,52 @@ Loading Certificates
>>> cert.serial
2

Loading Certificate Revocation Lists
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. function:: load_pem_x509_crl(data, backend)

.. versionadded:: 1.0

Deserialize a certificate revocation list (CRL) from PEM encoded data. PEM
requests are base64 decoded and have delimiters that look like
``-----BEGIN X509 CRL-----``. This format is also known as
PKCS#10.

:param bytes data: The PEM encoded request data.

:param backend: A backend supporting the
:class:`~cryptography.hazmat.backends.interfaces.X509Backend`
interface.

:returns: An instance of
:class:`~cryptography.x509.CertificateRevocationList`.

.. function:: load_der_x509_crl(data, backend)

.. versionadded:: 1.0

Deserialize a certificate revocation list (CRL) from DER encoded data. DER
is a binary format.

:param bytes data: The DER encoded request data.

:param backend: A backend supporting the
:class:`~cryptography.hazmat.backends.interfaces.X509Backend`
interface.

:returns: An instance of
:class:`~cryptography.x509.CertificateRevocationList`.

.. doctest::

>>> from cryptography import x509
>>> from cryptography.hazmat.backends import default_backend
>>> from cryptography.hazmat.primitives import hashes
>>> crl = x509.load_pem_x509_crl(pem_crl_data, default_backend())
>>> isinstance(crl.signature_hash_algorithm, hashes.SHA256)
True

Loading Certificate Signing Requests
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -349,6 +410,12 @@ X.509 CRL (Certificate Revocation List) Object
:return bytes: The fingerprint using the supplied hash algorithm, as
bytes.

.. doctest::

>>> from cryptography.hazmat.primitives import hashes
>>> crl.fingerprint(hashes.SHA256())
'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'

.. attribute:: signature_hash_algorithm

:type: :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm`
Expand All @@ -357,31 +424,58 @@ X.509 CRL (Certificate Revocation List) Object
:class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` which
was used in signing this CRL.

.. doctest::

>>> from cryptography.hazmat.primitives import hashes
>>> isinstance(crl.signature_hash_algorithm, hashes.SHA256)
True

.. attribute:: issuer

:type: :class:`Name`

The :class:`Name` of the issuer.

.. doctest::

>>> crl.issuer
<Name([<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.6, name=countryName)>, value=u'US')>, <NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value=u'cryptography.io')>])>

.. attribute:: next_update

:type: :class:`datetime.datetime`

A naïve datetime representing when the next update to this CRL is
expected.

.. doctest::

>>> crl.next_update
datetime.datetime(2016, 1, 1, 0, 0)

.. attribute:: last_update

:type: :class:`datetime.datetime`

A naïve datetime representing when the this CRL was last updated.

.. doctest::

>>> crl.last_update
datetime.datetime(2015, 1, 1, 0, 0)

.. attribute:: revoked_certificates

:type: list of :class:`RevokedCertificate`

The revoked certificates listed in this CRL.

.. doctest::

>>> for r in crl.revoked_certificates:
... print(r.serial_number)
0

.. attribute:: extensions

:type: :class:`Extensions`
Expand Down Expand Up @@ -610,18 +704,35 @@ X.509 Revoked Certificate Object

An integer representing the serial number of the revoked certificate.

.. doctest::

>>> crl.revoked_certificates[0].serial_number
0

.. attribute:: revocation_date

:type: :class:`datetime.datetime`

A naïve datetime representing the date this certificates was revoked.

.. doctest::

>>> crl.revoked_certificates[0].revocation_date
datetime.datetime(2015, 1, 1, 0, 0)

.. attribute:: extensions

:type: :class:`Extensions`

The extensions encoded in the revoked certificate.

.. doctest::

>>> for ext in crl.revoked_certificates[0].extensions:
... print(ext)
<Extension(oid=<ObjectIdentifier(oid=2.5.29.24, name=invalidityDate)>, critical=False, value=2015-01-01 00:00:00)>
<Extension(oid=<ObjectIdentifier(oid=2.5.29.21, name=cRLReason)>, critical=False, value=ReasonFlags.key_compromise)>

X.509 CSR (Certificate Signing Request) Builder Object
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
18 changes: 18 additions & 0 deletions src/cryptography/hazmat/backends/multibackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,24 @@ def load_der_x509_certificate(self, data):
_Reasons.UNSUPPORTED_X509
)

def load_pem_x509_crl(self, data):
for b in self._filtered_backends(X509Backend):
return b.load_pem_x509_crl(data)

raise UnsupportedAlgorithm(
"This backend does not support X.509.",
_Reasons.UNSUPPORTED_X509
)

def load_der_x509_crl(self, data):
for b in self._filtered_backends(X509Backend):
return b.load_der_x509_crl(data)

raise UnsupportedAlgorithm(
"This backend does not support X.509.",
_Reasons.UNSUPPORTED_X509
)

def load_der_x509_csr(self, data):
for b in self._filtered_backends(X509Backend):
return b.load_der_x509_csr(data)
Expand Down
63 changes: 61 additions & 2 deletions src/cryptography/hazmat/backends/openssl/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import calendar
import collections
import datetime
import itertools
from contextlib import contextmanager

Expand Down Expand Up @@ -38,8 +39,8 @@
_RSAPrivateKey, _RSAPublicKey
)
from cryptography.hazmat.backends.openssl.x509 import (
_Certificate, _CertificateSigningRequest, _DISTPOINT_TYPE_FULLNAME,
_DISTPOINT_TYPE_RELATIVENAME
_Certificate, _CertificateRevocationList, _CertificateSigningRequest,
_DISTPOINT_TYPE_FULLNAME, _DISTPOINT_TYPE_RELATIVENAME
)
from cryptography.hazmat.bindings.openssl.binding import Binding
from cryptography.hazmat.primitives import hashes, serialization
Expand Down Expand Up @@ -1473,6 +1474,28 @@ def load_der_x509_certificate(self, data):
x509 = self._ffi.gc(x509, self._lib.X509_free)
return _Certificate(self, x509)

def load_pem_x509_crl(self, data):
mem_bio = self._bytes_to_bio(data)
x509_crl = self._lib.PEM_read_bio_X509_CRL(
mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL
)
if x509_crl == self._ffi.NULL:
self._consume_errors()
raise ValueError("Unable to load CRL")

x509_crl = self._ffi.gc(x509_crl, self._lib.X509_CRL_free)
return _CertificateRevocationList(self, x509_crl)

def load_der_x509_crl(self, data):
mem_bio = self._bytes_to_bio(data)
x509_crl = self._lib.d2i_X509_CRL_bio(mem_bio.bio, self._ffi.NULL)
if x509_crl == self._ffi.NULL:
self._consume_errors()
raise ValueError("Unable to load CRL")

x509_crl = self._ffi.gc(x509_crl, self._lib.X509_CRL_free)
return _CertificateRevocationList(self, x509_crl)

def load_pem_x509_csr(self, data):
mem_bio = self._bytes_to_bio(data)
x509_req = self._lib.PEM_read_bio_X509_REQ(
Expand Down Expand Up @@ -1941,6 +1964,42 @@ def _public_key_bytes(self, encoding, format, evp_pkey, cdata):
assert res == 1
return self._read_mem_bio(bio)

def _asn1_integer_to_int(self, asn1_int):
bn = self._lib.ASN1_INTEGER_to_BN(asn1_int, self._ffi.NULL)
assert bn != self._ffi.NULL
bn = self._ffi.gc(bn, self._lib.BN_free)
return self._bn_to_int(bn)

def _asn1_string_to_bytes(self, asn1_string):
return self._ffi.buffer(asn1_string.data, asn1_string.length)[:]

def _asn1_string_to_ascii(self, asn1_string):
return self._asn1_string_to_bytes(asn1_string).decode("ascii")

def _asn1_string_to_utf8(self, asn1_string):
buf = self._ffi.new("unsigned char **")
res = self._lib.ASN1_STRING_to_UTF8(buf, asn1_string)
assert res >= 0
assert buf[0] != self._ffi.NULL
buf = self._ffi.gc(
buf, lambda buffer: self._lib.OPENSSL_free(buffer[0])
)
return self._ffi.buffer(buf[0], res)[:].decode('utf8')

def _parse_asn1_time(self, asn1_time):
assert asn1_time != self._ffi.NULL
generalized_time = self._lib.ASN1_TIME_to_generalizedtime(
asn1_time, self._ffi.NULL
)
assert generalized_time != self._ffi.NULL
generalized_time = self._ffi.gc(
generalized_time, self._lib.ASN1_GENERALIZEDTIME_free
)
time = self._asn1_string_to_ascii(
self._ffi.cast("ASN1_STRING *", generalized_time)
)
return datetime.datetime.strptime(time, "%Y%m%d%H%M%SZ")


class GetCipherByName(object):
def __init__(self, fmt):
Expand Down

0 comments on commit 76902e8

Please sign in to comment.