summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Holsapple <sholsapp@gmail.com>2015-01-30 17:18:40 -0800
committerStephen Holsapple <sholsapp@gmail.com>2015-01-30 18:51:11 -0800
commit08ffaa641b8ac19ddaae6472688f5a65844c8473 (patch)
tree4c74edeabba8d4edfb730a90f1cab7d3c66716c7
parentbf1f81c666c2cde383a793de209b7b864fa58dd7 (diff)
downloadpyopenssl-08ffaa641b8ac19ddaae6472688f5a65844c8473.tar.gz
Refactoring verify_cert
Apply the changes that we've been talking about in https://github.com/pyca/pyopenssl/pull/155 regarding the placement of verify_cert, viz., moving verify_cert from top level of crypto into X509StoreContext. This makes the pyOpenSSL API slightly different than the OpenSSL API, but the plan will be to add back a verify_cert to the top level that is nice to use.
-rw-r--r--OpenSSL/crypto.py91
-rw-r--r--OpenSSL/test/test_crypto.py34
-rw-r--r--doc/api/crypto.rst28
3 files changed, 82 insertions, 71 deletions
diff --git a/OpenSSL/crypto.py b/OpenSSL/crypto.py
index b6951c0..395f273 100644
--- a/OpenSSL/crypto.py
+++ b/OpenSSL/crypto.py
@@ -32,35 +32,7 @@ class Error(Exception):
"""
-
-def _exception_from_context_error(exception_type, store_ctx):
- """
- Convert a :py:func:`OpenSSL.crypto.verify_cert` failure into a Python
- exception.
-
- When a call to native OpenSSL X509_verify_cert fails, additonal information
- about the failure can be obtained from the store context.
- """
-
- errors = [
- _lib.X509_STORE_CTX_get_error(store_ctx._store_ctx),
- _lib.X509_STORE_CTX_get_error_depth(store_ctx._store_ctx),
- _native(_ffi.string(_lib.X509_verify_cert_error_string(
- _lib.X509_STORE_CTX_get_error(store_ctx._store_ctx)))),
- ]
- _x509 = _lib.X509_STORE_CTX_get_current_cert(store_ctx._store_ctx)
- if _x509 != _ffi.NULL:
- _cert = _lib.X509_dup(_x509)
- pycert = X509.__new__(X509)
- pycert._x509 = _ffi.gc(_cert, _lib.X509_free)
- e = exception_type(errors)
- e.certificate = pycert
- raise e
-
-
-
_raise_current_error = partial(_exception_from_error_queue, Error)
-_raise_context_error = partial(_exception_from_context_error, Error)
@@ -1387,6 +1359,19 @@ class X509Store(object):
X509StoreType = X509Store
+class X509StoreContextError(Exception):
+ """
+ An error occurred while verifying a certificate using
+ `OpenSSL.X509StoreContext.verify_certificate`.
+
+ :param certificate: The certificate which caused verificate failure.
+ :type cert: :class:`X509`
+
+ """
+ def __init__(self, message, certificate):
+ super(X509StoreContextError, self).__init__(message)
+ self.certificate = certificate
+
class X509StoreContext(object):
"""
@@ -1440,6 +1425,42 @@ class X509StoreContext(object):
_lib.X509_STORE_CTX_cleanup(self._store_ctx)
+ def _exception_from_context(self):
+ """
+ Convert an OpenSSL native context error failure into a Python
+ exception.
+
+ When a call to native OpenSSL X509_verify_cert fails, additonal information
+ about the failure can be obtained from the store context.
+ """
+ errors = [
+ _lib.X509_STORE_CTX_get_error(self._store_ctx),
+ _lib.X509_STORE_CTX_get_error_depth(self._store_ctx),
+ _native(_ffi.string(_lib.X509_verify_cert_error_string(
+ _lib.X509_STORE_CTX_get_error(self._store_ctx)))),
+ ]
+ _x509 = _lib.X509_STORE_CTX_get_current_cert(self._store_ctx)
+ if _x509 != _ffi.NULL:
+ _cert = _lib.X509_dup(_x509)
+ pycert = X509.__new__(X509)
+ pycert._x509 = _ffi.gc(_cert, _lib.X509_free)
+ return X509StoreContextError(errors, pycert)
+
+
+ def verify_certificate(self):
+ """
+ Verify a certificate in a context.
+
+ :param store_ctx: The :py:class:`X509StoreContext` to verify.
+ :raises: Error
+ """
+ self._init()
+ ret = _lib.X509_verify_cert(self._store_ctx)
+ self._cleanup()
+ if ret <= 0:
+ raise self._exception_from_context()
+
+
def load_certificate(type, buffer):
"""
@@ -2388,20 +2409,6 @@ def verify(cert, signature, data, digest):
_raise_current_error()
-def verify_cert(store_ctx):
- """
- Verify a certificate in a context.
-
- :param store_ctx: The :py:class:`X509StoreContext` to verify.
- :raises: Error
- """
- store_ctx._init()
- ret = _lib.X509_verify_cert(store_ctx._store_ctx)
- store_ctx._cleanup()
- if ret <= 0:
- _raise_context_error(store_ctx)
-
-
def load_crl(type, buffer):
"""
Load a certificate revocation list from a buffer
diff --git a/OpenSSL/test/test_crypto.py b/OpenSSL/test/test_crypto.py
index fc6350c..0aac1e5 100644
--- a/OpenSSL/test/test_crypto.py
+++ b/OpenSSL/test/test_crypto.py
@@ -17,7 +17,7 @@ from six import u, b, binary_type
from OpenSSL.crypto import TYPE_RSA, TYPE_DSA, Error, PKey, PKeyType
from OpenSSL.crypto import X509, X509Type, X509Name, X509NameType
-from OpenSSL.crypto import X509Store, X509StoreType, X509StoreContext
+from OpenSSL.crypto import X509Store, X509StoreType, X509StoreContext, X509StoreContextError
from OpenSSL.crypto import X509Req, X509ReqType
from OpenSSL.crypto import X509Extension, X509ExtensionType
from OpenSSL.crypto import load_certificate, load_privatekey
@@ -29,7 +29,7 @@ from OpenSSL.crypto import PKCS12, PKCS12Type, load_pkcs12
from OpenSSL.crypto import CRL, Revoked, load_crl
from OpenSSL.crypto import NetscapeSPKI, NetscapeSPKIType
from OpenSSL.crypto import (
- sign, verify, verify_cert, get_elliptic_curve, get_elliptic_curves)
+ sign, verify, get_elliptic_curve, get_elliptic_curves)
from OpenSSL.test.util import EqualityTestsMixin, TestCase
from OpenSSL._util import native, lib
@@ -3175,9 +3175,9 @@ class CRLTests(TestCase):
-class VerifyCertTests(TestCase):
+class X509StoreContextTests(TestCase):
"""
- Tests for :py:obj:`OpenSSL.crypto.verify_cert`.
+ Tests for :py:obj:`OpenSSL.crypto.X509StoreContext`.
"""
root_cert = load_certificate(FILETYPE_PEM, root_cert_pem)
intermediate_cert = load_certificate(FILETYPE_PEM, intermediate_cert_pem)
@@ -3185,74 +3185,74 @@ class VerifyCertTests(TestCase):
def test_valid(self):
"""
- :py:obj:`verify_cert` returns ``None`` when called with a certificate
+ :py:obj:`verify_certificate` returns ``None`` when called with a certificate
and valid chain.
"""
store = X509Store()
store.add_cert(self.root_cert)
store.add_cert(self.intermediate_cert)
store_ctx = X509StoreContext(store, self.intermediate_server_cert)
- self.assertEqual(verify_cert(store_ctx), None)
+ self.assertEqual(store_ctx.verify_certificate(), None)
def test_reuse(self):
"""
- :py:obj:`verify_cert` can be called multiple times with the same
+ :py:obj:`verify_certificate` can be called multiple times with the same
``X509StoreContext`` instance to produce the same result.
"""
store = X509Store()
store.add_cert(self.root_cert)
store.add_cert(self.intermediate_cert)
store_ctx = X509StoreContext(store, self.intermediate_server_cert)
- self.assertEqual(verify_cert(store_ctx), None)
- self.assertEqual(verify_cert(store_ctx), None)
+ self.assertEqual(store_ctx.verify_certificate(), None)
+ self.assertEqual(store_ctx.verify_certificate(), None)
def test_trusted_self_signed(self):
"""
- :py:obj:`verify_cert` returns ``None`` when called with a self-signed
+ :py:obj:`verify_certificate` returns ``None`` when called with a self-signed
certificate and itself in the chain.
"""
store = X509Store()
store.add_cert(self.root_cert)
store_ctx = X509StoreContext(store, self.root_cert)
- self.assertEqual(verify_cert(store_ctx), None)
+ self.assertEqual(store_ctx.verify_certificate(), None)
def test_untrusted_self_signed(self):
"""
- :py:obj:`verify_cert` raises error when a self-signed certificate is
+ :py:obj:`verify_certificate` raises error when a self-signed certificate is
verified without itself in the chain.
"""
store = X509Store()
store_ctx = X509StoreContext(store, self.root_cert)
- e = self.assertRaises(Error, verify_cert, store_ctx)
+ e = self.assertRaises(X509StoreContextError, store_ctx.verify_certificate)
self.assertEqual(e.args[0][2], 'self signed certificate')
self.assertEqual(e.certificate.get_subject().CN, 'Testing Root CA')
def test_invalid_chain_no_root(self):
"""
- :py:obj:`verify_cert` raises error when a root certificate is missing
+ :py:obj:`verify_certificate` raises error when a root certificate is missing
from the chain.
"""
store = X509Store()
store.add_cert(self.intermediate_cert)
store_ctx = X509StoreContext(store, self.intermediate_server_cert)
- e = self.assertRaises(Error, verify_cert, store_ctx)
+ e = self.assertRaises(X509StoreContextError, store_ctx.verify_certificate)
self.assertEqual(e.args[0][2], 'unable to get issuer certificate')
self.assertEqual(e.certificate.get_subject().CN, 'intermediate')
def test_invalid_chain_no_intermediate(self):
"""
- :py:obj:`verify_cert` raises error when an intermediate certificate is
+ :py:obj:`verify_certificate` raises error when an intermediate certificate is
missing from the chain.
"""
store = X509Store()
store.add_cert(self.root_cert)
store_ctx = X509StoreContext(store, self.intermediate_server_cert)
- e = self.assertRaises(Error, verify_cert, store_ctx)
+ e = self.assertRaises(X509StoreContextError, store_ctx.verify_certificate)
self.assertEqual(e.args[0][2], 'unable to get local issuer certificate')
self.assertEqual(e.certificate.get_subject().CN, 'intermediate-service')
diff --git a/doc/api/crypto.rst b/doc/api/crypto.rst
index 344fa40..5528870 100644
--- a/doc/api/crypto.rst
+++ b/doc/api/crypto.rst
@@ -240,18 +240,6 @@
.. versionadded:: 0.11
-.. py:function:: verify_cert(store_ctx)
-
- Verify a certificate in a context.
-
- A :py:class:`X509StoreContext` is used to verify a certificate in some
- context in conjunction with :py:func:`verify_cert`. The information
- encapsulated in this object includes, but is not limited to, a set of
- trusted certificates, verification parameters and revoked certificates.
-
- .. versionadded:: 0.15
-
-
.. _openssl-x509:
X509 objects
@@ -548,6 +536,22 @@ The X509Store object has currently just one method:
Add the certificate *cert* to the certificate store.
+X509StoreContext objects
+------------------------
+
+The X509StoreContext object is used for verifying a certificate against a set
+of trusted certificates.
+
+
+.. py:method:: X509StoreContext.verify_certificate()
+
+ Verify a certificate in the context of this initialized `X509StoreContext`.
+ On error, raises `X509StoreContextError`, otherwise does nothing.
+
+ .. versionadded:: 0.15
+
+
+
.. _openssl-pkey:
PKey objects