From 9d345312d5ff22cd40d2359dc1765170badf42ea Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 26 Dec 2015 18:09:52 -0600 Subject: [PATCH] Support EC and DSA signing of CRLs in the OpenSSL backend --- .../hazmat/backends/openssl/backend.py | 21 +++--- tests/hazmat/backends/test_openssl.py | 37 +++++++++++ tests/test_x509_crlbuilder.py | 64 +++++++++++++++++-- 3 files changed, 107 insertions(+), 15 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 38fe077200bc..e8b0322e7017 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -1453,16 +1453,17 @@ def create_x509_crl(self, builder, private_key, algorithm): if not isinstance(algorithm, hashes.HashAlgorithm): raise TypeError('Algorithm must be a registered hash algorithm.') - if isinstance(private_key, _DSAPrivateKey): - raise NotImplementedError( - "CRL signatures aren't implemented for DSA" - " keys at this time." - ) - if isinstance(private_key, _EllipticCurvePrivateKey): - raise NotImplementedError( - "CRL signatures aren't implemented for EC" - " keys at this time." - ) + if self._lib.OPENSSL_VERSION_NUMBER <= 0x10001000: + if isinstance(private_key, _DSAPrivateKey): + raise NotImplementedError( + "CRL signatures aren't implemented for DSA" + " keys on OpenSSL versions less than 1.0.1." + ) + if isinstance(private_key, _EllipticCurvePrivateKey): + raise NotImplementedError( + "CRL signatures aren't implemented for EC" + " keys on OpenSSL versions less than 1.0.1." + ) evp_md = self._lib.EVP_get_digestbyname( algorithm.name.encode('ascii') diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py index c0e9d28f5a63..ad2daf7de880 100644 --- a/tests/hazmat/backends/test_openssl.py +++ b/tests/hazmat/backends/test_openssl.py @@ -558,6 +558,43 @@ def test_invalid_builder(self): with pytest.raises(TypeError): backend.create_x509_crl(object(), private_key, hashes.SHA256()) + @pytest.mark.skipif( + backend._lib.OPENSSL_VERSION_NUMBER >= 0x10001000, + reason="Requires an older OpenSSL. Must be < 1.0.1" + ) + def test_sign_with_dsa_private_key_is_unsupported(self): + private_key = DSA_KEY_2048.private_key(backend) + builder = x509.CertificateRevocationListBuilder() + builder = builder.issuer_name( + x509.Name([x509.NameAttribute(x509.NameOID.COUNTRY_NAME, u'US')]) + ).last_update( + datetime.datetime(2002, 1, 1, 12, 1) + ).next_update( + datetime.datetime(2032, 1, 1, 12, 1) + ) + + with pytest.raises(NotImplementedError): + builder.sign(private_key, hashes.SHA1(), backend) + + @pytest.mark.skipif( + backend._lib.OPENSSL_VERSION_NUMBER >= 0x10001000, + reason="Requires an older OpenSSL. Must be < 1.0.1" + ) + def test_sign_with_ec_private_key_is_unsupported(self): + _skip_curve_unsupported(backend, ec.SECP256R1()) + private_key = ec.generate_private_key(ec.SECP256R1(), backend) + builder = x509.CertificateRevocationListBuilder() + builder = builder.issuer_name( + x509.Name([x509.NameAttribute(x509.NameOID.COUNTRY_NAME, u'US')]) + ).last_update( + datetime.datetime(2002, 1, 1, 12, 1) + ).next_update( + datetime.datetime(2032, 1, 1, 12, 1) + ) + + with pytest.raises(NotImplementedError): + builder.sign(private_key, hashes.SHA512(), backend) + class TestOpenSSLCreateRevokedCertificate(object): def test_invalid_builder(self): diff --git a/tests/test_x509_crlbuilder.py b/tests/test_x509_crlbuilder.py index 763a647245ff..32a074876581 100644 --- a/tests/test_x509_crlbuilder.py +++ b/tests/test_x509_crlbuilder.py @@ -308,8 +308,23 @@ def test_sign_with_invalid_hash(self, backend): @pytest.mark.requires_backend_interface(interface=DSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) - def test_sign_dsa_key_unsupported(self, backend): + def test_sign_dsa_key(self, backend): + if backend._lib.OPENSSL_VERSION_NUMBER < 0x10001000: + pytest.skip("Requires a newer OpenSSL. Must be >= 1.0.1") private_key = DSA_KEY_2048.private_key(backend) + invalidity_date = x509.InvalidityDate( + datetime.datetime(2002, 1, 1, 0, 0) + ) + ian = x509.IssuerAlternativeName([ + x509.UniformResourceIdentifier(u"https://cryptography.io"), + ]) + revoked_cert0 = x509.RevokedCertificateBuilder().serial_number( + 2 + ).revocation_date( + datetime.datetime(2012, 1, 1, 1, 1) + ).add_extension( + invalidity_date, False + ).build(backend) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = x509.CertificateRevocationListBuilder().issuer_name( @@ -320,16 +335,43 @@ def test_sign_dsa_key_unsupported(self, backend): last_update ).next_update( next_update + ).add_revoked_certificate( + revoked_cert0 + ).add_extension( + ian, False ) - with pytest.raises(NotImplementedError): - builder.sign(private_key, hashes.SHA256(), backend) + crl = builder.sign(private_key, hashes.SHA256(), backend) + assert crl.extensions.get_extension_for_class( + x509.IssuerAlternativeName + ).value == ian + assert crl[0].serial_number == revoked_cert0.serial_number + assert crl[0].revocation_date == revoked_cert0.revocation_date + assert len(crl[0].extensions) == 1 + ext = crl[0].extensions.get_extension_for_class(x509.InvalidityDate) + assert ext.critical is False + assert ext.value == invalidity_date @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) @pytest.mark.requires_backend_interface(interface=X509Backend) def test_sign_ec_key_unsupported(self, backend): + if backend._lib.OPENSSL_VERSION_NUMBER < 0x10001000: + pytest.skip("Requires a newer OpenSSL. Must be >= 1.0.1") _skip_curve_unsupported(backend, ec.SECP256R1()) private_key = ec.generate_private_key(ec.SECP256R1(), backend) + invalidity_date = x509.InvalidityDate( + datetime.datetime(2002, 1, 1, 0, 0) + ) + ian = x509.IssuerAlternativeName([ + x509.UniformResourceIdentifier(u"https://cryptography.io"), + ]) + revoked_cert0 = x509.RevokedCertificateBuilder().serial_number( + 2 + ).revocation_date( + datetime.datetime(2012, 1, 1, 1, 1) + ).add_extension( + invalidity_date, False + ).build(backend) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = x509.CertificateRevocationListBuilder().issuer_name( @@ -340,10 +382,22 @@ def test_sign_ec_key_unsupported(self, backend): last_update ).next_update( next_update + ).add_revoked_certificate( + revoked_cert0 + ).add_extension( + ian, False ) - with pytest.raises(NotImplementedError): - builder.sign(private_key, hashes.SHA256(), backend) + crl = builder.sign(private_key, hashes.SHA256(), backend) + assert crl.extensions.get_extension_for_class( + x509.IssuerAlternativeName + ).value == ian + assert crl[0].serial_number == revoked_cert0.serial_number + assert crl[0].revocation_date == revoked_cert0.revocation_date + assert len(crl[0].extensions) == 1 + ext = crl[0].extensions.get_extension_for_class(x509.InvalidityDate) + assert ext.critical is False + assert ext.value == invalidity_date @pytest.mark.requires_backend_interface(interface=RSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend)