Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for signature & tbs_certificate to Certificate #2387

Merged
merged 3 commits into from
Nov 4, 2015
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ Changelog

.. note:: This version is not yet released and is under active development.

* The :class:`~cryptography.x509.Certificate` class now has
:attr:`~cryptography.x509.Certificate.signature` and
:attr:`~cryptography.x509.Certificate.tbs_certificate` attributes.

1.1 - 2015-10-28
~~~~~~~~~~~~~~~~

Expand All @@ -30,7 +34,6 @@ Changelog
* ``countryName`` is now encoded as a ``PrintableString`` when creating subject
and issuer distinguished names with the Certificate and CSR builder classes.


1.0.2 - 2015-09-27
~~~~~~~~~~~~~~~~~~
* **SECURITY ISSUE**: The OpenSSL backend prior to 1.0.2 made extensive use
Expand Down
20 changes: 20 additions & 0 deletions docs/x509/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,26 @@ X.509 Certificate Object
<Extension(oid=<ObjectIdentifier(oid=2.5.29.32, name=certificatePolicies)>, critical=False, value=<CertificatePolicies([<PolicyInformation(policy_identifier=<ObjectIdentifier(oid=2.16.840.1.101.3.2.1.48.1, name=Unknown OID)>, policy_qualifiers=None)>])>)>
<Extension(oid=<ObjectIdentifier(oid=2.5.29.19, name=basicConstraints)>, critical=True, value=<BasicConstraints(ca=True, path_length=None)>)>

.. attribute:: signature

.. versionadded:: 1.2

:type: bytes

The bytes of the certificate's signature.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need some words to express that you probably don't want to use this?


.. attribute:: tbs_certificate
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the right way to expose this data? It feels a bit ad-hoc compared to public_bytes or something?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what the best approach is here. tbsCertificate is a subset of the ASN1 that makes up the complete certificate object. I don't think I'd like it in public_bytes because it would be beyond confusing to require users to pass the serialization type (when 99.99% of the time you just want the whole thing) in addition to encoding.


.. versionadded:: 1.2

:type: bytes

The DER encoded bytes payload (as defined by :rfc:`5280`) that is hashed
and then signed by the private key of the certificate's issuer. This
data may be used to validate a signature, but use extreme caution as
certificate validation is a complex problem that involves much more
than just signature checks.

.. method:: public_bytes(encoding)

.. versionadded:: 1.0
Expand Down
2 changes: 1 addition & 1 deletion src/_cffi_src/openssl/asn1.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

typedef struct asn1_string_st ASN1_OCTET_STRING;
typedef struct asn1_string_st ASN1_IA5STRING;
typedef ... ASN1_BIT_STRING;
typedef struct asn1_string_st ASN1_BIT_STRING;
typedef ... ASN1_OBJECT;
typedef struct asn1_string_st ASN1_STRING;
typedef struct asn1_string_st ASN1_UTF8STRING;
Expand Down
2 changes: 2 additions & 0 deletions src/_cffi_src/openssl/x509.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
typedef struct {
X509_ALGOR *sig_alg;
X509_CINF *cert_info;
ASN1_BIT_STRING *signature;
...;
} X509;

Expand Down Expand Up @@ -257,6 +258,7 @@
"""

MACROS = """
int i2d_X509_CINF(X509_CINF *, unsigned char **);
long X509_get_version(X509 *);

ASN1_TIME *X509_get_notBefore(X509 *);
Expand Down
14 changes: 14 additions & 0 deletions src/cryptography/hazmat/backends/openssl/x509.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,20 @@ def signature_hash_algorithm(self):
def extensions(self):
return _CERTIFICATE_EXTENSION_PARSER.parse(self._backend, self._x509)

@property
def signature(self):
return self._backend._asn1_string_to_bytes(self._x509.signature)

@property
def tbs_certificate(self):
pp = self._backend._ffi.new("unsigned char **")
res = self._backend._lib.i2d_X509_CINF(self._x509.cert_info, pp)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment that says that X509_CINF holds all the tbs data

self._backend.openssl_assert(res > 0)
pp = self._backend._ffi.gc(
pp, lambda pointer: self._backend._lib.OPENSSL_free(pointer[0])
)
return self._backend._ffi.buffer(pp[0], res)[:]

