diff --git a/src/truststore/hazmat/__init__.py b/src/truststore/hazmat/__init__.py new file mode 100644 index 0000000..f9d433c --- /dev/null +++ b/src/truststore/hazmat/__init__.py @@ -0,0 +1,10 @@ +""" +This is the "Hazardous Materials" part of the truststore API. It +contains advanced cryptographic primitives that are often dangerous +and can be used incorrectly. They require an in-depth knowledge of the +cryptographic concepts at work. Using this part of the API without a +thorough understanding of the details involved will put your +application and its users at severe risk. +""" + +from ._api import verify_cert_chain diff --git a/src/truststore/hazmat/_api.py b/src/truststore/hazmat/_api.py new file mode 100644 index 0000000..1a53593 --- /dev/null +++ b/src/truststore/hazmat/_api.py @@ -0,0 +1,17 @@ +import platform +import ssl + +if platform.system() == "Windows": + from .._windows import _verify_peercerts_impl as _verify_cert_chain_impl +elif platform.system() == "Darwin": + from .._macos import _verify_peercerts_impl as _verify_cert_chain_impl +else: + from ._openssl import _verify_cert_chain_impl + + +def verify_cert_chain(cert_chain): + context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + + _verify_cert_chain_impl( + ssl_context=context, cert_chain=cert_chain, server_hostname=None + ) diff --git a/src/truststore/hazmat/_openssl.py b/src/truststore/hazmat/_openssl.py new file mode 100644 index 0000000..696ac69 --- /dev/null +++ b/src/truststore/hazmat/_openssl.py @@ -0,0 +1,28 @@ +from .._openssl import _configure_context + +try: + import OpenSSL.crypto + import OpenSSL.SSL +except ImportError: + raise Exception("Certificate chain verification on OpenSSL requires pyOpenSSL") + + +def _verify_intermediate_cert_and_add_to_store(store, cert): + store_ctx = OpenSSL.crypto.X509StoreContext(store, cert) + store_ctx.verify_certificate() + store.add_cert(cert) + + +def _verify_cert_chain_impl(ssl_context, cert_chain, server_hostname=None): + # We use the pyOpenSSL SSL context, not the stdlib ssl one. + # Discard the one that was passed in to avoid confusion. + ssl_context = None + + pyopenssl_context = OpenSSL.SSL.Context(OpenSSL.SSL.TLS_CLIENT_METHOD) + with _configure_context(pyopenssl_context): + store = pyopenssl_context.get_cert_store() + for cert in reversed(cert_chain): + pyopenssl_cert = OpenSSL.crypto.load_certificate( + OpenSSL.crypto.FILETYPE_ASN1, cert + ) + _verify_intermediate_cert_and_add_to_store(store, pyopenssl_cert)