aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Kehrer <paul.l.kehrer@gmail.com>2015-12-19 23:32:08 -0600
committerPaul Kehrer <paul.l.kehrer@gmail.com>2015-12-24 18:49:11 -0600
commitbfac2d10305cf72d634e0e74a87fd08d4cd07257 (patch)
treea93206af48941b2539019d8bb6e290f17956ad97 /src
parent48f17cb225abcf43f77915d152f6cc15b762c702 (diff)
downloadcryptography-bfac2d10305cf72d634e0e74a87fd08d4cd07257.tar.gz
cryptography-bfac2d10305cf72d634e0e74a87fd08d4cd07257.tar.bz2
cryptography-bfac2d10305cf72d634e0e74a87fd08d4cd07257.zip
CertificateRevocationListBuilder
RSA keys only. Currently does not support CRL extensions or CRLEntry extensions.
Diffstat (limited to 'src')
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py68
-rw-r--r--src/cryptography/x509/__init__.py2
-rw-r--r--src/cryptography/x509/base.py66
3 files changed, 135 insertions, 1 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index c3eccb06..6d19b806 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -1456,7 +1456,73 @@ class Backend(object):
return _Certificate(self, x509_cert)
def create_x509_crl(self, builder, private_key, algorithm):
- raise NotImplementedError
+ if not isinstance(builder, x509.CertificateRevocationListBuilder):
+ raise TypeError('Builder type mismatch.')
+ if not isinstance(algorithm, hashes.HashAlgorithm):
+ raise TypeError('Algorithm must be a registered hash algorithm.')
+
+ if isinstance(private_key, _DSAPrivateKey):
+ raise NotImplementedError(
+ "CRL signatures aren't implemented for DSA"
+ " keys at this time."
+ )
+ if isinstance(private_key, _EllipticCurvePrivateKey):
+ raise NotImplementedError(
+ "CRL signatures aren't implemented for EC"
+ " keys at this time."
+ )
+
+ evp_md = self._lib.EVP_get_digestbyname(
+ algorithm.name.encode('ascii')
+ )
+ self.openssl_assert(evp_md != self._ffi.NULL)
+
+ # Create an empty CRL.
+ x509_crl = self._lib.X509_CRL_new()
+ x509_crl = self._ffi.gc(x509_crl, backend._lib.X509_CRL_free)
+
+ # Set the x509 CRL version. We only support v2 (integer value 1).
+ res = self._lib.X509_CRL_set_version(x509_crl, 1)
+ self.openssl_assert(res == 1)
+
+ # Set the issuer name.
+ res = self._lib.X509_CRL_set_issuer_name(
+ x509_crl, _encode_name_gc(self, list(builder._issuer_name))
+ )
+ self.openssl_assert(res == 1)
+
+ # Set the last update time.
+ last_update = self._lib.ASN1_TIME_set(
+ self._ffi.NULL, calendar.timegm(builder._last_update.timetuple())
+ )
+ self.openssl_assert(last_update != self._ffi.NULL)
+ last_update = self._ffi.gc(last_update, self._lib.ASN1_TIME_free)
+ res = self._lib.X509_CRL_set_lastUpdate(x509_crl, last_update)
+ self.openssl_assert(res == 1)
+
+ # Set the next update time.
+ next_update = self._lib.ASN1_TIME_set(
+ self._ffi.NULL, calendar.timegm(builder._next_update.timetuple())
+ )
+ self.openssl_assert(next_update != self._ffi.NULL)
+ next_update = self._ffi.gc(next_update, self._lib.ASN1_TIME_free)
+ res = self._lib.X509_CRL_set_nextUpdate(x509_crl, next_update)
+ self.openssl_assert(res == 1)
+ # TODO: support revoked certificates
+
+ # TODO: add support for CRL extensions
+ res = self._lib.X509_CRL_sign(
+ x509_crl, private_key._evp_pkey, evp_md
+ )
+ if res == 0:
+ errors = self._consume_errors()
+ self.openssl_assert(errors[0][1] == self._lib.ERR_LIB_RSA)
+ self.openssl_assert(
+ errors[0][3] == self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY
+ )
+ raise ValueError("Digest too big for RSA key")
+
+ return _CertificateRevocationList(self, x509_crl)
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 c4434fd1..4978b199 100644
--- a/src/cryptography/x509/__init__.py
+++ b/src/cryptography/x509/__init__.py
@@ -6,6 +6,7 @@ from __future__ import absolute_import, division, print_function
from cryptography.x509.base import (
Certificate, CertificateBuilder, CertificateRevocationList,
+ CertificateRevocationListBuilder,
CertificateSigningRequest, CertificateSigningRequestBuilder,
InvalidVersion, RevokedCertificate,
Version, load_der_x509_certificate, load_der_x509_crl, load_der_x509_csr,
@@ -152,6 +153,7 @@ __all__ = [
"OtherName",
"Certificate",
"CertificateRevocationList",
+ "CertificateRevocationListBuilder",
"CertificateSigningRequest",
"RevokedCertificate",
"CertificateSigningRequestBuilder",
diff --git a/src/cryptography/x509/base.py b/src/cryptography/x509/base.py
index 057d0e9b..6bca2c52 100644
--- a/src/cryptography/x509/base.py
+++ b/src/cryptography/x509/base.py
@@ -518,3 +518,69 @@ class CertificateBuilder(object):
raise ValueError("A certificate must have a public key")
return backend.create_x509_certificate(self, private_key, algorithm)
+
+
+class CertificateRevocationListBuilder(object):
+ def __init__(self, issuer_name=None, last_update=None, next_update=None,
+ extensions=[], revoked_certificates=[]):
+ self._issuer_name = issuer_name
+ self._last_update = last_update
+ self._next_update = next_update
+ self._extensions = extensions
+ self._revoked_certificates = revoked_certificates
+
+ def issuer_name(self, issuer_name):
+ if not isinstance(issuer_name, Name):
+ raise TypeError('Expecting x509.Name object.')
+ if self._issuer_name is not None:
+ raise ValueError('The issuer name may only be set once.')
+ return CertificateRevocationListBuilder(
+ issuer_name, self._last_update, self._next_update,
+ self._extensions, self._revoked_certificates
+ )
+
+ def last_update(self, last_update):
+ if not isinstance(last_update, datetime.datetime):
+ raise TypeError('Expecting datetime object.')
+ if self._last_update is not None:
+ raise ValueError('Last update may only be set once.')
+ if last_update <= _UNIX_EPOCH:
+ raise ValueError('The last update date must be after the unix'
+ ' epoch (1970 January 1).')
+ if self._next_update is not None and last_update > self._next_update:
+ raise ValueError(
+ 'The last update date must be before the next update date.'
+ )
+ return CertificateRevocationListBuilder(
+ self._issuer_name, last_update, self._next_update,
+ self._extensions, self._revoked_certificates
+ )
+
+ def next_update(self, next_update):
+ if not isinstance(next_update, datetime.datetime):
+ raise TypeError('Expecting datetime object.')
+ if self._next_update is not None:
+ raise ValueError('Last update may only be set once.')
+ if next_update <= _UNIX_EPOCH:
+ raise ValueError('The last update date must be after the unix'
+ ' epoch (1970 January 1).')
+ if self._last_update is not None and next_update < self._last_update:
+ raise ValueError(
+ 'The next update date must be after the last update date.'
+ )
+ return CertificateRevocationListBuilder(
+ self._issuer_name, self._last_update, next_update,
+ self._extensions, self._revoked_certificates
+ )
+
+ def sign(self, private_key, algorithm, backend):
+ if self._issuer_name is None:
+ raise ValueError("A CRL must have an issuer name")
+
+ if self._last_update is None:
+ raise ValueError("A CRL must have a last update time")
+
+ if self._next_update is None:
+ raise ValueError("A CRL must have a next update time")
+
+ return backend.create_x509_crl(self, private_key, algorithm)