Merge pull request #2228 from alex/rand-int

Document how to get a random number as an integer, fixes #2190
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 63877d8..5c808f3 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -48,10 +48,16 @@
 
 * 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/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index fdd38fa..6675f67 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -182,6 +182,36 @@
     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 @@
         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 da7603c..397274e 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -1812,6 +1812,10 @@
         """
         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 1cbff51..9ca8931 100644
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -2026,6 +2026,82 @@
         )
         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)