def public_bytes(self, encoding):
bio = self._backend._create_mem_bio()
if encoding is serialization.Encoding.PEM:
Expand Down
12 changes: 12 additions & 0 deletions src/cryptography/x509/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,18 @@ def extensions(self):
Returns an Extensions object.
"""

@abc.abstractproperty
def signature(self):
"""
Returns the signature bytes.
"""

@abc.abstractproperty
def tbs_certificate(self):
"""
Returns the tbsCertificate payload bytes as defined in RFC 5280.
"""

@abc.abstractmethod
def __eq__(self, other):
"""
Expand Down
186 changes: 185 additions & 1 deletion tests/test_x509.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@
DSABackend, EllipticCurveBackend, RSABackend, X509Backend
)
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
from cryptography.hazmat.primitives.asymmetric import dsa, ec, padding, rsa
from cryptography.hazmat.primitives.asymmetric.utils import (
decode_dss_signature
)
from cryptography.x509.oid import (
AuthorityInformationAccessOID, ExtendedKeyUsageOID, ExtensionOID, NameOID
)
Expand Down Expand Up @@ -332,6 +335,63 @@ def test_load_der_cert(self, backend):
assert fingerprint == b"6f49779533d565e8b7c1062503eab41492c38e4d"
assert isinstance(cert.signature_hash_algorithm, hashes.SHA256)

def test_signature(self, backend):
cert = _load_cert(
os.path.join("x509", "custom", "post2000utctime.pem"),
x509.load_pem_x509_certificate,
backend
)
assert cert.signature == binascii.unhexlify(
b"8e0f72fcbebe4755abcaf76c8ce0bae17cde4db16291638e1b1ce04a93cdb4c"
b"44a3486070986c5a880c14fdf8497e7d289b2630ccb21d24a3d1aa1b2d87482"
b"07f3a1e16ccdf8daa8a7ea1a33d49774f513edf09270bd8e665b6300a10f003"
b"66a59076905eb63cf10a81a0ca78a6ef3127f6cb2f6fb7f947fce22a30d8004"
b"8c243ba2c1a54c425fe12310e8a737638f4920354d4cce25cbd9dea25e6a2fe"
b"0d8579a5c8d929b9275be221975479f3f75075bcacf09526523b5fd67f7683f"
b"3cda420fabb1e9e6fc26bc0649cf61bb051d6932fac37066bb16f55903dfe78"
b"53dc5e505e2a10fbba4f9e93a0d3b53b7fa34b05d7ba6eef869bfc34b8e514f"
b"d5419f75"
)
assert len(cert.signature) == cert.public_key().key_size // 8

def test_tbs_certificate(self, backend):
cert = _load_cert(
os.path.join("x509", "custom", "post2000utctime.pem"),
x509.load_pem_x509_certificate,
backend
)
assert cert.tbs_certificate == binascii.unhexlify(
b"308202d8a003020102020900a06cb4b955f7f4db300d06092a864886f70d010"
b"10505003058310b3009060355040613024155311330110603550408130a536f"
b"6d652d53746174653121301f060355040a1318496e7465726e6574205769646"
b"769747320507479204c74643111300f0603550403130848656c6c6f20434130"
b"1e170d3134313132363231343132305a170d3134313232363231343132305a3"
b"058310b3009060355040613024155311330110603550408130a536f6d652d53"
b"746174653121301f060355040a1318496e7465726e657420576964676974732"
b"0507479204c74643111300f0603550403130848656c6c6f2043413082012230"
b"0d06092a864886f70d01010105000382010f003082010a0282010100b03af70"
b"2059e27f1e2284b56bbb26c039153bf81f295b73a49132990645ede4d2da0a9"
b"13c42e7d38d3589a00d3940d194f6e6d877c2ef812da22a275e83d8be786467"
b"48b4e7f23d10e873fd72f57a13dec732fc56ab138b1bb308399bb412cd73921"
b"4ef714e1976e09603405e2556299a05522510ac4574db5e9cb2cf5f99e8f48c"
b"1696ab3ea2d6d2ddab7d4e1b317188b76a572977f6ece0a4ad396f0150e7d8b"
b"1a9986c0cb90527ec26ca56e2914c270d2a198b632fa8a2fda55079d3d39864"
b"b6fb96ddbe331cacb3cb8783a8494ccccd886a3525078847ca01ca5f803e892"
b"14403e8a4b5499539c0b86f7a0daa45b204a8e079d8a5b03db7ba1ba3d7011a"
b"70203010001a381bc3081b9301d0603551d0e04160414d8e89dc777e4472656"
b"f1864695a9f66b7b0400ae3081890603551d23048181307f8014d8e89dc777e"
b"4472656f1864695a9f66b7b0400aea15ca45a3058310b300906035504061302"
b"4155311330110603550408130a536f6d652d53746174653121301f060355040"
b"a1318496e7465726e6574205769646769747320507479204c74643111300f06"
b"03550403130848656c6c6f204341820900a06cb4b955f7f4db300c0603551d1"
b"3040530030101ff"
)
verifier = cert.public_key().verifier(
cert.signature, padding.PKCS1v15(), cert.signature_hash_algorithm
)
verifier.update(cert.tbs_certificate)
verifier.verify()

