Repository: pyca/cryptography

Issue:
x509.AuthorityKeyIdentifier documented use is buggy
[Docs here](https://cryptography.io/en/latest/x509/reference/#cryptography.x509.AuthorityKeyIdentifier.from_issuer_public_key)

used in some of my own code like so:

``` python
builder.add_extension(
    x509.AuthorityKeyIdentifier.from_issuer_public_key(ca_cert.public_key()), critical=False
    )
```

https://gist.github.com/jrabbit/e84b3c593e18bcf7d2dd#file-cert-py-L57
output cert fails to verify in `openssl x509 verify` & `certtool --verify`

the AKI it computes seems to be the SKI?



Discussion:
Is there any chance you can make the example generate the CA cert/key in memory so that the example is runnable standalone?

https://gist.github.com/jrabbit/e6d6bed8a13dae019a8e so this works (verifies in certtool & openssl verify) so maybe it's the CA cert being read wrong?

I can provide the ca cert it's not sensitive

Ok when loading from a gnutls generated CA cert/key it has issues. https://gist.github.com/jrabbit/009ca3e92a0ab5ae8ad6 not just this set but a fresh one without the unicode also. I'll see if cryptography generated key loaded from disk has issues in a bit

It looks like the issue here is that the method of generating the key identifier that we use doesn't mesh with the way it is generated by gnutls (RFC 5280 defines two methods but also says "go nuts and make your own": https://tools.ietf.org/html/rfc5280#section-4.2.1.2)

I can make this work just fine with the gnutls cert by changing the the AKI generation to do this (which just copies the CA's SKI digest value to the cert's AKI key_identifier):

```
    ext = ca_cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
    builder = builder.add_extension(
        x509.AuthorityKeyIdentifier(
            key_identifier=ext.value.digest, 
            authority_cert_issuer=None, 
            authority_cert_serial_number=None),
        ext.critical
        )
```

One way we could handle this would be to add a `AuthorityKeyIdentifier.copy_from_ca_ski(cert)`, although that would fail if the parent certificate does not have a SKI.

`AKI.from_issuer_subject_key_identifier(cert)` sounds like a great solution.

On Sun, Mar 6, 2016 at 4:25 PM, Paul Kehrer notifications@github.com
wrote:

> It looks like the issue here is that the method of generating the key
> identifier that we use doesn't mesh with the way it is generated by gnutls
> (RFC 5280 defines two methods but also says "go nuts and make your own":
> https://tools.ietf.org/html/rfc5280#section-4.2.1.2)
> 
> I can make this work just fine with the gnutls cert by changing the the
> AKI generation to do this (which just copies the CA's SKI digest value to
> the cert's AKI key_identifier):
> 
> ```
> ext = ca_cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
> builder = builder.add_extension(
>     x509.AuthorityKeyIdentifier(
>         key_identifier=ext.value.digest,
>         authority_cert_issuer=None,
>         authority_cert_serial_number=None),
>     ext.critical
>     )
> ```
> 
> One way we could handle this would be to add a
> AuthorityKeyIdentifier.copy_from_ca_ski(cert), although that would fail
> if the parent certificate does not have a SKI.
> 
> —
> Reply to this email directly or view it on GitHub
> https://github.com/pyca/cryptography/issues/2758#issuecomment-193000323.

## 

"I disapprove of what you say, but I will defend to the death your right to
say it." -- Evelyn Beatrice Hall (summarizing Voltaire)
"The people's good is the highest law." -- Cicero
GPG Key fingerprint: 125F 5C67 DFE9 4084


Resolving patch:
diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py
--- a/src/cryptography/x509/extensions.py
+++ b/src/cryptography/x509/extensions.py
@@ -191,6 +191,14 @@ def from_issuer_public_key(cls, public_key):
             authority_cert_serial_number=None
         )
 
+    @classmethod
+    def from_issuer_subject_key_identifier(cls, ski):
+        return cls(
+            key_identifier=ski.value.digest,
+            authority_cert_issuer=None,
+            authority_cert_serial_number=None
+        )
+
     def __repr__(self):
         return (
             "<AuthorityKeyIdentifier(key_identifier={0.key_identifier!r}, "


Test patch:
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -2634,6 +2634,28 @@ def test_from_certificate(self, backend):
         )
         assert ext.value == aki
 
+    def test_from_issuer_subject_key_identifier(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(
+            ExtensionOID.AUTHORITY_KEY_IDENTIFIER
+        )
+        ski = issuer_cert.extensions.get_extension_for_class(
+            x509.SubjectKeyIdentifier
+        )
+        aki = x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(
+            ski
+        )
+        assert ext.value == aki
+
 
 class TestNameConstraints(object):
     def test_ipaddress_wrong_type(self):