diff options
author | Paul Kehrer <paul.l.kehrer@gmail.com> | 2018-08-30 10:41:32 -0400 |
---|---|---|
committer | Alex Gaynor <alex.gaynor@gmail.com> | 2018-08-30 10:41:32 -0400 |
commit | 002fa75d6c57420ed1805e088e1d6ecbce880938 (patch) | |
tree | da85fb85fd2e9ba73b180611270948c47d5696c4 | |
parent | 2284eea98847bd42d3ddf7dead504baf3d544d98 (diff) | |
download | cryptography-002fa75d6c57420ed1805e088e1d6ecbce880938.tar.gz cryptography-002fa75d6c57420ed1805e088e1d6ecbce880938.tar.bz2 cryptography-002fa75d6c57420ed1805e088e1d6ecbce880938.zip |
make an ocsp request (#4402)
* make an ocsp request
* update test, add docs
* make it an OCSPRequestBuilder
* review feedback and more tests
* make it a class
* empty commit to retrigger
* type check
-rw-r--r-- | docs/x509/ocsp.rst | 113 | ||||
-rw-r--r-- | src/_cffi_src/openssl/ocsp.py | 2 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 16 | ||||
-rw-r--r-- | src/cryptography/x509/ocsp.py | 30 | ||||
-rw-r--r-- | tests/x509/test_ocsp.py | 64 |
5 files changed, 225 insertions, 0 deletions
diff --git a/docs/x509/ocsp.rst b/docs/x509/ocsp.rst index 72227f07..afbb2ef7 100644 --- a/docs/x509/ocsp.rst +++ b/docs/x509/ocsp.rst @@ -5,6 +5,69 @@ OCSP .. testsetup:: + import base64 + pem_cert = b""" + -----BEGIN CERTIFICATE----- + MIIFvTCCBKWgAwIBAgICPyAwDQYJKoZIhvcNAQELBQAwRzELMAkGA1UEBhMCVVMx + FjAUBgNVBAoTDUdlb1RydXN0IEluYy4xIDAeBgNVBAMTF1JhcGlkU1NMIFNIQTI1 + NiBDQSAtIEczMB4XDTE0MTAxNTEyMDkzMloXDTE4MTExNjAxMTUwM1owgZcxEzAR + BgNVBAsTCkdUNDg3NDI5NjUxMTAvBgNVBAsTKFNlZSB3d3cucmFwaWRzc2wuY29t + L3Jlc291cmNlcy9jcHMgKGMpMTQxLzAtBgNVBAsTJkRvbWFpbiBDb250cm9sIFZh + bGlkYXRlZCAtIFJhcGlkU1NMKFIpMRwwGgYDVQQDExN3d3cuY3J5cHRvZ3JhcGh5 + LmlvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAom/FebKJIot7Sp3s + itG1sicpe3thCssjI+g1JDAS7I3GLVNmbms1DOdIIqwf01gZkzzXBN2+9sOnyRaR + PPfCe1jTr3dk2y6rPE559vPa1nZQkhlzlhMhlPyjaT+S7g4Tio4qV2sCBZU01DZJ + CaksfohN+5BNVWoJzTbOcrHOEJ+M8B484KlBCiSxqf9cyNQKru4W3bHaCVNVJ8eu + 6i6KyhzLa0L7yK3LXwwXVs583C0/vwFhccGWsFODqD/9xHUzsBIshE8HKjdjDi7Y + 3BFQzVUQFjBB50NSZfAA/jcdt1blxJouc7z9T8Oklh+V5DDBowgAsrT4b6Z2Fq6/ + r7D1GqivLK/ypUQmxq2WXWAUBb/Q6xHgxASxI4Br+CByIUQJsm8L2jzc7k+mF4hW + ltAIUkbo8fGiVnat0505YJgxWEDKOLc4Gda6d/7GVd5AvKrz242bUqeaWo6e4MTx + diku2Ma3rhdcr044Qvfh9hGyjqNjvhWY/I+VRWgihU7JrYvgwFdJqsQ5eiKT4OHi + gsejvWwkZzDtiQ+aQTrzM1FsY2swJBJsLSX4ofohlVRlIJCn/ME+XErj553431Lu + YQ5SzMd3nXzN78Vj6qzTfMUUY72UoT1/AcFiUMobgIqrrmwuNxfrkbVE2b6Bga74 + FsJX63prvrJ41kuHK/16RQBM7fcCAwEAAaOCAWAwggFcMB8GA1UdIwQYMBaAFMOc + 8/zTRgg0u85Gf6B8W/PiCMtZMFcGCCsGAQUFBwEBBEswSTAfBggrBgEFBQcwAYYT + aHR0cDovL2d2LnN5bWNkLmNvbTAmBggrBgEFBQcwAoYaaHR0cDovL2d2LnN5bWNi + LmNvbS9ndi5jcnQwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMB + BggrBgEFBQcDAjAvBgNVHREEKDAmghN3d3cuY3J5cHRvZ3JhcGh5Lmlvgg9jcnlw + dG9ncmFwaHkuaW8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL2d2LnN5bWNiLmNv + bS9ndi5jcmwwDAYDVR0TAQH/BAIwADBFBgNVHSAEPjA8MDoGCmCGSAGG+EUBBzYw + LDAqBggrBgEFBQcCARYeaHR0cHM6Ly93d3cucmFwaWRzc2wuY29tL2xlZ2FsMA0G + CSqGSIb3DQEBCwUAA4IBAQAzIYO2jx7h17FBT74tJ2zbV9OKqGb7QF8y3wUtP4xc + dH80vprI/Cfji8s86kr77aAvAqjDjaVjHn7UzebhSUivvRPmfzRgyWBacomnXTSt + Xlt2dp2nDQuwGyK2vB7dMfKnQAkxwq1sYUXznB8i0IhhCAoXp01QGPKq51YoIlnF + 7DRMk6iEaL1SJbkIrLsCQyZFDf0xtfW9DqXugMMLoxeCsBhZJQzNyS2ryirrv9LH + aK3+6IZjrcyy9bkpz/gzJucyhU+75c4My/mnRCrtItRbCQuiI5pd5poDowm+HH9i + GVI9+0lAFwxOUnOnwsoI40iOoxjLMGB+CgFLKCGUcWxP + -----END CERTIFICATE----- + """ + pem_issuer = b""" + -----BEGIN CERTIFICATE----- + MIIEJTCCAw2gAwIBAgIDAjp3MA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT + MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i + YWwgQ0EwHhcNMTQwODI5MjEzOTMyWhcNMjIwNTIwMjEzOTMyWjBHMQswCQYDVQQG + EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXUmFwaWRTU0wg + U0hBMjU2IENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCv + VJvZWF0eLFbG1eh/9H0WA//Qi1rkjqfdVC7UBMBdmJyNkA+8EGVf2prWRHzAn7Xp + SowLBkMEu/SW4ib2YQGRZjEiwzQ0Xz8/kS9EX9zHFLYDn4ZLDqP/oIACg8PTH2lS + 1p1kD8mD5xvEcKyU58Okaiy9uJ5p2L4KjxZjWmhxgHsw3hUEv8zTvz5IBVV6s9cQ + DAP8m/0Ip4yM26eO8R5j3LMBL3+vV8M8SKeDaCGnL+enP/C1DPz1hNFTvA5yT2AM + QriYrRmIV9cE7Ie/fodOoyH5U/02mEiN1vi7SPIpyGTRzFRIU4uvt2UevykzKdkp + YEj4/5G8V1jlNS67abZZAgMBAAGjggEdMIIBGTAfBgNVHSMEGDAWgBTAephojYn7 + qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUw5zz/NNGCDS7zkZ/oHxb8+IIy1kwEgYD + VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwNQYDVR0fBC4wLDAqoCig + JoYkaHR0cDovL2cuc3ltY2IuY29tL2NybHMvZ3RnbG9iYWwuY3JsMC4GCCsGAQUF + BwEBBCIwIDAeBggrBgEFBQcwAYYSaHR0cDovL2cuc3ltY2QuY29tMEwGA1UdIARF + MEMwQQYKYIZIAYb4RQEHNjAzMDEGCCsGAQUFBwIBFiVodHRwOi8vd3d3Lmdlb3Ry + dXN0LmNvbS9yZXNvdXJjZXMvY3BzMA0GCSqGSIb3DQEBCwUAA4IBAQCjWB7GQzKs + rC+TeLfqrlRARy1+eI1Q9vhmrNZPc9ZE768LzFvB9E+aj0l+YK/CJ8cW8fuTgZCp + fO9vfm5FlBaEvexJ8cQO9K8EWYOHDyw7l8NaEpt7BDV7o5UzCHuTcSJCs6nZb0+B + kvwHtnm8hEqddwnxxYny8LScVKoSew26T++TGezvfU5ho452nFnPjJSxhJf3GrkH + uLLGTxN5279PURt/aQ1RKsHWFf83UTRlUfQevjhq7A6rvz17OQV79PP7GqHQyH5O + ZI3NjGFVkP46yl0lD/gdo0p0Vk8aVUBwdSWmMy66S6VdU5oNMOGNX2Esr8zvsJmh + gP8L8mJMcCaY + -----END CERTIFICATE----- + """ der_ocsp_req = ( b"0V0T0R0P0N0\t\x06\x05+\x0e\x03\x02\x1a\x05\x00\x04\x148\xcaF\x8c" b"\x07D\x8d\xf4\x81\x96\xc7mmLpQ\x9e`\xa7\xbd\x04\x14yu\xbb\x84:\xcb" @@ -39,6 +102,56 @@ Loading Requests 872625873161273451176241581705670534707360122361 +Creating Requests +~~~~~~~~~~~~~~~~~ + +.. class:: OCSPRequestBuilder + + .. versionadded:: 2.4 + + This class is used to create :class:`~cryptography.x509.ocsp.OCSPRequest` + objects. + + + .. method:: add_request(cert, issuer, algorithm) + + Adds a request using a certificate, issuer certificate, and hash + algorithm. + + :param cert: The :class:`~cryptography.x509.Certificate` whose validity + is being checked. + + :param issuer: The issuer :class:`~cryptography.x509.Certificate` of + the certificate that is being checked. + + :param algorithm: A + :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` + instance. For OCSP only + :class:`~cryptography.hazmat.primitives.hashes.SHA1`, + :class:`~cryptography.hazmat.primitives.hashes.SHA224`, + :class:`~cryptography.hazmat.primitives.hashes.SHA256`, + :class:`~cryptography.hazmat.primitives.hashes.SHA384`, and + :class:`~cryptography.hazmat.primitives.hashes.SHA512` are allowed. + + .. method:: build() + + :returns: A new :class:`~cryptography.x509.ocsp.OCSPRequest`. + + .. doctest:: + + >>> from cryptography.hazmat.backends import default_backend + >>> from cryptography.hazmat.primitives import serialization + >>> from cryptography.hazmat.primitives.hashes import SHA256 + >>> from cryptography.x509 import load_pem_x509_certificate, ocsp + >>> cert = load_pem_x509_certificate(pem_cert, default_backend()) + >>> issuer = load_pem_x509_certificate(pem_issuer, default_backend()) + >>> builder = ocsp.OCSPRequestBuilder() + >>> builder = builder.add_request(cert, issuer, SHA256()) + >>> req = builder.build() + >>> base64.b64encode(req.public_bytes(serialization.Encoding.DER)) + b'MF8wXTBbMFkwVzANBglghkgBZQMEAgEFAAQgn3BowBaoh77h17ULfkX6781dUDPD82Taj8wO1jZWhZoEINxPgjoQth3w7q4AouKKerMxIMIuUG4EuWU2pZfwih52AgI/IA==' + + Interfaces ~~~~~~~~~~ diff --git a/src/_cffi_src/openssl/ocsp.py b/src/_cffi_src/openssl/ocsp.py index 1701f41c..61546027 100644 --- a/src/_cffi_src/openssl/ocsp.py +++ b/src/_cffi_src/openssl/ocsp.py @@ -35,6 +35,8 @@ OCSP_ONEREQ *OCSP_request_onereq_get0(OCSP_REQUEST *, int); int OCSP_ONEREQ_get_ext_count(OCSP_ONEREQ *); X509_EXTENSION *OCSP_ONEREQ_get_ext(OCSP_ONEREQ *, int); OCSP_CERTID *OCSP_onereq_get0_id(OCSP_ONEREQ *); +OCSP_ONEREQ *OCSP_request_add0_id(OCSP_REQUEST *, OCSP_CERTID *); +OCSP_CERTID *OCSP_cert_to_id(const EVP_MD *, const X509 *, const X509 *); OCSP_BASICRESP *OCSP_BASICRESP_new(void); diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 6a0446bc..bdf8f370 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -1430,6 +1430,22 @@ class Backend(object): request = self._ffi.gc(request, self._lib.OCSP_REQUEST_free) return _OCSPRequest(self, request) + def create_ocsp_request(self, builder): + ocsp_req = self._lib.OCSP_REQUEST_new() + self.openssl_assert(ocsp_req != self._ffi.NULL) + ocsp_req = self._ffi.gc(ocsp_req, self._lib.OCSP_REQUEST_free) + for cert, issuer, algorithm in builder._requests: + evp_md = self._lib.EVP_get_digestbyname( + algorithm.name.encode("ascii")) + self.openssl_assert(evp_md != self._ffi.NULL) + certid = self._lib.OCSP_cert_to_id( + evp_md, cert._x509, issuer._x509 + ) + self.openssl_assert(certid != self._ffi.NULL) + onereq = self._lib.OCSP_request_add0_id(ocsp_req, certid) + self.openssl_assert(onereq != self._ffi.NULL) + return _OCSPRequest(self, ocsp_req) + def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve): return ( self.elliptic_curve_supported(curve) and diff --git a/src/cryptography/x509/ocsp.py b/src/cryptography/x509/ocsp.py index 22894dde..0567197d 100644 --- a/src/cryptography/x509/ocsp.py +++ b/src/cryptography/x509/ocsp.py @@ -9,6 +9,7 @@ import abc import six from cryptography.hazmat.primitives import hashes +from cryptography.x509 import Certificate _OIDS_TO_HASH = { @@ -25,6 +26,35 @@ def load_der_ocsp_request(data): return backend.load_der_ocsp_request(data) +class OCSPRequestBuilder(object): + def __init__(self, requests=[]): + self._requests = requests + + def add_request(self, cert, issuer, algorithm): + allowed_hashes = ( + hashes.SHA1, hashes.SHA224, hashes.SHA256, + hashes.SHA384, hashes.SHA512 + ) + if not isinstance(algorithm, allowed_hashes): + raise ValueError( + "Algorithm must be SHA1, SHA224, SHA256, SHA384, or SHA512" + ) + if ( + not isinstance(cert, Certificate) or + not isinstance(issuer, Certificate) + ): + raise TypeError("cert and issuer must be a Certificate") + + return OCSPRequestBuilder(self._requests + [(cert, issuer, algorithm)]) + + def build(self): + from cryptography.hazmat.backends.openssl.backend import backend + if len(self._requests) == 0: + raise ValueError("You must add a request before building") + + return backend.create_ocsp_request(self) + + @six.add_metaclass(abc.ABCMeta) class OCSPRequest(object): @abc.abstractmethod diff --git a/tests/x509/test_ocsp.py b/tests/x509/test_ocsp.py index 22f34df2..709ef6f4 100644 --- a/tests/x509/test_ocsp.py +++ b/tests/x509/test_ocsp.py @@ -4,14 +4,17 @@ from __future__ import absolute_import, division, print_function +import base64 import os import pytest +from cryptography import x509 from cryptography.exceptions import UnsupportedAlgorithm from cryptography.hazmat.primitives import hashes, serialization from cryptography.x509 import ocsp +from .test_x509 import _load_cert from ..utils import load_vectors_from_file @@ -23,6 +26,21 @@ def _load_data(filename, loader): ) +def _cert_and_issuer(): + from cryptography.hazmat.backends.openssl.backend import backend + cert = _load_cert( + os.path.join("x509", "cryptography.io.pem"), + x509.load_pem_x509_certificate, + backend + ) + issuer = _load_cert( + os.path.join("x509", "rapidssl_sha256_ca_g3.pem"), + x509.load_pem_x509_certificate, + backend + ) + return cert, issuer + + class TestOCSPRequest(object): def test_bad_request(self): with pytest.raises(ValueError): @@ -113,3 +131,49 @@ class TestOCSPRequest(object): req.public_bytes("invalid") with pytest.raises(ValueError): req.public_bytes(serialization.Encoding.PEM) + + +class TestOCSPRequestBuilder(object): + def test_create_ocsp_request_no_req(self): + builder = ocsp.OCSPRequestBuilder() + with pytest.raises(ValueError): + builder.build() + + def test_create_ocsp_request_invalid_alg(self): + cert, issuer = _cert_and_issuer() + builder = ocsp.OCSPRequestBuilder() + with pytest.raises(ValueError): + builder.add_request(cert, issuer, hashes.MD5()) + + def test_create_ocsp_request_invalid_cert(self): + cert, issuer = _cert_and_issuer() + builder = ocsp.OCSPRequestBuilder() + with pytest.raises(TypeError): + builder.add_request(b"notacert", issuer, hashes.SHA1()) + + with pytest.raises(TypeError): + builder.add_request(cert, b"notacert", hashes.SHA1()) + + def test_create_ocsp_request(self): + cert, issuer = _cert_and_issuer() + builder = ocsp.OCSPRequestBuilder() + builder = builder.add_request(cert, issuer, hashes.SHA1()) + req = builder.build() + serialized = req.public_bytes(serialization.Encoding.DER) + assert serialized == base64.b64decode( + b"MEMwQTA/MD0wOzAJBgUrDgMCGgUABBRAC0Z68eay0wmDug1gfn5ZN0gkxAQUw5zz" + b"/NNGCDS7zkZ/oHxb8+IIy1kCAj8g" + ) + + def test_create_ocsp_request_two_reqs(self): + builder = ocsp.OCSPRequestBuilder() + cert, issuer = _cert_and_issuer() + builder = builder.add_request(cert, issuer, hashes.SHA1()) + builder = builder.add_request(cert, issuer, hashes.SHA1()) + req = builder.build() + serialized = req.public_bytes(serialization.Encoding.DER) + assert serialized == base64.b64decode( + b"MIGDMIGAMH4wPTA7MAkGBSsOAwIaBQAEFEALRnrx5rLTCYO6DWB+flk3SCTEBBTD" + b"nPP800YINLvORn+gfFvz4gjLWQICPyAwPTA7MAkGBSsOAwIaBQAEFEALRnrx5rLT" + b"CYO6DWB+flk3SCTEBBTDnPP800YINLvORn+gfFvz4gjLWQICPyA=" + ) |