]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Issue #19292: Add SSLContext.load_default_certs() to load default root CA
authorChristian Heimes <christian@cheimes.de>
Sat, 23 Nov 2013 12:56:58 +0000 (13:56 +0100)
committerChristian Heimes <christian@cheimes.de>
Sat, 23 Nov 2013 12:56:58 +0000 (13:56 +0100)
certificates from default stores or system stores. By default the method
loads CA certs for authentication of server certs.

Doc/library/ssl.rst
Lib/ssl.py
Lib/test/test_ssl.py
Misc/NEWS

index 7a7ddacb9a447b689ca2b56a423d7ab6e6195b2f..8d14ae9119800d3849897022bc3b9c6c2b243b36 100644 (file)
@@ -681,6 +681,20 @@ Constants
 
    .. versionadded:: 3.4
 
+.. data:: Purpose.SERVER_AUTH
+
+   Option for :meth:`SSLContext.load_default_certs` to load CA certificates
+   for TLS web server authentication (client side socket).
+
+   .. versionadded:: 3.4
+
+.. data:: Purpose.clientAuth
+
+   Option for :meth:`SSLContext.load_default_certs` to load CA certificates
+   for TLS web client authentication (server side socket).
+
+   .. versionadded:: 3.4
+
 
 SSL Sockets
 -----------
@@ -903,6 +917,22 @@ to speed up repeated connections from the same clients.
    .. versionchanged:: 3.3
       New optional argument *password*.
 
+.. method:: SSLContext.load_default_certs(purpose=Purpose.SERVER_AUTH)
+
+   Load a set of default "certification authority" (CA) certificates from
+   default locations. On Windows it loads CA certs from the ``CA`` and
+   ``ROOT`` system stores. On other systems it calls
+   :meth:`SSLContext.set_default_verify_paths`. In the future the method may
+   load CA certificates from other locations, too.
+
+   The *purpose* flag specifies what kind of CA certificates are loaded. The
+   default settings :data:`Purpose.SERVER_AUTH` loads certificates, that are
+   flagged and trusted for TLS web server authentication (client side
+   sockets). :data:`Purpose.clientAuth` loads CA certificates for client
+   certificate verification on the server side.
+
+   .. versionadded:: 3.4
+
 .. method:: SSLContext.load_verify_locations(cafile=None, capath=None, cadata=None)
 
    Load a set of "certification authority" (CA) certificates used to validate
@@ -931,7 +961,6 @@ to speed up repeated connections from the same clients.
    .. versionchanged:: 3.4
       New optional argument *cadata*
 
-
 .. method:: SSLContext.get_ca_certs(binary_form=False)
 
    Get a list of loaded "certification authority" (CA) certificates. If the
index d4c7bad6f804ea7b6bd5e57f8afe6d67ef33bbf0..e668dc181f0d526b6c9b5c7ffe752736cbcb0f4c 100644 (file)
@@ -92,6 +92,7 @@ import re
 import sys
 import os
 from collections import namedtuple
+from enum import Enum as _Enum
 
 import _ssl             # if we can't import it, let the error propagate
 
@@ -298,11 +299,19 @@ class _ASN1Object(namedtuple("_ASN1Object", "nid shortname longname oid")):
         return super().__new__(cls, *_txt2obj(name, name=True))
 
 
+class Purpose(_ASN1Object, _Enum):
+    """SSLContext purpose flags with X509v3 Extended Key Usage objects
+    """
+    SERVER_AUTH = '1.3.6.1.5.5.7.3.1'
+    CLIENT_AUTH = '1.3.6.1.5.5.7.3.2'
+
+
 class SSLContext(_SSLContext):
     """An SSLContext holds various SSL-related configuration options and
     data, such as certificates and possibly a private key."""
 
     __slots__ = ('protocol', '__weakref__')
+    _windows_cert_stores = ("CA", "ROOT")
 
     def __new__(cls, protocol, *args, **kwargs):
         self = _SSLContext.__new__(cls, protocol)
