diff options
author | Shane Harvey <shane.harvey@mongodb.com> | 2020-08-05 16:48:51 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-08-05 18:48:51 -0500 |
commit | 33c5499ce34f5e1c7c2630c6a1446353eee31755 (patch) | |
tree | 45f4871892c7b5e29e46fca2009e5cf77dc9eaa8 /src/OpenSSL/SSL.py | |
parent | bb971ae935059b73830ea2abe3f66391125b2bfb (diff) | |
download | pyopenssl-git-33c5499ce34f5e1c7c2630c6a1446353eee31755.tar.gz |
Allow accessing a connection's verfied certificate chain (#894)
* Allow accessing a connection's verfied certificate chain
Add X509StoreContext.get_verified_chain using X509_STORE_CTX_get1_chain.
Add Connection.get_verified_chain using SSL_get0_verified_chain if
available (ie OpenSSL 1.1+) and X509StoreContext.get_verified_chain
otherwise.
Fixes #740.
* TLSv1_METHOD -> SSLv23_METHOD
* Use X509_up_ref instead of X509_dup
* Add _openssl_assert where appropriate
* SSL_get_peer_cert_chain should not be null
* Reformat with black
* Fix <OpenSSL.crypto.X509 object at 0x7fdbb59e8050> != <OpenSSL.crypto.X509 object at 0x7fdbb59daad0>
* Add Changelog entry
* Remove _add_chain
Diffstat (limited to 'src/OpenSSL/SSL.py')
-rw-r--r-- | src/OpenSSL/SSL.py | 61 |
1 files changed, 54 insertions, 7 deletions
diff --git a/src/OpenSSL/SSL.py b/src/OpenSSL/SSL.py index 8a54994..29e489a 100644 --- a/src/OpenSSL/SSL.py +++ b/src/OpenSSL/SSL.py @@ -28,6 +28,7 @@ from OpenSSL.crypto import ( X509Name, X509, X509Store, + X509StoreContext, ) __all__ = [ @@ -2126,6 +2127,22 @@ class Connection(object): return X509._from_raw_x509_ptr(cert) return None + @staticmethod + def _cert_stack_to_list(cert_stack): + """ + Internal helper to convert a STACK_OF(X509) to a list of X509 + instances. + """ + result = [] + for i in range(_lib.sk_X509_num(cert_stack)): + cert = _lib.sk_X509_value(cert_stack, i) + _openssl_assert(cert != _ffi.NULL) + res = _lib.X509_up_ref(cert) + _openssl_assert(res >= 1) + pycert = X509._from_raw_x509_ptr(cert) + result.append(pycert) + return result + def get_peer_cert_chain(self): """ Retrieve the other side's certificate (if any) @@ -2137,13 +2154,43 @@ class Connection(object): if cert_stack == _ffi.NULL: return None - result = [] - for i in range(_lib.sk_X509_num(cert_stack)): - # TODO could incref instead of dup here - cert = _lib.X509_dup(_lib.sk_X509_value(cert_stack, i)) - pycert = X509._from_raw_x509_ptr(cert) - result.append(pycert) - return result + return self._cert_stack_to_list(cert_stack) + + def get_verified_chain(self): + """ + Retrieve the verified certificate chain of the peer including the + peer's end entity certificate. It must be called after a session has + been successfully established. If peer verification was not successful + the chain may be incomplete, invalid, or None. + + :return: A list of X509 instances giving the peer's verified + certificate chain, or None if it does not have one. + + .. versionadded:: 20.0 + """ + if hasattr(_lib, "SSL_get0_verified_chain"): + # OpenSSL 1.1+ + cert_stack = _lib.SSL_get0_verified_chain(self._ssl) + if cert_stack == _ffi.NULL: + return None + + return self._cert_stack_to_list(cert_stack) + + pycert = self.get_peer_certificate() + if pycert is None: + return None + + # Should never be NULL because the peer presented a certificate. + cert_stack = _lib.SSL_get_peer_cert_chain(self._ssl) + _openssl_assert(cert_stack != _ffi.NULL) + + pystore = self._context.get_cert_store() + if pystore is None: + return None + + pystorectx = X509StoreContext(pystore, pycert) + pystorectx._chain = cert_stack + return pystorectx.get_verified_chain() def want_read(self): """ |