Skip to content

Commit db225c8

Browse files
committed
Expose set_purpose on X509Store to allow verify_certificate with purpose
Signed-off-by: Arne Schwabe <[email protected]>
1 parent ca63d31 commit db225c8

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

Diff for: src/OpenSSL/crypto.py

+39
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"X509Req",
3434
"X509",
3535
"X509StoreFlags",
36+
"X509StorePurposes",
3637
"X509Store",
3738
"X509StoreContextError",
3839
"X509StoreContext",
@@ -1583,6 +1584,28 @@ class X509StoreFlags:
15831584
CHECK_SS_SIGNATURE = _lib.X509_V_FLAG_CHECK_SS_SIGNATURE
15841585

15851586

1587+
class X509StorePurposes:
1588+
"""
1589+
Flags for X509 verification, used to change the behavior of
1590+
:class:`X509Store`.
1591+
1592+
See `OpenSSL check purpose`_ for details.
1593+
1594+
.. _OpenSSL check purpose:
1595+
https://www.openssl.org/docs/manmaster/man3/X509_check_purpose.html
1596+
"""
1597+
1598+
X509_PURPOSE_SSL_CLIENT = _lib.X509_PURPOSE_SSL_CLIENT
1599+
X509_PURPOSE_SSL_SERVER = _lib.X509_PURPOSE_SSL_SERVER
1600+
X509_PURPOSE_NS_SSL_SERVER = _lib.X509_PURPOSE_NS_SSL_SERVER
1601+
X509_PURPOSE_SMIME_SIGN = _lib.X509_PURPOSE_SMIME_SIGN
1602+
X509_PURPOSE_SMIME_ENCRYPT = _lib.X509_PURPOSE_SMIME_ENCRYPT
1603+
X509_PURPOSE_CRL_SIGN = _lib.X509_PURPOSE_CRL_SIGN
1604+
X509_PURPOSE_ANY = _lib.X509_PURPOSE_ANY
1605+
X509_PURPOSE_OCSP_HELPER = _lib.X509_PURPOSE_OCSP_HELPER
1606+
X509_PURPOSE_TIMESTAMP_SIGN = _lib.X509_PURPOSE_TIMESTAMP_SIGN
1607+
1608+
15861609
class X509Store:
15871610
"""
15881611
An X.509 store.
@@ -1687,6 +1710,22 @@ def set_time(self, vfy_time):
16871710
)
16881711
_openssl_assert(_lib.X509_STORE_set1_param(self._store, param) != 0)
16891712

1713+
def set_purpose(self, purpose):
1714+
"""
1715+
Set purpose of this store.
1716+
1717+
.. versionadded:: 22.1.0
1718+
1719+
:param int flags: The verification flags to set on this store.
1720+
See :class:`X509StorePurposes` for available constants.
1721+
:return: ``None`` if the verification flags were successfully set.
1722+
"""
1723+
1724+
param = _lib.X509_VERIFY_PARAM_new()
1725+
param = _ffi.gc(param, _lib.X509_VERIFY_PARAM_free)
1726+
_lib.X509_VERIFY_PARAM_set_purpose(param, purpose)
1727+
_openssl_assert(_lib.X509_STORE_set1_param(self._store, param) != 0)
1728+
16901729
def load_locations(self, cafile, capath=None):
16911730
"""
16921731
Let X509Store know where we can find trusted certificates for the

Diff for: tests/test_crypto.py

+28
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from OpenSSL.crypto import (
2727
X509Store,
2828
X509StoreFlags,
29+
X509StorePurposes,
2930
X509StoreContext,
3031
X509StoreContextError,
3132
)
@@ -3528,6 +3529,7 @@ class TestCRL:
35283529
intermediate_server_key = load_privatekey(
35293530
FILETYPE_PEM, intermediate_server_key_pem
35303531
)
3532+
server_cert = load_certificate(FILETYPE_PEM, server_cert_pem)
35313533

35323534
def test_construction(self):
35333535
"""
@@ -3835,6 +3837,32 @@ def test_verify_with_revoked(self):
38353837
store_ctx.verify_certificate()
38363838
assert err.value.args[0][2] == "certificate revoked"
38373839

3840+
def test_verify_with_correct_purpose(self):
3841+
store = X509Store()
3842+
store.add_cert(self.root_cert)
3843+
store.add_cert(self.intermediate_cert)
3844+
store.set_purpose(X509StorePurposes.X509_PURPOSE_SSL_SERVER)
3845+
3846+
store_ctx = X509StoreContext(store, self.server_cert)
3847+
store_ctx.verify_certificate()
3848+
3849+
# The intermediate server certificate has no EKU and so it is fit
3850+
# for any purpose
3851+
store_ctx = X509StoreContext(store, self.intermediate_server_cert)
3852+
store_ctx.verify_certificate()
3853+
3854+
def test_verify_with_incorrect_purpose(self):
3855+
store = X509Store()
3856+
store.add_cert(self.root_cert)
3857+
store.add_cert(self.intermediate_cert)
3858+
store.set_purpose(X509StorePurposes.X509_PURPOSE_SSL_CLIENT)
3859+
3860+
store_ctx = X509StoreContext(store, self.server_cert)
3861+
with pytest.raises(X509StoreContextError) as err:
3862+
result = store_ctx.verify_certificate()
3863+
3864+
assert err.value.args[0][2] == "unsupported certificate purpose"
3865+
38383866
def test_verify_with_missing_crl(self):
38393867
"""
38403868
`verify_certificate` raises error when an intermediate certificate's

0 commit comments

Comments
 (0)