diff --git a/CHANGELOG.rst b/CHANGELOG.rst index fdea8c3521775..eee519dc429d9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,6 +7,8 @@ Changelog .. note:: This version is not yet released and is under active development. * Added :class:`~cryptography.hazmat.primitives.kdf.x963kdf.X963KDF`. +* The :class:`~cryptography.x509.Certificate` class now has a + :attr:`~cryptography.x509.Certificate.signature` attribute. 1.0.2 - 2015-09-27 ~~~~~~~~~~~~~~~~~~ diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst index 62bdb3a97cc19..0e3eebd0ca62d 100644 --- a/docs/x509/reference.rst +++ b/docs/x509/reference.rst @@ -322,6 +322,14 @@ X.509 Certificate Object , critical=False, value=, policy_qualifiers=None)>])>)> , critical=True, value=)> + .. attribute:: signature + + .. versionadded:: 1.1 + + :type: bytes + + The bytes of the certificate's signature. + .. method:: public_bytes(encoding) .. versionadded:: 1.0 diff --git a/src/_cffi_src/openssl/asn1.py b/src/_cffi_src/openssl/asn1.py index bbbffd8f633d2..e54fbe9343d3e 100644 --- a/src/_cffi_src/openssl/asn1.py +++ b/src/_cffi_src/openssl/asn1.py @@ -40,7 +40,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; diff --git a/src/_cffi_src/openssl/x509.py b/src/_cffi_src/openssl/x509.py index 468d74eaae5f2..63c4e952f1ed5 100644 --- a/src/_cffi_src/openssl/x509.py +++ b/src/_cffi_src/openssl/x509.py @@ -71,6 +71,7 @@ typedef struct { X509_ALGOR *sig_alg; X509_CINF *cert_info; + ASN1_BIT_STRING *signature; ...; } X509; diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 80f32e292eb19..1c2cf7cd3cddc 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -301,6 +301,10 @@ 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) + def public_bytes(self, encoding): bio = self._backend._create_mem_bio() if encoding is serialization.Encoding.PEM: diff --git a/src/cryptography/x509/base.py b/src/cryptography/x509/base.py index 27eafac6a1aa2..91efc1d39c763 100644 --- a/src/cryptography/x509/base.py +++ b/src/cryptography/x509/base.py @@ -109,6 +109,12 @@ def extensions(self): Returns an Extensions object. """ + @abc.abstractproperty + def signature(self): + """ + Returns the signature bytes. + """ + @abc.abstractmethod def __eq__(self, other): """ diff --git a/tests/test_x509.py b/tests/test_x509.py index 220e71a51857a..0e5b9667fc285 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -20,6 +20,9 @@ ) from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa +from cryptography.hazmat.primitives.asymmetric.utils import ( + decode_dss_signature +) from cryptography.x509.oid import ( AuthorityInformationAccessOID, ExtendedKeyUsageOID, ExtensionOID, NameOID ) @@ -79,6 +82,25 @@ 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_issuer(self, backend): cert = _load_cert( os.path.join( @@ -2278,6 +2300,20 @@ 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 + @pytest.mark.parametrize( ("path", "loader_func"), [ @@ -2331,6 +2367,30 @@ 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_load_ecdsa_no_named_curve(self, backend): _skip_curve_unsupported(backend, ec.SECP256R1()) cert = _load_cert(