From 83ef2306a1481e0cf7f53899c390497256711e29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ndor=20Oroszi?= Date: Mon, 12 Oct 2020 15:42:23 +0200 Subject: Allow using additional untrusted certificates for chain building in X509StoreContext (#948) The additional certificates provided in the new `chain` parameter will be untrusted but may be used to build the chain. This makes it easier to validate a certificate against a store which contains only root ca certificates, and the intermediates come from e.g. the same untrusted source as the certificate to be verified. Co-authored-by: Sandor Oroszi --- src/OpenSSL/crypto.py | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) (limited to 'src/OpenSSL/crypto.py') diff --git a/src/OpenSSL/crypto.py b/src/OpenSSL/crypto.py index 880c2b3..f89a28f 100644 --- a/src/OpenSSL/crypto.py +++ b/src/OpenSSL/crypto.py @@ -1750,22 +1750,54 @@ class X509StoreContext(object): collected. :ivar _store: See the ``store`` ``__init__`` parameter. :ivar _cert: See the ``certificate`` ``__init__`` parameter. + :ivar _chain: See the ``chain`` ``__init__`` parameter. :param X509Store store: The certificates which will be trusted for the purposes of any verifications. :param X509 certificate: The certificate to be verified. + :param chain: List of untrusted certificates that may be used for building + the certificate chain. May be ``None``. + :type chain: :class:`list` of :class:`X509` """ - def __init__(self, store, certificate): + def __init__(self, store, certificate, chain=None): store_ctx = _lib.X509_STORE_CTX_new() self._store_ctx = _ffi.gc(store_ctx, _lib.X509_STORE_CTX_free) self._store = store self._cert = certificate - self._chain = _ffi.NULL + self._chain = self._build_certificate_stack(chain) # Make the store context available for use after instantiating this # class by initializing it now. Per testing, subsequent calls to # :meth:`_init` have no adverse affect. self._init() + @staticmethod + def _build_certificate_stack(certificates): + def cleanup(s): + # Equivalent to sk_X509_pop_free, but we don't + # currently have a CFFI binding for that available + for i in range(_lib.sk_X509_num(s)): + x = _lib.sk_X509_value(s, i) + _lib.X509_free(x) + _lib.sk_X509_free(s) + + if certificates is None or len(certificates) == 0: + return _ffi.NULL + + stack = _lib.sk_X509_new_null() + _openssl_assert(stack != _ffi.NULL) + stack = _ffi.gc(stack, cleanup) + + for cert in certificates: + if not isinstance(cert, X509): + raise TypeError("One of the elements is not an X509 instance") + + _openssl_assert(_lib.X509_up_ref(cert._x509) > 0) + if _lib.sk_X509_push(stack, cert._x509) <= 0: + _lib.X509_free(cert._x509) + _raise_current_error() + + return stack + def _init(self): """ Set up the store context for a subsequent verification operation. -- cgit v1.2.1