diff -r 9ee40eec0180 Doc/library/ssl.rst --- a/Doc/library/ssl.rst Sat Nov 23 14:42:01 2013 +0100 +++ b/Doc/library/ssl.rst Sat Nov 23 15:28:55 2013 +0100 @@ -346,6 +346,24 @@ .. versionchanged:: 3.3 This function is now IPv6-compatible. +.. function:: create_default_context(purpose=Purpose,SERVER_AUTH, cafile=None, capath=None, cadata=None) + + Create a :class:`SSLContext` with default settings. + + The current settings are: :data:`PROTOCOL_TLSv1` with high encryption + cipher suites without RC4 and without unauthenticated cipher suites. The + *purpose* :data:`Purpose,SERVER_AUTH` sets verify_mode to + :data:`CERT_REQUIRED` and either loads CA certs (when at least one of + *cafile*, *capath* or *cadata* is given) or uses + :meth:`SSLContext.load_default_certs` to load default CA certs. + + .. note:: + The protocol, options, cipher and other settings may change to more + restrictive values anytime without prior deprecation. The values + represent a fair balance between maximum compatibility and security. + + .. versionadded:: 3.4 + .. function:: DER_cert_to_PEM_cert(DER_cert_bytes) Given a certificate as a DER-encoded blob of bytes, returns a PEM-encoded diff -r 9ee40eec0180 Lib/ssl.py --- a/Lib/ssl.py Sat Nov 23 14:42:01 2013 +0100 +++ b/Lib/ssl.py Sat Nov 23 15:28:55 2013 +0100 @@ -165,6 +165,13 @@ # (OpenSSL's default setting is 'DEFAULT:!aNULL:!eNULL') _DEFAULT_CIPHERS = 'DEFAULT:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2' +# restricted and more secure ciphers +# HIGH: high encryption cipher suites with key length >= 128 bits (no MD5) +# !aNULL: only authenticated cipher suites (no anonymous DH) +# !RC4: no RC4 streaming cipher, RC4 is broken +# !DSS: RSA is preferred over DSA +_RESTRICTED_CIPHERS = 'HIGH:!aNULL:!RC4:!DSS' + class CertificateError(ValueError): pass @@ -363,6 +370,34 @@ self.set_default_verify_paths() +def create_default_context(purpose=Purpose.SERVER_AUTH, *, cafile=None, + capath=None, cadata=None): + """Create a SSLContext object with default settings. + + NOTE: The protocol and settings may change anytime without prior + deprecation. The values represent a fair balance between maximum + compatibility and security. + """ + if not isinstance(purpose, _ASN1Object): + raise TypeError(purpose) + context = SSLContext(PROTOCOL_TLSv1) + # SSLv2 considered harmful. + context.options |= OP_NO_SSLv2 + # disallow ciphers with known vulnerabilities + context.set_ciphers(_RESTRICTED_CIPHERS) + # verify certs in client mode + if purpose == Purpose.SERVER_AUTH: + context.verify_mode = CERT_REQUIRED + if cafile or capath or cadata: + context.load_verify_locations(cafile, capath, cadata) + elif context.verify_mode != CERT_NONE: + # no explicit cafile, capath or cadata but the verify mode is + # CERT_OPTIONAL or CERT_REQUIRED. Let's try to load default system + # root CA certificates for the given purpose. This may fail silently. + context.load_default_certs(purpose) + return context + + class SSLSocket(socket): """This class implements a subtype of socket.socket that wraps the underlying OS socket in an SSL context when necessary, and diff -r 9ee40eec0180 Lib/test/test_ssl.py --- a/Lib/test/test_ssl.py Sat Nov 23 14:42:01 2013 +0100 +++ b/Lib/test/test_ssl.py Sat Nov 23 15:28:55 2013 +0100 @@ -999,6 +999,26 @@ self.assertRaises(TypeError, ctx.load_default_certs, None) self.assertRaises(TypeError, ctx.load_default_certs, 'SERVER_AUTH') + def test_create_default_context(self): + ctx = ssl.create_default_context() + self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1) + self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED) + self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + + with open(SIGNING_CA) as f: + cadata = f.read() + ctx = ssl.create_default_context(cafile=SIGNING_CA, capath=CAPATH, + cadata=cadata) + self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1) + self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED) + self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + + ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1) + self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) + self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + + class SSLErrorTests(unittest.TestCase):