aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/_cffi_src/openssl/x509name.py10
-rw-r--r--src/_cffi_src/openssl/x509v3.py5
-rw-r--r--src/cryptography/hazmat/backends/interfaces.py4
-rw-r--r--src/cryptography/hazmat/backends/multibackend.py4
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py92
-rw-r--r--src/cryptography/hazmat/backends/openssl/x509.py6
-rw-r--r--src/cryptography/x509.py75
7 files changed, 176 insertions, 20 deletions
diff --git a/src/_cffi_src/openssl/x509name.py b/src/_cffi_src/openssl/x509name.py
index 2d87db4c..7b833d61 100644
--- a/src/_cffi_src/openssl/x509name.py
+++ b/src/_cffi_src/openssl/x509name.py
@@ -15,10 +15,13 @@ typedef STACK_OF(X509_NAME_ENTRY) Cryptography_STACK_OF_X509_NAME_ENTRY;
"""
TYPES = """
-typedef ... X509_NAME;
+typedef ... Cryptography_STACK_OF_X509_NAME_ENTRY;
+typedef struct {
+ Cryptography_STACK_OF_X509_NAME_ENTRY *entries;
+ ...;
+} X509_NAME;
typedef ... X509_NAME_ENTRY;
typedef ... Cryptography_STACK_OF_X509_NAME;
-typedef ... Cryptography_STACK_OF_X509_NAME_ENTRY;
"""
FUNCTIONS = """
@@ -55,6 +58,9 @@ void sk_X509_NAME_free(Cryptography_STACK_OF_X509_NAME *);
int sk_X509_NAME_ENTRY_num(Cryptography_STACK_OF_X509_NAME_ENTRY *);
X509_NAME_ENTRY *sk_X509_NAME_ENTRY_value(
Cryptography_STACK_OF_X509_NAME_ENTRY *, int);
+Cryptography_STACK_OF_X509_NAME_ENTRY *sk_X509_NAME_ENTRY_dup(
+ Cryptography_STACK_OF_X509_NAME_ENTRY *
+);
"""
CUSTOMIZATIONS = """
diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py
index f6a18903..6e35dacc 100644
--- a/src/_cffi_src/openssl/x509v3.py
+++ b/src/_cffi_src/openssl/x509v3.py
@@ -189,7 +189,9 @@ BASIC_CONSTRAINTS *BASIC_CONSTRAINTS_new(void);
void BASIC_CONSTRAINTS_free(BASIC_CONSTRAINTS *);
/* This is a macro defined by a call to DECLARE_ASN1_FUNCTIONS in the
x509v3.h header. */
+AUTHORITY_KEYID *AUTHORITY_KEYID_new(void);
void AUTHORITY_KEYID_free(AUTHORITY_KEYID *);
+int i2d_AUTHORITY_KEYID(AUTHORITY_KEYID *, unsigned char **);
NAME_CONSTRAINTS *NAME_CONSTRAINTS_new(void);
void NAME_CONSTRAINTS_free(NAME_CONSTRAINTS *);
@@ -283,6 +285,9 @@ X509_EXTENSION *X509V3_EXT_i2d(int, int, void *);
DIST_POINT *DIST_POINT_new(void);
void DIST_POINT_free(DIST_POINT *);
+DIST_POINT_NAME *DIST_POINT_NAME_new(void);
+void DIST_POINT_NAME_free(DIST_POINT_NAME *);
+
int i2d_CRL_DIST_POINTS(Cryptography_STACK_OF_DIST_POINT *, unsigned char **);
"""
diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py
index 49ccda18..a43621a7 100644
--- a/src/cryptography/hazmat/backends/interfaces.py
+++ b/src/cryptography/hazmat/backends/interfaces.py
@@ -281,9 +281,9 @@ class X509Backend(object):
"""
@abc.abstractmethod
- def sign_x509_certificate(self, builder, private_key, algorithm):
+ def create_x509_certificate(self, builder, private_key, algorithm):
"""
- Sign an X.509 Certificate from a CertificateBuilder object.
+ Create and sign an X.509 certificate from a CertificateBuilder object.
"""
diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py
index 8008989e..9db32aa5 100644
--- a/src/cryptography/hazmat/backends/multibackend.py
+++ b/src/cryptography/hazmat/backends/multibackend.py
@@ -352,9 +352,9 @@ class MultiBackend(object):
_Reasons.UNSUPPORTED_X509
)
- def sign_x509_certificate(self, builder, private_key, algorithm):
+ def create_x509_certificate(self, builder, private_key, algorithm):
for b in self._filtered_backends(X509Backend):
- return b.sign_x509_certificate(builder, private_key, algorithm)
+ return b.create_x509_certificate(builder, private_key, algorithm)
raise UnsupportedAlgorithm(
"This backend does not support X.509.",
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index f9da9ea7..2752d98d 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -38,7 +38,8 @@ from cryptography.hazmat.backends.openssl.rsa import (
_RSAPrivateKey, _RSAPublicKey
)
from cryptography.hazmat.backends.openssl.x509 import (
- _Certificate, _CertificateSigningRequest
+ _Certificate, _CertificateSigningRequest, _DISTPOINT_TYPE_FULLNAME,
+ _DISTPOINT_TYPE_RELATIVENAME
)
from cryptography.hazmat.bindings.openssl.binding import Binding
from cryptography.hazmat.primitives import hashes, serialization
@@ -228,18 +229,22 @@ def _encode_authority_information_access(backend, authority_info_access):
return pp, r
-def _encode_subject_alt_name(backend, san):
+def _encode_general_names(backend, names):
general_names = backend._lib.GENERAL_NAMES_new()
assert general_names != backend._ffi.NULL
- general_names = backend._ffi.gc(
- general_names, backend._lib.GENERAL_NAMES_free
- )
-
- for alt_name in san:
- gn = _encode_general_name(backend, alt_name)
+ for name in names:
+ gn = _encode_general_name(backend, name)
res = backend._lib.sk_GENERAL_NAME_push(general_names, gn)
assert res != 0
+ return general_names
+
+
+def _encode_subject_alt_name(backend, san):
+ general_names = _encode_general_names(backend, san)
+ general_names = backend._ffi.gc(
+ general_names, backend._lib.GENERAL_NAMES_free
+ )
pp = backend._ffi.new("unsigned char **")
r = backend._lib.i2d_GENERAL_NAMES(general_names, pp)
assert r > 0
@@ -355,6 +360,67 @@ def _encode_extended_key_usage(backend, extended_key_usage):
return pp, r
+_CRLREASONFLAGS = {
+ x509.ReasonFlags.key_compromise: 1,
+ x509.ReasonFlags.ca_compromise: 2,
+ x509.ReasonFlags.affiliation_changed: 3,
+ x509.ReasonFlags.superseded: 4,
+ x509.ReasonFlags.cessation_of_operation: 5,
+ x509.ReasonFlags.certificate_hold: 6,
+ x509.ReasonFlags.privilege_withdrawn: 7,
+ x509.ReasonFlags.aa_compromise: 8,
+}
+
+
+def _encode_crl_distribution_points(backend, crl_distribution_points):
+ cdp = backend._lib.sk_DIST_POINT_new_null()
+ cdp = backend._ffi.gc(cdp, backend._lib.sk_DIST_POINT_free)
+ for point in crl_distribution_points:
+ dp = backend._lib.DIST_POINT_new()
+ assert dp != backend._ffi.NULL
+
+ if point.reasons:
+ bitmask = backend._lib.ASN1_BIT_STRING_new()
+ assert bitmask != backend._ffi.NULL
+ dp.reasons = bitmask
+ for reason in point.reasons:
+ res = backend._lib.ASN1_BIT_STRING_set_bit(
+ bitmask, _CRLREASONFLAGS[reason], 1
+ )
+ assert res == 1
+
+ if point.full_name:
+ dpn = backend._lib.DIST_POINT_NAME_new()
+ assert dpn != backend._ffi.NULL
+ dpn.type = _DISTPOINT_TYPE_FULLNAME
+ dpn.name.fullname = _encode_general_names(backend, point.full_name)
+ dp.distpoint = dpn
+
+ if point.relative_name:
+ dpn = backend._lib.DIST_POINT_NAME_new()
+ assert dpn != backend._ffi.NULL
+ dpn.type = _DISTPOINT_TYPE_RELATIVENAME
+ name = _encode_name_gc(backend, point.relative_name)
+ relativename = backend._lib.sk_X509_NAME_ENTRY_dup(name.entries)
+ assert relativename != backend._ffi.NULL
+ dpn.name.relativename = relativename
+ dp.distpoint = dpn
+
+ if point.crl_issuer:
+ dp.CRLissuer = _encode_general_names(backend, point.crl_issuer)
+
+ res = backend._lib.sk_DIST_POINT_push(cdp, dp)
+ assert res >= 1
+
+ pp = backend._ffi.new('unsigned char **')
+ r = backend._lib.i2d_CRL_DIST_POINTS(cdp, pp)
+ assert r > 0
+ pp = backend._ffi.gc(
+ pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0])
+ )
+ return pp, r
+
+
@utils.register_interface(CipherBackend)
@utils.register_interface(CMACBackend)
@utils.register_interface(DERSerializationBackend)
@@ -1096,7 +1162,7 @@ class Backend(object):
return _CertificateSigningRequest(self, x509_req)
- def sign_x509_certificate(self, builder, private_key, algorithm):
+ def create_x509_certificate(self, builder, private_key, algorithm):
if not isinstance(builder, x509.CertificateBuilder):
raise TypeError('Builder type mismatch.')
if not isinstance(algorithm, hashes.HashAlgorithm):
@@ -1173,16 +1239,22 @@ class Backend(object):
pp, r = _encode_authority_information_access(
self, extension.value
)
+ elif isinstance(extension.value, x509.CRLDistributionPoints):
+ pp, r = _encode_crl_distribution_points(
+ self, extension.value
+ )
else:
raise NotImplementedError('Extension not yet supported.')
- obj = _txt2obj(self, extension.oid.dotted_string)
+ obj = _txt2obj_gc(self, extension.oid.dotted_string)
extension = self._lib.X509_EXTENSION_create_by_OBJ(
self._ffi.NULL,
obj,
1 if extension.critical else 0,
_encode_asn1_str_gc(self, pp[0], r)
)
+ assert extension != self._ffi.NULL
+ extension = self._ffi.gc(extension, self._lib.X509_EXTENSION_free)
res = self._lib.X509_add_ext(x509_cert, extension, i)
assert res == 1
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index ee9a3bbf..564b2680 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -592,6 +592,10 @@ def _decode_extended_key_usage(backend, sk):
return x509.ExtendedKeyUsage(ekus)
+_DISTPOINT_TYPE_FULLNAME = 0
+_DISTPOINT_TYPE_RELATIVENAME = 1
+
+
def _decode_crl_distribution_points(backend, cdps):
cdps = backend._ffi.cast("Cryptography_STACK_OF_DIST_POINT *", cdps)
cdps = backend._ffi.gc(cdps, backend._lib.sk_DIST_POINT_free)
@@ -651,7 +655,7 @@ def _decode_crl_distribution_points(backend, cdps):
# point so make sure it's not null.
if cdp.distpoint != backend._ffi.NULL:
# Type 0 is fullName, there is no #define for it in the code.
- if cdp.distpoint.type == 0:
+ if cdp.distpoint.type == _DISTPOINT_TYPE_FULLNAME:
full_name = _decode_general_names(
backend, cdp.distpoint.name.fullname
)
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index 82b8bd36..5ed3c094 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -324,7 +324,19 @@ class Extension(object):
return not self == other
+@six.add_metaclass(abc.ABCMeta)
+class ExtensionType(object):
+ @abc.abstractproperty
+ def oid(self):
+ """
+ Returns the oid associated with the given extension type.
+ """
+
+
+@utils.register_interface(ExtensionType)
class ExtendedKeyUsage(object):
+ oid = OID_EXTENDED_KEY_USAGE
+
def __init__(self, usages):
if not all(isinstance(x, ObjectIdentifier) for x in usages):
raise TypeError(
@@ -352,11 +364,15 @@ class ExtendedKeyUsage(object):
return not self == other
+@utils.register_interface(ExtensionType)
class OCSPNoCheck(object):
- pass
+ oid = OID_OCSP_NO_CHECK
+@utils.register_interface(ExtensionType)
class BasicConstraints(object):
+ oid = OID_BASIC_CONSTRAINTS
+
def __init__(self, ca, path_length):
if not isinstance(ca, bool):
raise TypeError("ca must be a boolean value")
@@ -392,7 +408,10 @@ class BasicConstraints(object):
return not self == other
+@utils.register_interface(ExtensionType)
class KeyUsage(object):
+ oid = OID_KEY_USAGE
+
def __init__(self, digital_signature, content_commitment, key_encipherment,
data_encipherment, key_agreement, key_cert_sign, crl_sign,
encipher_only, decipher_only):
@@ -475,7 +494,10 @@ class KeyUsage(object):
return not self == other
+@utils.register_interface(ExtensionType)
class AuthorityInformationAccess(object):
+ oid = OID_AUTHORITY_INFORMATION_ACCESS
+
def __init__(self, descriptions):
if not all(isinstance(x, AccessDescription) for x in descriptions):
raise TypeError(
@@ -539,7 +561,10 @@ class AccessDescription(object):
access_location = utils.read_only_property("_access_location")
+@utils.register_interface(ExtensionType)
class CertificatePolicies(object):
+ oid = OID_CERTIFICATE_POLICIES
+
def __init__(self, policies):
if not all(isinstance(x, PolicyInformation) for x in policies):
raise TypeError(
@@ -676,7 +701,10 @@ class NoticeReference(object):
notice_numbers = utils.read_only_property("_notice_numbers")
+@utils.register_interface(ExtensionType)
class SubjectKeyIdentifier(object):
+ oid = OID_SUBJECT_KEY_IDENTIFIER
+
def __init__(self, digest):
self._digest = digest
@@ -718,7 +746,10 @@ class SubjectKeyIdentifier(object):
return not self == other
+@utils.register_interface(ExtensionType)
class NameConstraints(object):
+ oid = OID_NAME_CONSTRAINTS
+
def __init__(self, permitted_subtrees, excluded_subtrees):
if permitted_subtrees is not None:
if not all(
@@ -782,7 +813,10 @@ class NameConstraints(object):
excluded_subtrees = utils.read_only_property("_excluded_subtrees")
+@utils.register_interface(ExtensionType)
class CRLDistributionPoints(object):
+ oid = OID_CRL_DISTRIBUTION_POINTS
+
def __init__(self, distribution_points):
if not all(
isinstance(x, DistributionPoint) for x in distribution_points
@@ -817,7 +851,8 @@ class DistributionPoint(object):
def __init__(self, full_name, relative_name, reasons, crl_issuer):
if full_name and relative_name:
raise ValueError(
- "At least one of full_name and relative_name must be None"
+ "You cannot provide both full_name and relative_name, at "
+ "least one must be None."
)
if full_name and not all(
@@ -902,7 +937,10 @@ class ReasonFlags(Enum):
remove_from_crl = "removeFromCRL"
+@utils.register_interface(ExtensionType)
class InhibitAnyPolicy(object):
+ oid = OID_INHIBIT_ANY_POLICY
+
def __init__(self, skip_certs):
if not isinstance(skip_certs, six.integer_types):
raise TypeError("skip_certs must be an integer")
@@ -1192,7 +1230,10 @@ class GeneralNames(object):
return not self == other
+@utils.register_interface(ExtensionType)
class SubjectAlternativeName(object):
+ oid = OID_SUBJECT_ALTERNATIVE_NAME
+
def __init__(self, general_names):
self._general_names = GeneralNames(general_names)
@@ -1218,7 +1259,10 @@ class SubjectAlternativeName(object):
return not self == other
+@utils.register_interface(ExtensionType)
class IssuerAlternativeName(object):
+ oid = OID_ISSUER_ALTERNATIVE_NAME
+
def __init__(self, general_names):
self._general_names = GeneralNames(general_names)
@@ -1244,7 +1288,10 @@ class IssuerAlternativeName(object):
return not self == other
+@utils.register_interface(ExtensionType)
class AuthorityKeyIdentifier(object):
+ oid = OID_AUTHORITY_KEY_IDENTIFIER
+
def __init__(self, key_identifier, authority_cert_issuer,
authority_cert_serial_number):
if authority_cert_issuer or authority_cert_serial_number:
@@ -1766,6 +1813,10 @@ class CertificateBuilder(object):
)
elif isinstance(extension, InhibitAnyPolicy):
extension = Extension(OID_INHIBIT_ANY_POLICY, critical, extension)
+ elif isinstance(extension, CRLDistributionPoints):
+ extension = Extension(
+ OID_CRL_DISTRIBUTION_POINTS, critical, extension
+ )
else:
raise NotImplementedError('Unsupported X.509 extension.')
@@ -1784,4 +1835,22 @@ class CertificateBuilder(object):
"""
Signs the certificate using the CA's private key.
"""
- return backend.sign_x509_certificate(self, private_key, algorithm)
+ if self._subject_name is None:
+ raise ValueError("A certificate must have a subject name")
+
+ if self._issuer_name is None:
+ raise ValueError("A certificate must have an issuer name")
+
+ if self._serial_number is None:
+ raise ValueError("A certificate must have a serial number")
+
+ if self._not_valid_before is None:
+ raise ValueError("A certificate must have a not valid before time")
+
+ if self._not_valid_after is None:
+ raise ValueError("A certificate must have a not valid after time")
+
+ if self._public_key is None:
+ raise ValueError("A certificate must have a public key")
+
+ return backend.create_x509_certificate(self, private_key, algorithm)