diff options
-rw-r--r-- | CHANGELOG.rst | 8 | ||||
-rw-r--r-- | docs/random-numbers.rst | 8 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 32 | ||||
-rw-r--r-- | src/cryptography/x509.py | 4 | ||||
-rw-r--r-- | tests/test_x509.py | 76 |
5 files changed, 127 insertions, 1 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 63877d8a..5c808f39 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -48,10 +48,16 @@ Changelog * Add support for creating signed certificates with :class:`~cryptography.x509.CertificateBuilder`. This includes support for - the following extensions + the following extensions: * :class:`~cryptography.x509.BasicConstraints` * :class:`~cryptography.x509.SubjectAlternativeName` + * :class:`~cryptography.x509.KeyUsage` + * :class:`~cryptography.x509.ExtendedKeyUsage` + * :class:`~cryptography.x509.SubjectKeyIdentifier` + * :class:`~cryptography.x509.AuthorityKeyIdentifier` + * :class:`~cryptography.x509.AuthorityInformationAccess` + * :class:`~cryptography.x509.CRLDistributionPoints` 0.9.3 - 2015-07-09 ~~~~~~~~~~~~~~~~~~ diff --git a/docs/random-numbers.rst b/docs/random-numbers.rst index 8b119a3e..5f94bf13 100644 --- a/docs/random-numbers.rst +++ b/docs/random-numbers.rst @@ -21,4 +21,12 @@ you can obtain them with: This will use ``/dev/urandom`` on UNIX platforms, and ``CryptGenRandom`` on Windows. +If you need your random number as an integer (for example, for +:meth:`~cryptography.x509.CertificateBuilder.serial_number`), you can use +``int.from_bytes`` to convert the result of ``os.urandom``: + +.. code-block:: pycon + + >>> serial = int.from_bytes(os.urandom(20), byteorder="big") + .. _`always use your operating system's provided random number generator`: http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/ diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index fdd38fa3..6675f677 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -182,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( @@ -1240,6 +1270,8 @@ 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): diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index da7603c4..397274e8 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -1812,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): diff --git a/tests/test_x509.py b/tests/test_x509.py index 1cbff511..9ca8931d 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -2026,6 +2026,82 @@ class TestCertificateSigningRequestBuilder(object): ) 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) |