aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/hazmat/backends/interfaces.rst20
-rw-r--r--docs/x509/reference.rst179
-rw-r--r--src/_cffi_src/openssl/asn1.py5
-rw-r--r--src/_cffi_src/openssl/ec.py2
-rw-r--r--src/_cffi_src/openssl/x509v3.py1
-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.py119
-rw-r--r--src/cryptography/hazmat/backends/openssl/x509.py6
-rw-r--r--src/cryptography/utils.py7
-rw-r--r--src/cryptography/x509.py112
-rw-r--r--tests/hazmat/backends/test_multibackend.py6
-rw-r--r--tests/hazmat/backends/test_openssl.py2
-rw-r--r--tests/test_x509.py260
-rw-r--r--tests/test_x509_ext.py82
15 files changed, 785 insertions, 24 deletions
diff --git a/docs/hazmat/backends/interfaces.rst b/docs/hazmat/backends/interfaces.rst
index fb3786c3..442bd0de 100644
--- a/docs/hazmat/backends/interfaces.rst
+++ b/docs/hazmat/backends/interfaces.rst
@@ -550,6 +550,26 @@ A specific ``backend`` may provide one or more of these interfaces.
:returns: A new object with the
:class:`~cryptography.x509.CertificateSigningRequest` interface.
+ .. method:: create_x509_certificate(builder, private_key, algorithm)
+
+ .. versionadded:: 1.0
+
+ :param builder: An instance of
+ :class:`~cryptography.x509.CertificateBuilder`.
+
+ :param private_key: The
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey`,
+ :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey` or
+ :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey`
+ that will be used to sign the certificate.
+
+ :param algorithm: The
+ :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm`
+ that will be used to generate the certificate signature.
+
+ :returns: A new object with the
+ :class:`~cryptography.x509.Certificate` interface.
+
.. class:: DHBackend
diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst
index 61971fed..d86ebbe8 100644
--- a/docs/x509/reference.rst
+++ b/docs/x509/reference.rst
@@ -909,14 +909,28 @@ X.509 Extensions
Returns an instance of the extension type corresponding to the OID.
+.. class:: ExtensionType
+
+ .. versionadded:: 1.0
+
+ This is the interface against which all the following extension types are
+ registered.
+
.. class:: KeyUsage
.. versionadded:: 0.9
The key usage extension defines the purpose of the key contained in the
certificate. The usage restriction might be employed when a key that could
- be used for more than one operation is to be restricted. It corresponds to
- :data:`OID_KEY_USAGE`.
+ be used for more than one operation is to be restricted.
+
+ .. attribute:: oid
+
+ .. versionadded:: 1.0
+
+ :type: :class:`ObjectIdentifier`
+
+ Returns :data:`OID_KEY_USAGE`.
.. attribute:: digital_signature
@@ -1007,8 +1021,15 @@ X.509 Extensions
Basic constraints is an X.509 extension type that defines whether a given
certificate is allowed to sign additional certificates and what path
- length restrictions may exist. It corresponds to
- :data:`OID_BASIC_CONSTRAINTS`.
+ length restrictions may exist.
+
+ .. attribute:: oid
+
+ .. versionadded:: 1.0
+
+ :type: :class:`ObjectIdentifier`
+
+ Returns :data:`OID_BASIC_CONSTRAINTS`.
.. attribute:: ca
@@ -1038,6 +1059,15 @@ X.509 Extensions
purposes indicated in the key usage extension. The object is
iterable to obtain the list of :ref:`extended key usage OIDs <eku_oids>`.
+ .. attribute:: oid
+
+ .. versionadded:: 1.0
+
+ :type: :class:`ObjectIdentifier`
+
+ Returns :data:`OID_EXTENDED_KEY_USAGE`.
+
+
.. class:: OCSPNoCheck
.. versionadded:: 1.0
@@ -1051,6 +1081,14 @@ X.509 Extensions
extension is only relevant when the certificate is an authorized OCSP
responder.
+ .. attribute:: oid
+
+ .. versionadded:: 1.0
+
+ :type: :class:`ObjectIdentifier`
+
+ Returns :data:`OID_OCSP_NO_CHECK`.
+
.. class:: NameConstraints
.. versionadded:: 1.0
@@ -1060,6 +1098,14 @@ X.509 Extensions
beneath the CA certificate must (or must not) be in. For specific details
on the way this extension should be processed see :rfc:`5280`.
+ .. attribute:: oid
+
+ .. versionadded:: 1.0
+
+ :type: :class:`ObjectIdentifier`
+
+ Returns :data:`OID_NAME_CONSTRAINTS`.
+
.. attribute:: permitted_subtrees
:type: list of :class:`GeneralName` objects or None
@@ -1087,6 +1133,14 @@ X.509 Extensions
certificate chain. For more information about generation and use of this
extension see `RFC 5280 section 4.2.1.1`_.
+ .. attribute:: oid
+
+ .. versionadded:: 1.0
+
+ :type: :class:`ObjectIdentifier`
+
+ Returns :data:`OID_AUTHORITY_KEY_IDENTIFIER`.
+
.. attribute:: key_identifier
:type: bytes
@@ -1106,6 +1160,37 @@ X.509 Extensions
The serial number of the issuer's issuer.
+ .. classmethod:: from_issuer_public_key(public_key)
+
+ .. versionadded:: 1.0
+
+ Creates a new AuthorityKeyIdentifier instance using the public key
+ provided to generate the appropriate digest. This should be the
+ **issuer's public key**. The resulting object will contain
+ :attr:`~cryptography.x509.AuthorityKeyIdentifier.key_identifier`, but
+ :attr:`~cryptography.x509.AuthorityKeyIdentifier.authority_cert_issuer`
+ and
+ :attr:`~cryptography.x509.AuthorityKeyIdentifier.authority_cert_serial_number`
+ will be None.
+ The generated ``key_identifier`` is the SHA1 hash of the ``subjectPublicKey``
+ ASN.1 bit string. This is the first recommendation in :rfc:`5280`
+ section 4.2.1.2.
+
+ :param public_key: One of
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey`
+ ,
+ :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey`
+ , or
+ :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey`.
+
+ .. doctest::
+
+ >>> from cryptography import x509
+ >>> from cryptography.hazmat.backends import default_backend
+ >>> issuer_cert = x509.load_pem_x509_certificate(pem_data, default_backend())
+ >>> x509.AuthorityKeyIdentifier.from_issuer_public_key(issuer_cert.public_key())
+ <AuthorityKeyIdentifier(key_identifier='X\x01\x84$\x1b\xbc+R\x94J=\xa5\x10r\x14Q\xf5\xaf:\xc9', authority_cert_issuer=None, authority_cert_serial_number=None)>
+
.. class:: SubjectKeyIdentifier
.. versionadded:: 0.9
@@ -1113,12 +1198,45 @@ X.509 Extensions
The subject key identifier extension provides a means of identifying
certificates that contain a particular public key.
+ .. attribute:: oid
+
+ .. versionadded:: 1.0
+
+ :type: :class:`ObjectIdentifier`
+
+ Returns :data:`OID_SUBJECT_KEY_IDENTIFIER`.
+
.. attribute:: digest
:type: bytes
The binary value of the identifier.
+ .. classmethod:: from_public_key(public_key)
+
+ .. versionadded:: 1.0
+
+ Creates a new SubjectKeyIdentifier instance using the public key
+ provided to generate the appropriate digest. This should be the public
+ key that is in the certificate. The generated digest is the SHA1 hash
+ of the ``subjectPublicKey`` ASN.1 bit string. This is the first
+ recommendation in :rfc:`5280` section 4.2.1.2.
+
+ :param public_key: One of
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey`
+ ,
+ :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey`
+ , or
+ :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey`.
+
+ .. doctest::
+
+ >>> from cryptography import x509
+ >>> from cryptography.hazmat.backends import default_backend
+ >>> csr = x509.load_pem_x509_csr(pem_req_data, default_backend())
+ >>> x509.SubjectKeyIdentifier.from_public_key(csr.public_key())
+ <SubjectKeyIdentifier(digest='\xdb\xaa\xf0\x06\x11\xdbD\xfe\xbf\x93\x03\x8av\x88WP7\xa6\x91\xf7')>
+
.. class:: SubjectAlternativeName
.. versionadded:: 0.9
@@ -1128,6 +1246,14 @@ X.509 Extensions
of identities for which the certificate is valid. The object is iterable to
get every element.
+ .. attribute:: oid
+
+ .. versionadded:: 1.0
+
+ :type: :class:`ObjectIdentifier`
+
+ Returns :data:`OID_SUBJECT_ALTERNATIVE_NAME`.
+
.. method:: get_values_for_type(type)
:param type: A :class:`GeneralName` provider. This is one of the
@@ -1158,6 +1284,14 @@ X.509 Extensions
of identities for the certificate issuer. The object is iterable to
get every element.
+ .. attribute:: oid
+
+ .. versionadded:: 1.0
+
+ :type: :class:`ObjectIdentifier`
+
+ Returns :data:`OID_ISSUER_ALTERNATIVE_NAME`.
+
.. method:: get_values_for_type(type)
:param type: A :class:`GeneralName` provider. This is one of the
@@ -1176,6 +1310,14 @@ X.509 Extensions
validation services (such as OCSP) and issuer data. It is an iterable,
containing one or more :class:`AccessDescription` instances.
+ .. attribute:: oid
+
+ .. versionadded:: 1.0
+
+ :type: :class:`ObjectIdentifier`
+
+ Returns :data:`OID_AUTHORITY_INFORMATION_ACCESS`.
+
.. class:: AccessDescription
@@ -1206,6 +1348,14 @@ X.509 Extensions
obtained. It is an iterable, containing one or more
:class:`DistributionPoint` instances.
+ .. attribute:: oid
+
+ .. versionadded:: 1.0
+
+ :type: :class:`ObjectIdentifier`
+
+ Returns :data:`OID_CRL_DISTRIBUTION_POINTS`.
+
.. class:: DistributionPoint
.. versionadded:: 0.9
@@ -1304,6 +1454,14 @@ X.509 Extensions
certificates issued by the subject of this certificate, but not in
additional certificates in the path.
+ .. attribute:: oid
+
+ .. versionadded:: 1.0
+
+ :type: :class:`ObjectIdentifier`
+
+ Returns :data:`OID_INHIBIT_ANY_POLICY`.
+
.. attribute:: skip_certs
:type: int
@@ -1315,6 +1473,14 @@ X.509 Extensions
The certificate policies extension is an iterable, containing one or more
:class:`PolicyInformation` instances.
+ .. attribute:: oid
+
+ .. versionadded:: 1.0
+
+ :type: :class:`ObjectIdentifier`
+
+ Returns :data:`OID_CERTIFICATE_POLICIES`.
+
Certificate Policies Classes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1668,6 +1834,11 @@ Extension OIDs
Corresponds to the dotted string ``"1.3.6.1.5.5.7.1.1"``. The identifier
for the :class:`AuthorityInformationAccess` extension type.
+.. data:: OID_INHIBIT_ANY_POLICY
+
+ Corresponds to the dotted string ``"2.5.29.54"``. The identifier
+ for the :class:`InhibitAnyPolicy` extension type.
+
.. data:: OID_OCSP_NO_CHECK
Corresponds to the dotted string ``"1.3.6.1.5.5.7.48.1.5"``. The identifier
diff --git a/src/_cffi_src/openssl/asn1.py b/src/_cffi_src/openssl/asn1.py
index 44e9de17..96084721 100644
--- a/src/_cffi_src/openssl/asn1.py
+++ b/src/_cffi_src/openssl/asn1.py
@@ -43,6 +43,7 @@ typedef struct asn1_string_st ASN1_IA5STRING;
typedef ... ASN1_BIT_STRING;
typedef ... ASN1_OBJECT;
typedef struct asn1_string_st ASN1_STRING;
+typedef struct asn1_string_st ASN1_UTF8STRING;
typedef ... ASN1_TYPE;
typedef ... ASN1_GENERALIZEDTIME;
typedef ... ASN1_ENUMERATED;
@@ -125,9 +126,13 @@ int ASN1_BIT_STRING_set_bit(ASN1_BIT_STRING *, int, int);
"""
MACROS = """
+ASN1_UTF8STRING *ASN1_UTF8STRING_new(void);
+void ASN1_UTF8STRING_free(ASN1_UTF8STRING *);
+
ASN1_BIT_STRING *ASN1_BIT_STRING_new(void);
void ASN1_BIT_STRING_free(ASN1_BIT_STRING *);
int i2d_ASN1_BIT_STRING(ASN1_BIT_STRING *, unsigned char **);
+int i2d_ASN1_OCTET_STRING(ASN1_OCTET_STRING *, unsigned char **);
/* This is not a macro, but is const on some versions of OpenSSL */
int ASN1_BIT_STRING_get_bit(ASN1_BIT_STRING *, int);
ASN1_TIME *M_ASN1_TIME_dup(void *);
diff --git a/src/_cffi_src/openssl/ec.py b/src/_cffi_src/openssl/ec.py
index 93ca2754..10c87c33 100644
--- a/src/_cffi_src/openssl/ec.py
+++ b/src/_cffi_src/openssl/ec.py
@@ -397,7 +397,7 @@ static const long Cryptography_HAS_EC2M = 1;
#endif
#if defined(OPENSSL_NO_EC) || OPENSSL_VERSION_NUMBER < 0x1000200f || \
- defined(LIBRESSL_VERSION_NUMBER)
+ defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20020002L
static const long Cryptography_HAS_EC_1_0_2 = 0;
const char *(*EC_curve_nid2nist)(int) = NULL;
#else
diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py
index 6e35dacc..84e49640 100644
--- a/src/_cffi_src/openssl/x509v3.py
+++ b/src/_cffi_src/openssl/x509v3.py
@@ -279,6 +279,7 @@ void sk_ASN1_INTEGER_free(Cryptography_STACK_OF_ASN1_INTEGER *);
int sk_ASN1_INTEGER_num(Cryptography_STACK_OF_ASN1_INTEGER *);
ASN1_INTEGER *sk_ASN1_INTEGER_value(Cryptography_STACK_OF_ASN1_INTEGER *, int);
int sk_ASN1_INTEGER_push(Cryptography_STACK_OF_ASN1_INTEGER *, ASN1_INTEGER *);
+Cryptography_STACK_OF_ASN1_INTEGER *sk_ASN1_INTEGER_new_null(void);
X509_EXTENSION *X509V3_EXT_i2d(int, int, void *);
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 cf294c01..6675f677 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
@@ -181,6 +182,36 @@ def _encode_key_usage(backend, key_usage):
return pp, r
+def _encode_authority_key_identifier(backend, authority_keyid):
+ akid = backend._lib.AUTHORITY_KEYID_new()
+ assert akid != backend._ffi.NULL
+ akid = backend._ffi.gc(akid, backend._lib.AUTHORITY_KEYID_free)
+ if authority_keyid.key_identifier is not None:
+ akid.keyid = _encode_asn1_str(
+ backend,
+ authority_keyid.key_identifier,
+ len(authority_keyid.key_identifier)
+ )
+
+ if authority_keyid.authority_cert_issuer is not None:
+ akid.issuer = _encode_general_names(
+ backend, authority_keyid.authority_cert_issuer
+ )
+
+ if authority_keyid.authority_cert_serial_number is not None:
+ akid.serial = _encode_asn1_int(
+ backend, authority_keyid.authority_cert_serial_number
+ )
+
+ pp = backend._ffi.new('unsigned char **')
+ r = backend._lib.i2d_AUTHORITY_KEYID(akid, pp)
+ assert r > 0
+ pp = backend._ffi.gc(
+ pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0])
+ )
+ return pp, r
+
+
def _encode_basic_constraints(backend, basic_constraints):
constraints = backend._lib.BASIC_CONSTRAINTS_new()
constraints = backend._ffi.gc(
@@ -253,6 +284,17 @@ def _encode_subject_alt_name(backend, san):
return pp, r
+def _encode_subject_key_identifier(backend, ski):
+ asn1_str = _encode_asn1_str_gc(backend, ski.digest, len(ski.digest))
+ pp = backend._ffi.new("unsigned char **")
+ r = backend._lib.i2d_ASN1_OCTET_STRING(asn1_str, pp)
+ assert r > 0
+ pp = backend._ffi.gc(
+ pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0])
+ )
+ return pp, r
+
+
def _encode_general_name(backend, name):
if isinstance(name, x509.DNSName):
gn = backend._lib.GENERAL_NAME_new()
@@ -359,6 +401,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)
@@ -1100,7 +1203,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):
@@ -1167,26 +1270,36 @@ class Backend(object):
for i, extension in enumerate(builder._extensions):
if isinstance(extension.value, x509.BasicConstraints):
pp, r = _encode_basic_constraints(self, extension.value)
+ elif isinstance(extension.value, x509.AuthorityKeyIdentifier):
+ pp, r = _encode_authority_key_identifier(self, extension.value)
elif isinstance(extension.value, x509.KeyUsage):
pp, r = _encode_key_usage(self, extension.value)
elif isinstance(extension.value, x509.ExtendedKeyUsage):
pp, r = _encode_extended_key_usage(self, extension.value)
elif isinstance(extension.value, x509.SubjectAlternativeName):
pp, r = _encode_subject_alt_name(self, extension.value)
+ elif isinstance(extension.value, x509.SubjectKeyIdentifier):
+ pp, r = _encode_subject_key_identifier(self, extension.value)
elif isinstance(extension.value, x509.AuthorityInformationAccess):
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/utils.py b/src/cryptography/utils.py
index 24afe612..993571bd 100644
--- a/src/cryptography/utils.py
+++ b/src/cryptography/utils.py
@@ -5,6 +5,7 @@
from __future__ import absolute_import, division, print_function
import abc
+import binascii
import inspect
import struct
import sys
@@ -46,6 +47,12 @@ else:
return result
+def int_to_bytes(integer):
+ hex_string = '%x' % integer
+ n = len(hex_string)
+ return binascii.unhexlify(hex_string.zfill(n + (n & 1)))
+
+
class InterfaceNotImplemented(Exception):
pass
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index 0ddff728..397274e8 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -6,21 +6,53 @@ from __future__ import absolute_import, division, print_function
import abc
import datetime
+import hashlib
import ipaddress
from email.utils import parseaddr
from enum import Enum
import idna
+from pyasn1.codec.der import decoder
+from pyasn1.type import namedtype, univ
+
import six
from six.moves import urllib_parse
from cryptography import utils
-from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
+class _SubjectPublicKeyInfo(univ.Sequence):
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType('algorithm', univ.Sequence()),
+ namedtype.NamedType('subjectPublicKey', univ.BitString())
+ )
+
+
+def _key_identifier_from_public_key(public_key):
+ # This is a very slow way to do this.
+ serialized = public_key.public_bytes(
+ serialization.Encoding.DER,
+ serialization.PublicFormat.SubjectPublicKeyInfo
+ )
+ spki, remaining = decoder.decode(
+ serialized, asn1Spec=_SubjectPublicKeyInfo()
+ )
+ assert not remaining
+ # the univ.BitString object is a tuple of bits. We need bytes and
+ # pyasn1 really doesn't want to give them to us. To get it we'll
+ # build an integer and convert that to bytes.
+ bits = 0
+ for bit in spki.getComponentByName("subjectPublicKey"):
+ bits = bits << 1 | bit
+
+ data = utils.int_to_bytes(bits)
+ return hashlib.sha1(data).digest()
+
+
_OID_NAMES = {
"2.5.4.3": "commonName",
"2.5.4.6": "countryName",
@@ -313,7 +345,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(
@@ -341,11 +385,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")
@@ -381,7 +429,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):
@@ -464,7 +515,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(
@@ -528,7 +582,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(
@@ -665,10 +722,17 @@ 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
+ @classmethod
+ def from_public_key(cls, public_key):
+ return cls(_key_identifier_from_public_key(public_key))
+
digest = utils.read_only_property("_digest")
def __repr__(self):
@@ -686,7 +750,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(
@@ -750,7 +817,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
@@ -785,7 +855,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(
@@ -870,7 +941,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")
@@ -1160,7 +1234,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)
@@ -1186,7 +1263,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)
@@ -1212,7 +1292,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:
@@ -1239,6 +1322,15 @@ class AuthorityKeyIdentifier(object):
self._authority_cert_issuer = authority_cert_issuer
self._authority_cert_serial_number = authority_cert_serial_number
+ @classmethod
+ def from_issuer_public_key(cls, public_key):
+ digest = _key_identifier_from_public_key(public_key)
+ return cls(
+ key_identifier=digest,
+ authority_cert_issuer=None,
+ authority_cert_serial_number=None
+ )
+
def __repr__(self):
return (
"<AuthorityKeyIdentifier(key_identifier={0.key_identifier!r}, "
@@ -1720,6 +1812,10 @@ class CertificateBuilder(object):
"""
if isinstance(extension, BasicConstraints):
extension = Extension(OID_BASIC_CONSTRAINTS, critical, extension)
+ elif isinstance(extension, AuthorityKeyIdentifier):
+ extension = Extension(
+ OID_AUTHORITY_KEY_IDENTIFIER, critical, extension
+ )
elif isinstance(extension, KeyUsage):
extension = Extension(OID_KEY_USAGE, critical, extension)
elif isinstance(extension, ExtendedKeyUsage):
@@ -1732,8 +1828,16 @@ class CertificateBuilder(object):
extension = Extension(
OID_AUTHORITY_INFORMATION_ACCESS, critical, extension
)
+ elif isinstance(extension, SubjectKeyIdentifier):
+ extension = Extension(
+ OID_SUBJECT_KEY_IDENTIFIER, critical, extension
+ )
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.')
@@ -1770,4 +1874,4 @@ class CertificateBuilder(object):
if self._public_key is None:
raise ValueError("A certificate must have a public key")
- return backend.sign_x509_certificate(self, private_key, algorithm)
+ return backend.create_x509_certificate(self, private_key, algorithm)
diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py
index d516af16..cc59a8d4 100644
--- a/tests/hazmat/backends/test_multibackend.py
+++ b/tests/hazmat/backends/test_multibackend.py
@@ -206,7 +206,7 @@ class DummyX509Backend(object):
def create_x509_csr(self, builder, private_key, algorithm):
pass
- def sign_x509_certificate(self, builder, private_key, algorithm):
+ def create_x509_certificate(self, builder, private_key, algorithm):
pass
@@ -487,7 +487,7 @@ class TestMultiBackend(object):
backend.load_pem_x509_csr(b"reqdata")
backend.load_der_x509_csr(b"reqdata")
backend.create_x509_csr(object(), b"privatekey", hashes.SHA1())
- backend.sign_x509_certificate(object(), b"privatekey", hashes.SHA1())
+ backend.create_x509_certificate(object(), b"privatekey", hashes.SHA1())
backend = MultiBackend([])
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
@@ -501,6 +501,6 @@ class TestMultiBackend(object):
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
backend.create_x509_csr(object(), b"privatekey", hashes.SHA1())
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_X509):
- backend.sign_x509_certificate(
+ backend.create_x509_certificate(
object(), b"privatekey", hashes.SHA1()
)
diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py
index bd9aadb8..051827af 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -512,7 +512,7 @@ class TestOpenSSLSignX509Certificate(object):
private_key = RSA_KEY_2048.private_key(backend)
with pytest.raises(TypeError):
- backend.sign_x509_certificate(object(), private_key, DummyHash())
+ backend.create_x509_certificate(object(), private_key, DummyHash())
def test_checks_for_unsupported_extensions(self):
private_key = RSA_KEY_2048.private_key(backend)
diff --git a/tests/test_x509.py b/tests/test_x509.py
index 5e0342cb..9ca8931d 100644
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -1143,6 +1143,154 @@ class TestCertificateBuilder(object):
with pytest.raises(NotImplementedError):
builder.sign(private_key, hashes.SHA512(), backend)
+ @pytest.mark.parametrize(
+ "cdp",
+ [
+ x509.CRLDistributionPoints([
+ x509.DistributionPoint(
+ full_name=None,
+ relative_name=x509.Name([
+ x509.NameAttribute(
+ x509.OID_COMMON_NAME,
+ u"indirect CRL for indirectCRL CA3"
+ ),
+ ]),
+ reasons=None,
+ crl_issuer=[x509.DirectoryName(
+ x509.Name([
+ x509.NameAttribute(x509.OID_COUNTRY_NAME, u"US"),
+ x509.NameAttribute(
+ x509.OID_ORGANIZATION_NAME,
+ u"Test Certificates 2011"
+ ),
+ x509.NameAttribute(
+ x509.OID_ORGANIZATIONAL_UNIT_NAME,
+ u"indirectCRL CA3 cRLIssuer"
+ ),
+ ])
+ )],
+ )
+ ]),
+ x509.CRLDistributionPoints([
+ x509.DistributionPoint(
+ full_name=[x509.DirectoryName(
+ x509.Name([
+ x509.NameAttribute(x509.OID_COUNTRY_NAME, u"US"),
+ ])
+ )],
+ relative_name=None,
+ reasons=None,
+ crl_issuer=[x509.DirectoryName(
+ x509.Name([
+ x509.NameAttribute(
+ x509.OID_ORGANIZATION_NAME,
+ u"cryptography Testing"
+ ),
+ ])
+ )],
+ )
+ ]),
+ x509.CRLDistributionPoints([
+ x509.DistributionPoint(
+ full_name=[
+ x509.UniformResourceIdentifier(
+ u"http://myhost.com/myca.crl"
+ ),
+ x509.UniformResourceIdentifier(
+ u"http://backup.myhost.com/myca.crl"
+ )
+ ],
+ relative_name=None,
+ reasons=frozenset([
+ x509.ReasonFlags.key_compromise,
+ x509.ReasonFlags.ca_compromise
+ ]),
+ crl_issuer=[x509.DirectoryName(
+ x509.Name([
+ x509.NameAttribute(x509.OID_COUNTRY_NAME, u"US"),
+ x509.NameAttribute(
+ x509.OID_COMMON_NAME, u"cryptography CA"
+ ),
+ ])
+ )],
+ )
+ ]),
+ x509.CRLDistributionPoints([
+ x509.DistributionPoint(
+ full_name=[x509.UniformResourceIdentifier(
+ u"http://domain.com/some.crl"
+ )],
+ relative_name=None,
+ reasons=frozenset([
+ x509.ReasonFlags.key_compromise,
+ x509.ReasonFlags.ca_compromise,
+ x509.ReasonFlags.affiliation_changed,
+ x509.ReasonFlags.superseded,
+ x509.ReasonFlags.privilege_withdrawn,
+ x509.ReasonFlags.cessation_of_operation,
+ x509.ReasonFlags.aa_compromise,
+ x509.ReasonFlags.certificate_hold,
+ ]),
+ crl_issuer=None
+ )
+ ]),
+ x509.CRLDistributionPoints([
+ x509.DistributionPoint(
+ full_name=None,
+ relative_name=None,
+ reasons=None,
+ crl_issuer=[x509.DirectoryName(
+ x509.Name([
+ x509.NameAttribute(
+ x509.OID_COMMON_NAME, u"cryptography CA"
+ ),
+ ])
+ )],
+ )
+ ]),
+ x509.CRLDistributionPoints([
+ x509.DistributionPoint(
+ full_name=[x509.UniformResourceIdentifier(
+ u"http://domain.com/some.crl"
+ )],
+ relative_name=None,
+ reasons=frozenset([x509.ReasonFlags.aa_compromise]),
+ crl_issuer=None
+ )
+ ])
+ ]
+ )
+ @pytest.mark.requires_backend_interface(interface=RSABackend)
+ @pytest.mark.requires_backend_interface(interface=X509Backend)
+ def test_crl_distribution_points(self, backend, cdp):
+ issuer_private_key = RSA_KEY_2048.private_key(backend)
+ subject_private_key = RSA_KEY_2048.private_key(backend)
+
+ builder = x509.CertificateBuilder().serial_number(
+ 4444444
+ ).issuer_name(x509.Name([
+ x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'),
+ ])).subject_name(x509.Name([
+ x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'),
+ ])).public_key(
+ subject_private_key.public_key()
+ ).add_extension(
+ cdp,
+ critical=False,
+ ).not_valid_before(
+ datetime.datetime(2002, 1, 1, 12, 1)
+ ).not_valid_after(
+ datetime.datetime(2030, 12, 31, 8, 30)
+ )
+
+ cert = builder.sign(issuer_private_key, hashes.SHA1(), backend)
+
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_CRL_DISTRIBUTION_POINTS
+ )
+ assert ext.critical is False
+ assert ext.value == cdp
+
@pytest.mark.requires_backend_interface(interface=DSABackend)
@pytest.mark.requires_backend_interface(interface=X509Backend)
def test_build_cert_with_dsa_private_key(self, backend):
@@ -1842,6 +1990,118 @@ class TestCertificateSigningRequestBuilder(object):
)
assert ext.value == aia
+ @pytest.mark.requires_backend_interface(interface=RSABackend)
+ @pytest.mark.requires_backend_interface(interface=X509Backend)
+ def test_build_cert_with_ski(self, backend):
+ issuer_private_key = RSA_KEY_2048.private_key(backend)
+ subject_private_key = RSA_KEY_2048.private_key(backend)
+
+ not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
+ not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)
+
+ ski = x509.SubjectKeyIdentifier.from_public_key(
+ subject_private_key.public_key()
+ )
+
+ builder = x509.CertificateBuilder().serial_number(
+ 777
+ ).issuer_name(x509.Name([
+ x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'),
+ ])).subject_name(x509.Name([
+ x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'),
+ ])).public_key(
+ subject_private_key.public_key()
+ ).add_extension(
+ ski, critical=False
+ ).not_valid_before(
+ not_valid_before
+ ).not_valid_after(
+ not_valid_after
+ )
+
+ cert = builder.sign(issuer_private_key, hashes.SHA1(), backend)
+
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_SUBJECT_KEY_IDENTIFIER
+ )
+ assert ext.value == ski
+
+ @pytest.mark.parametrize(
+ "aki",
+ [
+ x509.AuthorityKeyIdentifier(
+ b"\xc3\x9c\xf3\xfc\xd3F\x084\xbb\xceF\x7f\xa0|[\xf3\xe2\x08"
+ b"\xcbY",
+ None,
+ None
+ ),
+ x509.AuthorityKeyIdentifier(
+ b"\xc3\x9c\xf3\xfc\xd3F\x084\xbb\xceF\x7f\xa0|[\xf3\xe2\x08"
+ b"\xcbY",
+ [
+ x509.DirectoryName(
+ x509.Name([
+ x509.NameAttribute(
+ x509.OID_ORGANIZATION_NAME, u"PyCA"
+ ),
+ x509.NameAttribute(
+ x509.OID_COMMON_NAME, u"cryptography CA"
+ )
+ ])
+ )
+ ],
+ 333
+ ),
+ x509.AuthorityKeyIdentifier(
+ None,
+ [
+ x509.DirectoryName(
+ x509.Name([
+ x509.NameAttribute(
+ x509.OID_ORGANIZATION_NAME, u"PyCA"
+ ),
+ x509.NameAttribute(
+ x509.OID_COMMON_NAME, u"cryptography CA"
+ )
+ ])
+ )
+ ],
+ 333
+ ),
+ ]
+ )
+ @pytest.mark.requires_backend_interface(interface=RSABackend)
+ @pytest.mark.requires_backend_interface(interface=X509Backend)
+ def test_build_cert_with_aki(self, aki, backend):
+ issuer_private_key = RSA_KEY_2048.private_key(backend)
+ subject_private_key = RSA_KEY_2048.private_key(backend)
+
+ not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
+ not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)
+
+ builder = x509.CertificateBuilder().serial_number(
+ 777
+ ).issuer_name(x509.Name([
+ x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'),
+ ])).subject_name(x509.Name([
+ x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'),
+ ])).public_key(
+ subject_private_key.public_key()
+ ).add_extension(
+ aki, critical=False
+ ).not_valid_before(
+ not_valid_before
+ ).not_valid_after(
+ not_valid_after
+ )
+
+ cert = builder.sign(issuer_private_key, hashes.SHA256(), backend)
+
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_AUTHORITY_KEY_IDENTIFIER
+ )
+ assert ext.value == aki
+
@pytest.mark.requires_backend_interface(interface=DSABackend)
@pytest.mark.requires_backend_interface(interface=X509Backend)
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 890709ae..40231b93 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -13,8 +13,12 @@ import pytest
import six
from cryptography import x509
-from cryptography.hazmat.backends.interfaces import RSABackend, X509Backend
+from cryptography.hazmat.backends.interfaces import (
+ DSABackend, EllipticCurveBackend, RSABackend, X509Backend
+)
+from cryptography.hazmat.primitives.asymmetric import ec
+from .hazmat.primitives.test_ec import _skip_curve_unsupported
from .test_x509 import _load_cert
@@ -917,9 +921,9 @@ class TestBasicConstraintsExtension(object):
assert ext.value.ca is False
-@pytest.mark.requires_backend_interface(interface=RSABackend)
-@pytest.mark.requires_backend_interface(interface=X509Backend)
class TestSubjectKeyIdentifierExtension(object):
+ @pytest.mark.requires_backend_interface(interface=RSABackend)
+ @pytest.mark.requires_backend_interface(interface=X509Backend)
def test_subject_key_identifier(self, backend):
cert = _load_cert(
os.path.join("x509", "PKITS_data", "certs", "GoodCACert.crt"),
@@ -936,6 +940,8 @@ class TestSubjectKeyIdentifierExtension(object):
b"580184241bbc2b52944a3da510721451f5af3ac9"
)
+ @pytest.mark.requires_backend_interface(interface=RSABackend)
+ @pytest.mark.requires_backend_interface(interface=X509Backend)
def test_no_subject_key_identifier(self, backend):
cert = _load_cert(
os.path.join("x509", "custom", "bc_path_length_zero.pem"),
@@ -947,6 +953,57 @@ class TestSubjectKeyIdentifierExtension(object):
x509.OID_SUBJECT_KEY_IDENTIFIER
)
+ @pytest.mark.requires_backend_interface(interface=RSABackend)
+ @pytest.mark.requires_backend_interface(interface=X509Backend)
+ def test_from_rsa_public_key(self, backend):
+ cert = _load_cert(
+ os.path.join("x509", "PKITS_data", "certs", "GoodCACert.crt"),
+ x509.load_der_x509_certificate,
+ backend
+ )
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_SUBJECT_KEY_IDENTIFIER
+ )
+ ski = x509.SubjectKeyIdentifier.from_public_key(
+ cert.public_key()
+ )
+ assert ext.value == ski
+
+ @pytest.mark.requires_backend_interface(interface=DSABackend)
+ @pytest.mark.requires_backend_interface(interface=X509Backend)
+ def test_from_dsa_public_key(self, backend):
+ cert = _load_cert(
+ os.path.join("x509", "custom", "dsa_selfsigned_ca.pem"),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_SUBJECT_KEY_IDENTIFIER
+ )
+ ski = x509.SubjectKeyIdentifier.from_public_key(
+ cert.public_key()
+ )
+ assert ext.value == ski
+
+ @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
+ @pytest.mark.requires_backend_interface(interface=X509Backend)
+ def test_from_ec_public_key(self, backend):
+ _skip_curve_unsupported(backend, ec.SECP384R1())
+ cert = _load_cert(
+ os.path.join("x509", "ecdsa_root.pem"),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_SUBJECT_KEY_IDENTIFIER
+ )
+ ski = x509.SubjectKeyIdentifier.from_public_key(
+ cert.public_key()
+ )
+ assert ext.value == ski
+
@pytest.mark.requires_backend_interface(interface=RSABackend)
@pytest.mark.requires_backend_interface(interface=X509Backend)
@@ -2055,6 +2112,25 @@ class TestAuthorityKeyIdentifierExtension(object):
]
assert ext.value.authority_cert_serial_number == 3
+ def test_from_certificate(self, backend):
+ issuer_cert = _load_cert(
+ os.path.join("x509", "rapidssl_sha256_ca_g3.pem"),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ cert = _load_cert(
+ os.path.join("x509", "cryptography.io.pem"),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_AUTHORITY_KEY_IDENTIFIER
+ )
+ aki = x509.AuthorityKeyIdentifier.from_issuer_public_key(
+ issuer_cert.public_key()
+ )
+ assert ext.value == aki
+
class TestNameConstraints(object):
def test_ipaddress_wrong_type(self):