def test_issuer(self, backend):
cert = _load_cert(
os.path.join(
Expand Down Expand Up @@ -2681,6 +2741,76 @@ def test_load_dsa_cert(self, backend):
"822ff5d234e073b901cf5941f58e1f538e71d40d", 16
)

def test_signature(self, backend):
cert = _load_cert(
os.path.join("x509", "custom", "dsa_selfsigned_ca.pem"),
x509.load_pem_x509_certificate,
backend
)
assert cert.signature == binascii.unhexlify(
b"302c021425c4a84a936ab311ee017d3cbd9a3c650bb3ae4a02145d30c64b4326"
b"86bdf925716b4ed059184396bcce"
)
r, s = decode_dss_signature(cert.signature)
assert r == 215618264820276283222494627481362273536404860490
assert s == 532023851299196869156027211159466197586787351758

def test_tbs_certificate(self, backend):
cert = _load_cert(
os.path.join("x509", "custom", "dsa_selfsigned_ca.pem"),
x509.load_pem_x509_certificate,
backend
)
assert cert.tbs_certificate == binascii.unhexlify(
b"3082051aa003020102020900a37352e0b2142f86300906072a8648ce3804033"
b"067310b3009060355040613025553310e300c06035504081305546578617331"
b"0f300d0603550407130641757374696e3121301f060355040a1318496e74657"
b"26e6574205769646769747320507479204c7464311430120603550403130b50"
b"79434120445341204341301e170d3134313132373035313431375a170d31343"
b"13232373035313431375a3067310b3009060355040613025553310e300c0603"
b"55040813055465786173310f300d0603550407130641757374696e3121301f0"
b"60355040a1318496e7465726e6574205769646769747320507479204c746431"
b"1430120603550403130b50794341204453412043413082033a3082022d06072"
b"a8648ce380401308202200282010100bfade6048e373cd4e48b677e878c8e5b"
b"08c02102ae04eb2cb5c46a523a3af1c73d16b24f34a4964781ae7e50500e217"
b"77754a670bd19a7420d633084e5556e33ca2c0e7d547ea5f46a07a01bf8669a"
b"e3bdec042d9b2ae5e6ecf49f00ba9dac99ab6eff140d2cedf722ee62c2f9736"
b"857971444c25d0a33d2017dc36d682a1054fe2a9428dda355a851ce6e6d61e0"
b"3e419fd4ca4e703313743d86caa885930f62ed5bf342d8165627681e9cc3244"
b"ba72aa22148400a6bbe80154e855d042c9dc2a3405f1e517be9dea50562f56d"
b"a93f6085f844a7e705c1f043e65751c583b80d29103e590ccb26efdaa0893d8"
b"33e36468f3907cfca788a3cb790f0341c8a31bf021500822ff5d234e073b901"
b"cf5941f58e1f538e71d40d028201004b7ced71dc353965ecc10d441a9a06fc2"
b"4943a32d66429dd5ef44d43e67d789d99770aec32c0415dc92970880872da45"
b"fef8dd1e115a3e4801387ba6d755861f062fd3b6e9ea8e2641152339b828315"
b"b1528ee6c7b79458d21f3db973f6fc303f9397174c2799dd2351282aa2d8842"
b"c357a73495bbaac4932786414c55e60d73169f5761036fba29e9eebfb049f8a"
b"3b1b7cee6f3fbfa136205f130bee2cf5b9c38dc1095d4006f2e73335c07352c"
b"64130a1ab2b89f13b48f628d3cc3868beece9bb7beade9f830eacc6fa241425"
b"c0b3fcc0df416a0c89f7bf35668d765ec95cdcfbe9caff49cfc156c668c76fa"
b"6247676a6d3ac945844a083509c6a1b436baca0382010500028201004c08bfe"
b"5f2d76649c80acf7d431f6ae2124b217abc8c9f6aca776ddfa9453b6656f13e"
b"543684cd5f6431a314377d2abfa068b7080cb8ddc065afc2dea559f0b584c97"
b"a2b235b9b69b46bc6de1aed422a6f341832618bcaae2198aba388099dafb05f"
b"f0b5efecb3b0ae169a62e1c72022af50ae68af3b033c18e6eec1f7df4692c45"
b"6ccafb79cc7e08da0a5786e9816ceda651d61b4bb7b81c2783da97cea62df67"
b"af5e85991fdc13aff10fc60e06586386b96bb78d65750f542f86951e05a6d81"
b"baadbcd35a2e5cad4119923ae6a2002091a3d17017f93c52970113cdc119970"
b"b9074ca506eac91c3dd376325df4af6b3911ef267d26623a5a1c5df4a6d13f1"
b"ca381cc3081c9301d0603551d0e04160414a4fb887a13fcdeb303bbae9a1dec"
b"a72f125a541b3081990603551d2304819130818e8014a4fb887a13fcdeb303b"
b"bae9a1deca72f125a541ba16ba4693067310b3009060355040613025553310e"
b"300c060355040813055465786173310f300d0603550407130641757374696e3"
b"121301f060355040a1318496e7465726e657420576964676974732050747920"
b"4c7464311430120603550403130b5079434120445341204341820900a37352e"
b"0b2142f86300c0603551d13040530030101ff"
)
verifier = cert.public_key().verifier(
cert.signature, cert.signature_hash_algorithm
)
verifier.update(cert.tbs_certificate)
verifier.verify()

@pytest.mark.parametrize(
("path", "loader_func"),
[
Expand Down Expand Up @@ -2734,6 +2864,60 @@ def test_load_ecdsa_cert(self, backend):
)
assert isinstance(num.curve, ec.SECP384R1)

def test_signature(self, backend):
cert = _load_cert(
os.path.join("x509", "ecdsa_root.pem"),
x509.load_pem_x509_certificate,
backend
)
assert cert.signature == binascii.unhexlify(
b"3065023100adbcf26c3f124ad12d39c30a099773f488368c8827bbe6888d5085"
b"a763f99e32de66930ff1ccb1098fdd6cabfa6b7fa0023039665bc2648db89e50"
b"dca8d549a2edc7dcd1497f1701b8c8868f4e8c882ba89aa98ac5d100bdf854e2"
b"9ae55b7cb32717"
)
r, s = decode_dss_signature(cert.signature)
assert r == int(
"adbcf26c3f124ad12d39c30a099773f488368c8827bbe6888d5085a763f99e32"
"de66930ff1ccb1098fdd6cabfa6b7fa0",
16
)
assert s == int(
"39665bc2648db89e50dca8d549a2edc7dcd1497f1701b8c8868f4e8c882ba89a"
"a98ac5d100bdf854e29ae55b7cb32717",
16
)

def test_tbs_certificate(self, backend):
_skip_curve_unsupported(backend, ec.SECP384R1())
cert = _load_cert(
os.path.join("x509", "ecdsa_root.pem"),
x509.load_pem_x509_certificate,
backend
)
assert cert.tbs_certificate == binascii.unhexlify(
b"308201c5a0030201020210055556bcf25ea43535c3a40fd5ab4572300a06082"
b"a8648ce3d0403033061310b300906035504061302555331153013060355040a"
b"130c446967694365727420496e6331193017060355040b13107777772e64696"
b"769636572742e636f6d3120301e06035504031317446967694365727420476c"
b"6f62616c20526f6f74204733301e170d3133303830313132303030305a170d3"
b"338303131353132303030305a3061310b300906035504061302555331153013"
b"060355040a130c446967694365727420496e6331193017060355040b1310777"
b"7772e64696769636572742e636f6d3120301e06035504031317446967694365"
b"727420476c6f62616c20526f6f742047333076301006072a8648ce3d0201060"
b"52b8104002203620004dda7d9bb8ab80bfb0b7f21d2f0bebe73f3335d1abc34"
b"eadec69bbcd095f6f0ccd00bba615b51467e9e2d9fee8e630c17ec0770f5cf8"
b"42e40839ce83f416d3badd3a4145936789d0343ee10136c72deae88a7a16bb5"
b"43ce67dc23ff031ca3e23ea3423040300f0603551d130101ff040530030101f"
b"f300e0603551d0f0101ff040403020186301d0603551d0e04160414b3db48a4"
b"f9a1c5d8ae3641cc1163696229bc4bc6"
)
verifier = cert.public_key().verifier(
cert.signature, ec.ECDSA(cert.signature_hash_algorithm)
)
verifier.update(cert.tbs_certificate)
verifier.verify()

def test_load_ecdsa_no_named_curve(self, backend):
_skip_curve_unsupported(backend, ec.SECP256R1())
cert = _load_cert(
Expand Down