@@ -334,6 +343,25 @@ class SSLContext(_SSLContext):
 
         self._set_npn_protocols(protos)
 
+    def _load_windows_store_certs(self, storename, purpose):
+        certs = bytearray()
+        for cert, encoding, trust in enum_certificates(storename):
+            # CA certs are never PKCS#7 encoded
+            if encoding == "x509_asn":
+                if trust is True or purpose.oid in trust:
+                    certs.extend(cert)
+        self.load_verify_locations(cadata=certs)
+        return certs
+
+    def load_default_certs(self, purpose=Purpose.SERVER_AUTH):
+        if not isinstance(purpose, _ASN1Object):
+            raise TypeError(purpose)
+        if sys.platform == "win32":
+            for storename in self._windows_cert_stores:
+                self._load_windows_store_certs(storename, purpose)
+        else:
+            self.set_default_verify_paths()
+
 
 class SSLSocket(socket):
     """This class implements a subtype of socket.socket that wraps
index d6a7443427a00665c96f780074d34eaa99724328..722d331f273ed4f352628c1da022495ae9eef3da 100644 (file)
@@ -611,6 +611,23 @@ class BasicSocketTests(unittest.TestCase):
         with self.assertRaisesRegex(ValueError, "unknown object 'serverauth'"):
             ssl._ASN1Object.fromname('serverauth')
 
+    def test_purpose_enum(self):
+        val = ssl._ASN1Object('1.3.6.1.5.5.7.3.1')
+        self.assertIsInstance(ssl.Purpose.SERVER_AUTH, ssl._ASN1Object)
+        self.assertEqual(ssl.Purpose.SERVER_AUTH, val)
+        self.assertEqual(ssl.Purpose.SERVER_AUTH.nid, 129)
+        self.assertEqual(ssl.Purpose.SERVER_AUTH.shortname, 'serverAuth')
+        self.assertEqual(ssl.Purpose.SERVER_AUTH.oid,
+                              '1.3.6.1.5.5.7.3.1')
+
+        val = ssl._ASN1Object('1.3.6.1.5.5.7.3.2')
+        self.assertIsInstance(ssl.Purpose.CLIENT_AUTH, ssl._ASN1Object)
+        self.assertEqual(ssl.Purpose.CLIENT_AUTH, val)
+        self.assertEqual(ssl.Purpose.CLIENT_AUTH.nid, 130)
+        self.assertEqual(ssl.Purpose.CLIENT_AUTH.shortname, 'clientAuth')
+        self.assertEqual(ssl.Purpose.CLIENT_AUTH.oid,
+                              '1.3.6.1.5.5.7.3.2')
+
 
 class ContextTests(unittest.TestCase):
 
@@ -967,6 +984,21 @@ class ContextTests(unittest.TestCase):
         der = ssl.PEM_cert_to_DER_cert(pem)
         self.assertEqual(ctx.get_ca_certs(True), [der])
 
+    def test_load_default_certs(self):
+        ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
+        ctx.load_default_certs()
+
+        ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
+        ctx.load_default_certs(ssl.Purpose.SERVER_AUTH)
+        ctx.load_default_certs()
+
+        ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
+        ctx.load_default_certs(ssl.Purpose.CLIENT_AUTH)
+
+        ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
+        self.assertRaises(TypeError, ctx.load_default_certs, None)
+        self.assertRaises(TypeError, ctx.load_default_certs, 'SERVER_AUTH')
+
 
 class SSLErrorTests(unittest.TestCase):
 
index ed04e36b4250b589f416fe6e476e6083d20ce7ce..ac04743df5384767eb2fbc4b0be4c0ab60267a4e 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -68,6 +68,10 @@ Core and Builtins
 Library
 -------
 
+- Issue #19292: Add SSLContext.load_default_certs() to load default root CA
+  certificates from default stores or system stores. By default the method
+  loads CA certs for authentication of server certs.
+
 - Issue #19673: Add pathlib to the stdlib as a provisional module (PEP 428).
 
 - Issue #17916: Added dis.Bytecode.from_traceback() and