Issue #8550: Add first class `SSLContext` objects to the ssl module.
diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
index 95f5f70..45ffcb0 100644
--- a/Doc/library/ssl.rst
+++ b/Doc/library/ssl.rst
@@ -36,6 +36,11 @@
 connection, and a method, :meth:`cipher`, to retrieve the cipher being used for
 the secure connection.
 
+For more sophisticated applications, the :class:`ssl.SSLContext` class
+helps manage settings and certificates, which can then be inherited
+by SSL sockets created through the :meth:`SSLContext.wrap_socket` method.
+
+
 Functions, Constants, and Exceptions
 ------------------------------------
 
@@ -64,19 +69,6 @@
    connection.  See the discussion of :ref:`ssl-certificates` for more
    information on how the certificate is stored in the ``certfile``.
 
-   Often the private key is stored in the same file as the certificate; in this
-   case, only the ``certfile`` parameter need be passed.  If the private key is
-   stored in a separate file, both parameters must be used.  If the private key
-   is stored in the ``certfile``, it should come before the first certificate in
-   the certificate chain::
-
-      -----BEGIN RSA PRIVATE KEY-----
-      ... (private key in base64 encoding) ...
-      -----END RSA PRIVATE KEY-----
-      -----BEGIN CERTIFICATE-----
-      ... (certificate in base64 PEM encoding) ...
-      -----END CERTIFICATE-----
-
    The parameter ``server_side`` is a boolean which identifies whether
    server-side or client-side behavior is desired from this socket.
 
@@ -208,24 +200,36 @@
 
 .. data:: CERT_NONE
 
-   Value to pass to the ``cert_reqs`` parameter to :func:`sslobject` when no
-   certificates will be required or validated from the other side of the socket
-   connection.
+   Possible value for :attr:`SSLContext.verify_mode`, or the ``cert_reqs``
+   parameter to :func:`wrap_socket`.  In this mode (the default), no
+   certificates will be required from the other side of the socket connection.
+   If a certificate is received from the other end, no attempt to validate it
+   is made.
+
+   See the discussion of :ref:`ssl-security` below.
 
 .. data:: CERT_OPTIONAL
 
-   Value to pass to the ``cert_reqs`` parameter to :func:`sslobject` when no
-   certificates will be required from the other side of the socket connection,
-   but if they are provided, will be validated.  Note that use of this setting
-   requires a valid certificate validation file also be passed as a value of the
-   ``ca_certs`` parameter.
+   Possible value for :attr:`SSLContext.verify_mode`, or the ``cert_reqs``
+   parameter to :func:`wrap_socket`.  In this mode no certificates will be
+   required from the other side of the socket connection; but if they
+   are provided, validation will be attempted and an :class:`SSLError`
+   will be raised on failure.
+
+   Use of this setting requires a valid set of CA certificates to
+   be passed, either to :meth:`SSLContext.load_verify_locations` or as a
+   value of the ``ca_certs`` parameter to :func:`wrap_socket`.
 
 .. data:: CERT_REQUIRED
 
-   Value to pass to the ``cert_reqs`` parameter to :func:`sslobject` when
-   certificates will be required from the other side of the socket connection.
-   Note that use of this setting requires a valid certificate validation file
-   also be passed as a value of the ``ca_certs`` parameter.
+   Possible value for :attr:`SSLContext.verify_mode`, or the ``cert_reqs``
+   parameter to :func:`wrap_socket`.  In this mode, certificates are
+   required from the other side of the socket connection; an :class:`SSLError`
+   will be raised if no certificate is provided, or if its validation fails.
+
+   Use of this setting requires a valid set of CA certificates to
+   be passed, either to :meth:`SSLContext.load_verify_locations` or as a
+   value of the ``ca_certs`` parameter to :func:`wrap_socket`.
 
 .. data:: PROTOCOL_SSLv2
 
@@ -284,8 +288,8 @@
    .. versionadded:: 3.2
 
 
-SSLSocket Objects
------------------
+SSL Sockets
+-----------
 
 .. method:: SSLSocket.read(nbytes=1024, buffer=None)
 
@@ -371,6 +375,83 @@
    returned socket should always be used for further communication with the
    other side of the connection, rather than the original socket.
 
+
+SSL Contexts
+------------
+
+.. class:: SSLContext(protocol)
+
+   An object holding various data longer-lived than single SSL connections,
+   such as SSL configuration options, certificate(s) and private key(s).
+   You must pass *protocol* which must be one of the ``PROTOCOL_*`` constants
+   defined in this module.  :data:`PROTOCOL_SSLv23` is recommended for
+   maximum interoperability.
+
+:class:`SSLContext` objects have the following methods and attributes:
+
+.. method:: SSLContext.load_cert_chain(certfile, keyfile=None)
+
+   Load a private key and the corresponding certificate.  The *certfile*
+   string must be the path to a single file in PEM format containing the
+   certificate as well as any number of CA certificates needed to establish
+   the certificate's authenticity.  The *keyfile* string, if present, must
+   point to a file containing the private key in.  Otherwise the private
+   key will be taken from *certfile* as well.  See the discussion of
+   :ref:`ssl-certificates` for more information on how the certificate
+   is stored in the *certfile*.
+
+   An :class:`SSLError` is raised if the private key doesn't
+   match with the certificate.
+
+.. method:: SSLContext.load_verify_locations(cafile=None, capath=None)
+
+   Load a set of "certification authority" (CA) certificates used to validate
+   other peers' certificates when :data:`verify_mode` is other than
+   :data:`CERT_NONE`.  At least one of *cafile* or *capath* must be specified.
+
+   The *cafile* string, if present, is the path to a file of concatenated
+   CA certificates in PEM format. See the discussion of
+   :ref:`ssl-certificates` for more information about how to arrange the
+   certificates in this file.
+
+   The *capath* string, if present, is
+   the path to a directory containing several CA certificates in PEM format,
+   following an `OpenSSL specific layout
+   <http://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html>`_.
+
+.. method:: SSLContext.set_ciphers(ciphers)
+
+   Set the available ciphers for sockets created with this context.
+   It should be a string in the `OpenSSL cipher list format
+   <http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT>`_.
+   If no cipher can be selected (because compile-time options or other
+   configuration forbids use of all the specified ciphers), an
+   :class:`SSLError` will be raised.
+
+   .. note::
+      when connected, the :meth:`SSLSocket.cipher` method of SSL sockets will
+      give the currently selected cipher.
+
+.. method:: SSLContext.wrap_socket(sock, server_side=False, do_handshake_on_connect=True, suppress_ragged_eofs=True)
+
+   Wrap an existing Python socket *sock* and return an :class:`SSLSocket`
+   object.  The SSL socket is tied to the context, its settings and
+   certificates.  The parameters *server_side*, *do_handshake_on_connect*
+   and *suppress_ragged_eofs* have the same meaning as in the top-level
+   :func:`wrap_socket` function.
+
+.. attribute:: SSLContext.protocol
+
+   The protocol version chosen when constructing the context.  This attribute
+   is read-only.
+
+.. attribute:: SSLContext.verify_mode
+
+   Whether to try to verify other peers' certificates and how to behave
+   if verification fails.  This attribute must be one of
+   :data:`CERT_NONE`, :data:`CERT_OPTIONAL` or :data:`CERT_REQUIRED`.
+
+
 .. index:: single: certificates
 
 .. index:: single: X509 certificate
@@ -416,6 +497,9 @@
       ... (certificate in base64 PEM encoding) ...
       -----END CERTIFICATE-----
 
+Certificate chains
+^^^^^^^^^^^^^^^^^^
+
 The Python files which contain certificates can contain a sequence of
 certificates, sometimes called a *certificate chain*.  This chain should start
 with the specific certificate for the principal who "is" the client or server,
@@ -439,6 +523,9 @@
       ... (the root certificate for the CA's issuer)...
       -----END CERTIFICATE-----
 
+CA certificates
+^^^^^^^^^^^^^^^
+
 If you are going to require validation of the other side of the connection's
 certificate, you need to provide a "CA certs" file, filled with the certificate
 chains for each issuer you are willing to trust.  Again, this file just contains
@@ -458,6 +545,25 @@
 certificate to a root certificate.  See :rfc:`4158` for more discussion of the
 way in which certification chains can be built.
 
+Combined key and certificate
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Often the private key is stored in the same file as the certificate; in this
+case, only the ``certfile`` parameter to :meth:`SSLContext.load_cert_chain`
+and :func:`wrap_socket` needs to be passed.  If the private key is stored
+with the certificate, it should come before the first certificate in
+the certificate chain::
+
+   -----BEGIN RSA PRIVATE KEY-----
+   ... (private key in base64 encoding) ...
+   -----END RSA PRIVATE KEY-----
+   -----BEGIN CERTIFICATE-----
+   ... (certificate in base64 PEM encoding) ...
+   -----END CERTIFICATE-----
+
+Self-signed certificates
+^^^^^^^^^^^^^^^^^^^^^^^^
+
 If you are going to create a server that provides SSL-encrypted connection
 services, you will need to acquire a certificate for that service.  There are
 many ways of acquiring appropriate certificates, such as buying one from a
@@ -530,8 +636,7 @@
    print(pprint.pformat(ssl_sock.getpeercert()))
 
    # Set a simple HTTP request -- use http.client in actual code.
-   ssl_sock.write("""GET / HTTP/1.0\r
-   Host: www.verisign.com\r\n\r\n""")
+   ssl_sock.write(b"GET / HTTP/1.0\r\nHost: www.verisign.com\r\n\r\n")
 
    # Read a chunk of data.  Will not necessarily
    # read all the data returned by the server.
@@ -561,39 +666,91 @@
 
 which is a fairly poorly-formed ``subject`` field.
 
+This other example first creates an SSL context, instructs it to verify
+certificates sent by peers, and feeds it a set of recognized certificate
+authorities (CA)::
+
+   >>> context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+   >>> context.verify_mode = ssl.CERT_OPTIONAL
+   >>> context.load_verify_locations("/etc/ssl/certs/ca-bundle.crt")
+
+(it is assumed your operating system places a bundle of all CA certificates
+in ``/etc/ssl/certs/ca-bundle.crt``; if not, you'll get an error and have
+to adjust the location)
+
+When you use the context to connect to a server, :const:`CERT_OPTIONAL`
+validates the server certificate: it ensures that the server certificate
+was signed with one of the CA certificates, and checks the signature for
+correctness::
+
+   >>> conn = context.wrap_socket(socket.socket(socket.AF_INET))
+   >>> conn.connect(("linuxfr.org", 443))
+
+You should then fetch the certificate and check its fields for conformity.
+Here, the ``commonName`` field in the ``subject`` matches the desired HTTPS
+host ``linuxfr.org``::
+
+   >>> pprint.pprint(conn.getpeercert())
+   {'notAfter': 'Jun 26 21:41:46 2011 GMT',
+    'subject': ((('commonName', 'linuxfr.org'),),),
+    'subjectAltName': (('DNS', 'linuxfr.org'), ('othername', '<unsupported>'))}
+
+Now that you are assured of its authenticity, you can proceed to talk with
+the server::
+
+   >>> conn.write(b"HEAD / HTTP/1.0\r\nHost: linuxfr.org\r\n\r\n")
+   38
+   >>> pprint.pprint(conn.read().split(b"\r\n"))
+   [b'HTTP/1.1 302 Found',
+    b'Date: Sun, 16 May 2010 13:43:28 GMT',
+    b'Server: Apache/2.2',
+    b'Location: https://linuxfr.org/pub/',
+    b'Vary: Accept-Encoding',
+    b'Connection: close',
+    b'Content-Type: text/html; charset=iso-8859-1',
+    b'',
+    b'']
+
+
+See the discussion of :ref:`ssl-security` below.
+
+
 Server-side operation
 ^^^^^^^^^^^^^^^^^^^^^
 
-For server operation, typically you'd need to have a server certificate, and
-private key, each in a file.  You'd open a socket, bind it to a port, call
-:meth:`listen` on it, then start waiting for clients to connect::
+For server operation, typically you'll need to have a server certificate, and
+private key, each in a file.  You'll first create a context holding the key
+and the certificate, so that clients can check your authenticity.  Then
+you'll open a socket, bind it to a port, call :meth:`listen` on it, and start
+waiting for clients to connect::
 
    import socket, ssl
 
+   context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
+   context.load_cert_chain(certfile="mycertfile", keyfile="mykeyfile")
+
    bindsocket = socket.socket()
    bindsocket.bind(('myaddr.mydomain.com', 10023))
    bindsocket.listen(5)
 
-When one did, you'd call :meth:`accept` on the socket to get the new socket from
-the other end, and use :func:`wrap_socket` to create a server-side SSL context
-for it::
+When a client connects, you'll call :meth:`accept` on the socket to get the
+new socket from the other end, and use the context's :meth:`SSLContext.wrap_socket`
+method to create a server-side SSL socket for the connection::
 
    while True:
       newsocket, fromaddr = bindsocket.accept()
-      connstream = ssl.wrap_socket(newsocket,
-                                   server_side=True,
-                                   certfile="mycertfile",
-                                   keyfile="mykeyfile",
-                                   ssl_version=ssl.PROTOCOL_TLSv1)
-      deal_with_client(connstream)
+      connstream = context.wrap_socket(newsocket, server_side=True)
+      try:
+         deal_with_client(connstream)
+      finally:
+         connstream.close()
 
-Then you'd read data from the ``connstream`` and do something with it till you
+Then you'll read data from the ``connstream`` and do something with it till you
 are finished with the client (or the client is finished with you)::
 
    def deal_with_client(connstream):
-
       data = connstream.read()
-      # null data means the client is finished with us
+      # empty data means the client is finished with us
       while data:
          if not do_something(connstream, data):
             # we'll assume do_something returns False
@@ -601,9 +758,41 @@
             break
          data = connstream.read()
       # finished with client
-      connstream.close()
 
-And go back to listening for new client connections.
+And go back to listening for new client connections (of course, a real server
+would probably handle each client connection in a separate thread, or put
+the sockets in non-blocking mode and use an event loop).
+
+
+.. _ssl-security:
+
+Security considerations
+-----------------------
+
+Verifying certificates
+^^^^^^^^^^^^^^^^^^^^^^
+
+:const:`CERT_NONE` is the default.  Since it does not authenticate the other
+peer, it can be insecure, especially in client mode where most of time you
+would like to ensure the authenticity of the server you're talking to.
+Therefore, when in client mode, it is highly recommended to use
+:const:`CERT_REQUIRED`.  However, it is in itself not sufficient; you also
+have to check that the server certificate (obtained with
+:meth:`SSLSocket.getpeercert`) matches the desired service.  The exact way
+of doing so depends on the higher-level protocol used; for example, with
+HTTPS, you'll check that the host name in the URL matches either the
+``commonName`` field in the ``subjectName``, or one of the ``DNS`` fields
+in the ``subjectAltName``.
+
+In server mode, if you want to authenticate your clients using the SSL layer
+(rather than using a higher-level authentication mechanism), you'll also have
+to specify :const:`CERT_REQUIRED` and similarly check the client certificate.
+
+   .. note::
+
+      In client mode, :const:`CERT_OPTIONAL` and :const:`CERT_REQUIRED` are
+      equivalent unless anonymous ciphers are enabled (they are disabled
+      by default).
 
 
 .. seealso::
diff --git a/Lib/ssl.py b/Lib/ssl.py
index 19f50f4..24d3771 100644
--- a/Lib/ssl.py
+++ b/Lib/ssl.py
@@ -59,7 +59,7 @@
 import _ssl             # if we can't import it, let the error propagate
 
 from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION
-from _ssl import SSLError
+from _ssl import _SSLContext, SSLError
 from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
 from _ssl import (PROTOCOL_SSLv2, PROTOCOL_SSLv3, PROTOCOL_SSLv23,
                   PROTOCOL_TLSv1)
@@ -84,8 +84,29 @@
 import traceback
 import errno
 
-class SSLSocket(socket):
 
+class SSLContext(_SSLContext):
+    """An SSLContext holds various SSL-related configuration options and
+    data, such as certificates and possibly a private key."""
+
+    __slots__ = ('protocol',)
+
+    def __new__(cls, protocol, *args, **kwargs):
+        return _SSLContext.__new__(cls, protocol)
+
+    def __init__(self, protocol):
+        self.protocol = protocol
+
+    def wrap_socket(self, sock, server_side=False,
+                    do_handshake_on_connect=True,
+                    suppress_ragged_eofs=True):
+        return SSLSocket(sock=sock, server_side=server_side,
+                         do_handshake_on_connect=do_handshake_on_connect,
+                         suppress_ragged_eofs=suppress_ragged_eofs,
+                         _context=self)
+
+
+class SSLSocket(socket):
     """This class implements a subtype of socket.socket that wraps
     the underlying OS socket in an SSL context when necessary, and
     provides read and write methods over that channel."""
@@ -95,8 +116,31 @@
                  ssl_version=PROTOCOL_SSLv23, ca_certs=None,
                  do_handshake_on_connect=True,
                  family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None,
-                 suppress_ragged_eofs=True, ciphers=None):
+                 suppress_ragged_eofs=True, ciphers=None,
+                 _context=None):
 
+        if _context:
+            self.context = _context
+        else:
+            if certfile and not keyfile:
+                keyfile = certfile
+            self.context = SSLContext(ssl_version)
+            self.context.verify_mode = cert_reqs
+            if ca_certs:
+                self.context.load_verify_locations(ca_certs)
+            if certfile:
+                self.context.load_cert_chain(certfile, keyfile)
+            if ciphers:
+                self.context.set_ciphers(ciphers)
+            self.keyfile = keyfile
+            self.certfile = certfile
+            self.cert_reqs = cert_reqs
+            self.ssl_version = ssl_version
+            self.ca_certs = ca_certs
+            self.ciphers = ciphers
+
+        self.do_handshake_on_connect = do_handshake_on_connect
+        self.suppress_ragged_eofs = suppress_ragged_eofs
         connected = False
         if sock is not None:
             socket.__init__(self,
@@ -119,18 +163,12 @@
         else:
             socket.__init__(self, family=family, type=type, proto=proto)
 
-        if certfile and not keyfile:
-            keyfile = certfile
-
         self._closed = False
         self._sslobj = None
         if connected:
             # create the SSL object
             try:
-                self._sslobj = _ssl.sslwrap(self, server_side,
-                                            keyfile, certfile,
-                                            cert_reqs, ssl_version, ca_certs,
-                                            ciphers)
+                self._sslobj = self.context._wrap_socket(self, server_side)
                 if do_handshake_on_connect:
                     timeout = self.gettimeout()
                     if timeout == 0.0:
@@ -142,15 +180,6 @@
                 self.close()
                 raise x
 
-        self.keyfile = keyfile
-        self.certfile = certfile
-        self.cert_reqs = cert_reqs
-        self.ssl_version = ssl_version
-        self.ca_certs = ca_certs
-        self.ciphers = ciphers
-        self.do_handshake_on_connect = do_handshake_on_connect
-        self.suppress_ragged_eofs = suppress_ragged_eofs
-
     def dup(self):
         raise NotImplemented("Can't dup() %s instances" %
                              self.__class__.__name__)
@@ -331,9 +360,7 @@
         if self._sslobj:
             raise ValueError("attempt to connect already-connected SSLSocket!")
         socket.connect(self, addr)
-        self._sslobj = _ssl.sslwrap(self, False, self.keyfile, self.certfile,
-                                    self.cert_reqs, self.ssl_version,
-                                    self.ca_certs, self.ciphers)
+        self._sslobj = self.context._wrap_socket(self, False)
         try:
             if self.do_handshake_on_connect:
                 self.do_handshake()
diff --git a/Lib/test/capath/6e88d7b8.0 b/Lib/test/capath/6e88d7b8.0
new file mode 100644
index 0000000..9d7ac23
--- /dev/null
+++ b/Lib/test/capath/6e88d7b8.0
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICLDCCAdYCAQAwDQYJKoZIhvcNAQEEBQAwgaAxCzAJBgNVBAYTAlBUMRMwEQYD
+VQQIEwpRdWVlbnNsYW5kMQ8wDQYDVQQHEwZMaXNib2ExFzAVBgNVBAoTDk5ldXJv
+bmlvLCBMZGEuMRgwFgYDVQQLEw9EZXNlbnZvbHZpbWVudG8xGzAZBgNVBAMTEmJy
+dXR1cy5uZXVyb25pby5wdDEbMBkGCSqGSIb3DQEJARYMc2FtcG9AaWtpLmZpMB4X
+DTk2MDkwNTAzNDI0M1oXDTk2MTAwNTAzNDI0M1owgaAxCzAJBgNVBAYTAlBUMRMw
+EQYDVQQIEwpRdWVlbnNsYW5kMQ8wDQYDVQQHEwZMaXNib2ExFzAVBgNVBAoTDk5l
+dXJvbmlvLCBMZGEuMRgwFgYDVQQLEw9EZXNlbnZvbHZpbWVudG8xGzAZBgNVBAMT
+EmJydXR1cy5uZXVyb25pby5wdDEbMBkGCSqGSIb3DQEJARYMc2FtcG9AaWtpLmZp
+MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL7+aty3S1iBA/+yxjxv4q1MUTd1kjNw
+L4lYKbpzzlmC5beaQXeQ2RmGMTXU+mDvuqItjVHOK3DvPK7lTcSGftUCAwEAATAN
+BgkqhkiG9w0BAQQFAANBAFqPEKFjk6T6CKTHvaQeEAsX0/8YHPHqH/9AnhSjrwuX
+9EBc0n6bVGhN7XaXd6sJ7dym9sbsWxb+pJdurnkxjx4=
+-----END CERTIFICATE-----
diff --git a/Lib/test/capath/99d0fa06.0 b/Lib/test/capath/99d0fa06.0
new file mode 100644
index 0000000..e7dfc82
--- /dev/null
+++ b/Lib/test/capath/99d0fa06.0
@@ -0,0 +1,41 @@
+-----BEGIN CERTIFICATE-----
+MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290
+IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB
+IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA
+Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO
+BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi
+MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ
+ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ
+8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6
+zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y
+fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7
+w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc
+G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k
+epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q
+laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ
+QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU
+fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826
+YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w
+ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY
+gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe
+MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0
+IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy
+dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw
+czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0
+dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl
+aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC
+AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg
+b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB
+ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc
+nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg
+18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c
+gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl
+Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY
+sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T
+SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF
+CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum
+GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk
+zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW
+omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD
+-----END CERTIFICATE-----
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index 7895b4a..80c586b 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -10,6 +10,7 @@
 import os
 import errno
 import pprint
+import tempfile
 import urllib.parse, urllib.request
 import traceback
 import asyncore
@@ -25,8 +26,30 @@
     skip_expected = True
 
 HOST = support.HOST
-CERTFILE = None
-SVN_PYTHON_ORG_ROOT_CERT = None
+PROTOCOLS = [
+    ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv3,
+    ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1
+]
+
+data_file = lambda name: os.path.join(os.path.dirname(__file__), name)
+fsencode = lambda name: name.encode(sys.getfilesystemencoding(), "surrogateescape")
+
+CERTFILE = data_file("keycert.pem")
+BYTES_CERTFILE = fsencode(CERTFILE)
+ONLYCERT = data_file("ssl_cert.pem")
+ONLYKEY = data_file("ssl_key.pem")
+BYTES_ONLYCERT = fsencode(ONLYCERT)
+BYTES_ONLYKEY = fsencode(ONLYKEY)
+CAPATH = data_file("capath")
+BYTES_CAPATH = fsencode(CAPATH)
+
+SVN_PYTHON_ORG_ROOT_CERT = data_file("https_svn_python_org_root.pem")
+
+EMPTYCERT = data_file("nullcert.pem")
+BADCERT = data_file("badcert.pem")
+WRONGCERT = data_file("XXXnonexisting.pem")
+BADKEY = data_file("badkey.pem")
+
 
 def handle_error(prefix):
     exc_format = ' '.join(traceback.format_exception(*sys.exc_info()))
@@ -34,7 +57,7 @@
         sys.stdout.write(prefix + exc_format)
 
 
-class BasicTests(unittest.TestCase):
+class BasicSocketTests(unittest.TestCase):
 
     def test_constants(self):
         ssl.PROTOCOL_SSLv2
@@ -116,11 +139,10 @@
         s = ssl.wrap_socket(socket.socket(socket.AF_INET),
                             cert_reqs=ssl.CERT_NONE, ciphers="DEFAULT")
         s.connect(remote)
-        # Error checking occurs when connecting, because the SSL context
-        # isn't created before.
-        s = ssl.wrap_socket(socket.socket(socket.AF_INET),
-                            cert_reqs=ssl.CERT_NONE, ciphers="^$:,;?*'dorothyx")
+        # Error checking can happen at instantiation or when connecting
         with self.assertRaisesRegexp(ssl.SSLError, "No cipher can be selected"):
+            s = ssl.wrap_socket(socket.socket(socket.AF_INET),
+                                cert_reqs=ssl.CERT_NONE, ciphers="^$:,;?*'dorothyx")
             s.connect(remote)
 
     @support.cpython_only
@@ -143,26 +165,103 @@
             self.assertEqual(timeout, ss.gettimeout())
 
 
+class ContextTests(unittest.TestCase):
+
+    def test_constructor(self):
+        ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv2)
+        ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+        ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv3)
+        ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
+        self.assertRaises(TypeError, ssl.SSLContext)
+        self.assertRaises(ValueError, ssl.SSLContext, -1)
+        self.assertRaises(ValueError, ssl.SSLContext, 42)
+
+    def test_protocol(self):
+        for proto in PROTOCOLS:
+            ctx = ssl.SSLContext(proto)
+            self.assertEqual(ctx.protocol, proto)
+
+    def test_ciphers(self):
+        ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
+        ctx.set_ciphers("ALL")
+        ctx.set_ciphers("DEFAULT")
+        with self.assertRaisesRegexp(ssl.SSLError, "No cipher can be selected"):
+            ctx.set_ciphers("^$:,;?*'dorothyx")
+
+    def test_verify(self):
+        ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
+        # Default value
+        self.assertEqual(ctx.verify_mode, ssl.CERT_NONE)
+        ctx.verify_mode = ssl.CERT_OPTIONAL
+        self.assertEqual(ctx.verify_mode, ssl.CERT_OPTIONAL)
+        ctx.verify_mode = ssl.CERT_REQUIRED
+        self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED)
+        ctx.verify_mode = ssl.CERT_NONE
+        self.assertEqual(ctx.verify_mode, ssl.CERT_NONE)
+        with self.assertRaises(TypeError):
+            ctx.verify_mode = None
+        with self.assertRaises(ValueError):
+            ctx.verify_mode = 42
+
+    def test_load_cert_chain(self):
+        ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
+        # Combined key and cert in a single file
+        ctx.load_cert_chain(CERTFILE)
+        ctx.load_cert_chain(CERTFILE, keyfile=CERTFILE)
+        self.assertRaises(TypeError, ctx.load_cert_chain, keyfile=CERTFILE)
+        with self.assertRaisesRegexp(ssl.SSLError, "system lib"):
+            ctx.load_cert_chain(WRONGCERT)
+        with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"):
+            ctx.load_cert_chain(BADCERT)
+        with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"):
+            ctx.load_cert_chain(EMPTYCERT)
+        # Separate key and cert
+        ctx.load_cert_chain(ONLYCERT, ONLYKEY)
+        ctx.load_cert_chain(certfile=ONLYCERT, keyfile=ONLYKEY)
+        ctx.load_cert_chain(certfile=BYTES_ONLYCERT, keyfile=BYTES_ONLYKEY)
+        with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"):
+            ctx.load_cert_chain(ONLYCERT)
+        with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"):
+            ctx.load_cert_chain(ONLYKEY)
+        with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"):
+            ctx.load_cert_chain(certfile=ONLYKEY, keyfile=ONLYCERT)
+        # Mismatching key and cert
+        with self.assertRaisesRegexp(ssl.SSLError, "key values mismatch"):
+            ctx.load_cert_chain(CERTFILE, ONLYKEY)
+
+    def test_load_verify_locations(self):
+        ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
+        ctx.load_verify_locations(CERTFILE)
+        ctx.load_verify_locations(cafile=CERTFILE, capath=None)
+        ctx.load_verify_locations(BYTES_CERTFILE)
+        ctx.load_verify_locations(cafile=BYTES_CERTFILE, capath=None)
+        self.assertRaises(TypeError, ctx.load_verify_locations)
+        self.assertRaises(TypeError, ctx.load_verify_locations, None, None)
+        with self.assertRaisesRegexp(ssl.SSLError, "system lib"):
+            ctx.load_verify_locations(WRONGCERT)
+        with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"):
+            ctx.load_verify_locations(BADCERT)
+        ctx.load_verify_locations(CERTFILE, CAPATH)
+        ctx.load_verify_locations(CERTFILE, capath=BYTES_CAPATH)
+
+
 class NetworkedTests(unittest.TestCase):
 
     def test_connect(self):
         s = ssl.wrap_socket(socket.socket(socket.AF_INET),
                             cert_reqs=ssl.CERT_NONE)
-        s.connect(("svn.python.org", 443))
-        c = s.getpeercert()
-        if c:
-            self.fail("Peer cert %s shouldn't be here!")
-        s.close()
+        try:
+            s.connect(("svn.python.org", 443))
+            self.assertEqual({}, s.getpeercert())
+        finally:
+            s.close()
 
         # this should fail because we have no verification certs
         s = ssl.wrap_socket(socket.socket(socket.AF_INET),
                             cert_reqs=ssl.CERT_REQUIRED)
-        try:
-            s.connect(("svn.python.org", 443))
-        except ssl.SSLError:
-            pass
-        finally:
-            s.close()
+        self.assertRaisesRegexp(ssl.SSLError, "certificate verify failed",
+                                s.connect, ("svn.python.org", 443))
+        s.close()
 
         # this should succeed because we specify the root cert
         s = ssl.wrap_socket(socket.socket(socket.AF_INET),
@@ -170,6 +269,56 @@
                             ca_certs=SVN_PYTHON_ORG_ROOT_CERT)
         try:
             s.connect(("svn.python.org", 443))
+            self.assertTrue(s.getpeercert())
+        finally:
+            s.close()
+
+    def test_connect_with_context(self):
+        # Same as test_connect, but with a separately created context
+        ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+        s = ctx.wrap_socket(socket.socket(socket.AF_INET))
+        s.connect(("svn.python.org", 443))
+        try:
+            self.assertEqual({}, s.getpeercert())
+        finally:
+            s.close()
+        # This should fail because we have no verification certs
+        ctx.verify_mode = ssl.CERT_REQUIRED
+        s = ctx.wrap_socket(socket.socket(socket.AF_INET))
+        self.assertRaisesRegexp(ssl.SSLError, "certificate verify failed",
+                                s.connect, ("svn.python.org", 443))
+        s.close()
+        # This should succeed because we specify the root cert
+        ctx.load_verify_locations(SVN_PYTHON_ORG_ROOT_CERT)
+        s = ctx.wrap_socket(socket.socket(socket.AF_INET))
+        s.connect(("svn.python.org", 443))
+        try:
+            cert = s.getpeercert()
+            self.assertTrue(cert)
+        finally:
+            s.close()
+
+    def test_connect_capath(self):
+        # Verify server certificates using the `capath` argument
+        ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+        ctx.verify_mode = ssl.CERT_REQUIRED
+        ctx.load_verify_locations(capath=CAPATH)
+        s = ctx.wrap_socket(socket.socket(socket.AF_INET))
+        s.connect(("svn.python.org", 443))
+        try:
+            cert = s.getpeercert()
+            self.assertTrue(cert)
+        finally:
+            s.close()
+        # Same with a bytes `capath` argument
+        ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+        ctx.verify_mode = ssl.CERT_REQUIRED
+        ctx.load_verify_locations(capath=BYTES_CAPATH)
+        s = ctx.wrap_socket(socket.socket(socket.AF_INET))
+        s.connect(("svn.python.org", 443))
+        try:
+            cert = s.getpeercert()
+            self.assertTrue(cert)
         finally:
             s.close()
 
@@ -1227,18 +1376,14 @@
     if skip_expected:
         raise unittest.SkipTest("No SSL support")
 
-    global CERTFILE, SVN_PYTHON_ORG_ROOT_CERT
-    CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir,
-                            "keycert.pem")
-    SVN_PYTHON_ORG_ROOT_CERT = os.path.join(
-        os.path.dirname(__file__) or os.curdir,
-        "https_svn_python_org_root.pem")
+    for filename in [
+        CERTFILE, SVN_PYTHON_ORG_ROOT_CERT, BYTES_CERTFILE,
+        ONLYCERT, ONLYKEY, BYTES_ONLYCERT, BYTES_ONLYKEY,
+        BADCERT, BADKEY, EMPTYCERT]:
+        if not os.path.exists(filename):
+            raise support.TestFailed("Can't read certificate file %r" % filename)
 
-    if (not os.path.exists(CERTFILE) or
-        not os.path.exists(SVN_PYTHON_ORG_ROOT_CERT)):
-        raise support.TestFailed("Can't read certificate files!")
-
-    tests = [BasicTests]
+    tests = [ContextTests, BasicSocketTests]
 
     if support.is_resource_enabled('network'):
         tests.append(NetworkedTests)
diff --git a/Misc/NEWS b/Misc/NEWS
index 386e9e0..31adc03 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -363,6 +363,8 @@
 Library
 -------
 
+- Issue #8550: Add first class ``SSLContext`` objects to the ssl module.
+
 - Issue #8681: Make the zlib module's error messages more informative when
   the zlib itself doesn't give any detailed explanation.
 
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index 9ab184b..96d79b3 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -115,23 +115,29 @@
 
 typedef struct {
     PyObject_HEAD
-    PyObject        *Socket;            /* weakref to socket on which we're layered */
-    SSL_CTX*            ctx;
-    SSL*                ssl;
-    X509*               peer_cert;
-    int                 shutdown_seen_zero;
+    SSL_CTX *ctx;
+} PySSLContext;
 
-} PySSLObject;
+typedef struct {
+    PyObject_HEAD
+    PyObject *Socket; /* weakref to socket on which we're layered */
+    SSL *ssl;
+    X509 *peer_cert;
+    int shutdown_seen_zero;
+} PySSLSocket;
 
-static PyTypeObject PySSL_Type;
-static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args);
-static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args);
+static PyTypeObject PySSLContext_Type;
+static PyTypeObject PySSLSocket_Type;
+
+static PyObject *PySSL_SSLwrite(PySSLSocket *self, PyObject *args);
+static PyObject *PySSL_SSLread(PySSLSocket *self, PyObject *args);
 static int check_socket_and_wait_for_timeout(PySocketSockObject *s,
                                              int writing);
-static PyObject *PySSL_peercert(PySSLObject *self, PyObject *args);
-static PyObject *PySSL_cipher(PySSLObject *self);
+static PyObject *PySSL_peercert(PySSLSocket *self, PyObject *args);
+static PyObject *PySSL_cipher(PySSLSocket *self);
 
-#define PySSLObject_Check(v)    (Py_TYPE(v) == &PySSL_Type)
+#define PySSLContext_Check(v)   (Py_TYPE(v) == &PySSLContext_Type)
+#define PySSLSocket_Check(v)    (Py_TYPE(v) == &PySSLSocket_Type)
 
 typedef enum {
     SOCKET_IS_NONBLOCKING,
@@ -154,7 +160,7 @@
 */
 
 static PyObject *
-PySSL_SetError(PySSLObject *obj, int ret, char *filename, int lineno)
+PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
 {
     PyObject *v;
     char buf[2048];
@@ -258,126 +264,28 @@
     return NULL;
 }
 
-static PySSLObject *
-newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file,
-               enum py_ssl_server_or_client socket_type,
-               enum py_ssl_cert_requirements certreq,
-               enum py_ssl_version proto_version,
-               char *cacerts_file, char *ciphers)
+static PySSLSocket *
+newPySSLSocket(SSL_CTX *ctx, PySocketSockObject *sock,
+               enum py_ssl_server_or_client socket_type)
 {
-    PySSLObject *self;
-    char *errstr = NULL;
-    int ret;
-    int verification_mode;
+    PySSLSocket *self;
 
-    self = PyObject_New(PySSLObject, &PySSL_Type); /* Create new object */
+    self = PyObject_New(PySSLSocket, &PySSLSocket_Type);
     if (self == NULL)
         return NULL;
+
     self->peer_cert = NULL;
     self->ssl = NULL;
-    self->ctx = NULL;
     self->Socket = NULL;
 
     /* Make sure the SSL error state is initialized */
     (void) ERR_get_state();
     ERR_clear_error();
 
-    if ((key_file && !cert_file) || (!key_file && cert_file)) {
-        errstr = ERRSTR("Both the key & certificate files "
-                        "must be specified");
-        goto fail;
-    }
-
-    if ((socket_type == PY_SSL_SERVER) &&
-        ((key_file == NULL) || (cert_file == NULL))) {
-        errstr = ERRSTR("Both the key & certificate files "
-                        "must be specified for server-side operation");
-        goto fail;
-    }
-
     PySSL_BEGIN_ALLOW_THREADS
-    if (proto_version == PY_SSL_VERSION_TLS1)
-        self->ctx = SSL_CTX_new(TLSv1_method()); /* Set up context */
-    else if (proto_version == PY_SSL_VERSION_SSL3)
-        self->ctx = SSL_CTX_new(SSLv3_method()); /* Set up context */
-    else if (proto_version == PY_SSL_VERSION_SSL2)
-        self->ctx = SSL_CTX_new(SSLv2_method()); /* Set up context */
-    else if (proto_version == PY_SSL_VERSION_SSL23)
-        self->ctx = SSL_CTX_new(SSLv23_method()); /* Set up context */
+    self->ssl = SSL_new(ctx);
     PySSL_END_ALLOW_THREADS
-
-    if (self->ctx == NULL) {
-        errstr = ERRSTR("Invalid SSL protocol variant specified.");
-        goto fail;
-    }
-
-    if (ciphers != NULL) {
-        ret = SSL_CTX_set_cipher_list(self->ctx, ciphers);
-        if (ret == 0) {
-            errstr = ERRSTR("No cipher can be selected.");
-            goto fail;
-        }
-    }
-
-    if (certreq != PY_SSL_CERT_NONE) {
-        if (cacerts_file == NULL) {
-            errstr = ERRSTR("No root certificates specified for "
-                            "verification of other-side certificates.");
-            goto fail;
-        } else {
-            PySSL_BEGIN_ALLOW_THREADS
-            ret = SSL_CTX_load_verify_locations(self->ctx,
-                                                cacerts_file,
-                                                NULL);
-            PySSL_END_ALLOW_THREADS
-            if (ret != 1) {
-                _setSSLError(NULL, 0, __FILE__, __LINE__);
-                goto fail;
-            }
-        }
-    }
-    if (key_file) {
-        PySSL_BEGIN_ALLOW_THREADS
-        ret = SSL_CTX_use_PrivateKey_file(self->ctx, key_file,
-                                          SSL_FILETYPE_PEM);
-        PySSL_END_ALLOW_THREADS
-        if (ret != 1) {
-            _setSSLError(NULL, ret, __FILE__, __LINE__);
-            goto fail;
-        }
-
-        PySSL_BEGIN_ALLOW_THREADS
-        ret = SSL_CTX_use_certificate_chain_file(self->ctx,
-                                                 cert_file);
-        PySSL_END_ALLOW_THREADS
-        if (ret != 1) {
-            /*
-            fprintf(stderr, "ret is %d, errcode is %lu, %lu, with file \"%s\"\n",
-                ret, ERR_peek_error(), ERR_peek_last_error(), cert_file);
-                */
-            if (ERR_peek_last_error() != 0) {
-                _setSSLError(NULL, ret, __FILE__, __LINE__);
-                goto fail;
-            }
-        }
-    }
-
-    /* ssl compatibility */
-    SSL_CTX_set_options(self->ctx, SSL_OP_ALL);
-
-    verification_mode = SSL_VERIFY_NONE;
-    if (certreq == PY_SSL_CERT_OPTIONAL)
-        verification_mode = SSL_VERIFY_PEER;
-    else if (certreq == PY_SSL_CERT_REQUIRED)
-        verification_mode = (SSL_VERIFY_PEER |
-                             SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
-    SSL_CTX_set_verify(self->ctx, verification_mode,
-                       NULL); /* set verify lvl */
-
-    PySSL_BEGIN_ALLOW_THREADS
-    self->ssl = SSL_new(self->ctx); /* New ssl struct */
-    PySSL_END_ALLOW_THREADS
-    SSL_set_fd(self->ssl, Sock->sock_fd);       /* Set the socket for SSL */
+    SSL_set_fd(self->ssl, sock->sock_fd);
 #ifdef SSL_MODE_AUTO_RETRY
     SSL_set_mode(self->ssl, SSL_MODE_AUTO_RETRY);
 #endif
@@ -385,8 +293,7 @@
     /* If the socket is in non-blocking mode or timeout mode, set the BIO
      * to non-blocking mode (blocking is the default)
      */
-    if (Sock->sock_timeout >= 0.0) {
-        /* Set both the read and write BIO's to non-blocking mode */
+    if (sock->sock_timeout >= 0.0) {
         BIO_set_nbio(SSL_get_rbio(self->ssl), 1);
         BIO_set_nbio(SSL_get_wbio(self->ssl), 1);
     }
@@ -398,57 +305,13 @@
         SSL_set_accept_state(self->ssl);
     PySSL_END_ALLOW_THREADS
 
-    self->Socket = PyWeakref_NewRef((PyObject *) Sock, Py_None);
+    self->Socket = PyWeakref_NewRef((PyObject *) sock, NULL);
     return self;
- fail:
-    if (errstr)
-        PyErr_SetString(PySSLErrorObject, errstr);
-    Py_DECREF(self);
-    return NULL;
 }
 
-static PyObject *
-PySSL_sslwrap(PyObject *self, PyObject *args)
-{
-    PySocketSockObject *Sock;
-    int server_side = 0;
-    int verification_mode = PY_SSL_CERT_NONE;
-    int protocol = PY_SSL_VERSION_SSL23;
-    char *key_file = NULL;
-    char *cert_file = NULL;
-    char *cacerts_file = NULL;
-    char *ciphers = NULL;
-
-    if (!PyArg_ParseTuple(args, "O!i|zziizz:sslwrap",
-                          PySocketModule.Sock_Type,
-                          &Sock,
-                          &server_side,
-                          &key_file, &cert_file,
-                          &verification_mode, &protocol,
-                          &cacerts_file, &ciphers))
-        return NULL;
-
-    /*
-    fprintf(stderr,
-        "server_side is %d, keyfile %p, certfile %p, verify_mode %d, "
-        "protocol %d, certs %p\n",
-        server_side, key_file, cert_file, verification_mode,
-        protocol, cacerts_file);
-     */
-
-    return (PyObject *) newPySSLObject(Sock, key_file, cert_file,
-                                       server_side, verification_mode,
-                                       protocol, cacerts_file,
-                                       ciphers);
-}
-
-PyDoc_STRVAR(ssl_doc,
-"sslwrap(socket, server_side, [keyfile, certfile, certs_mode, protocol,\n"
-"                              cacertsfile, ciphers]) -> sslobject");
-
 /* SSL object methods */
 
-static PyObject *PySSL_SSLdo_handshake(PySSLObject *self)
+static PyObject *PySSL_SSLdo_handshake(PySSLSocket *self)
 {
     int ret;
     int err;
@@ -986,7 +849,7 @@
 
 
 static PyObject *
-PySSL_peercert(PySSLObject *self, PyObject *args)
+PySSL_peercert(PySSLSocket *self, PyObject *args)
 {
     PyObject *retval = NULL;
     int len;
@@ -1017,8 +880,7 @@
         return retval;
 
     } else {
-
-        verification = SSL_CTX_get_verify_mode(self->ctx);
+        verification = SSL_CTX_get_verify_mode(SSL_get_SSL_CTX(self->ssl));
         if ((verification & SSL_VERIFY_PEER) == 0)
             return PyDict_New();
         else
@@ -1038,7 +900,7 @@
 peer certificate, or None if no certificate was provided.  This will\n\
 return the certificate even if it wasn't validated.");
 
-static PyObject *PySSL_cipher (PySSLObject *self) {
+static PyObject *PySSL_cipher (PySSLSocket *self) {
 
     PyObject *retval, *v;
     SSL_CIPHER *current;
@@ -1084,14 +946,12 @@
     return NULL;
 }
 
-static void PySSL_dealloc(PySSLObject *self)
+static void PySSL_dealloc(PySSLSocket *self)
 {
     if (self->peer_cert)        /* Possible not to have one? */
         X509_free (self->peer_cert);
     if (self->ssl)
         SSL_free(self->ssl);
-    if (self->ctx)
-        SSL_CTX_free(self->ctx);
     Py_XDECREF(self->Socket);
     PyObject_Del(self);
 }
@@ -1166,7 +1026,7 @@
     return rc == 0 ? SOCKET_HAS_TIMED_OUT : SOCKET_OPERATION_OK;
 }
 
-static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args)
+static PyObject *PySSL_SSLwrite(PySSLSocket *self, PyObject *args)
 {
     Py_buffer buf;
     int len;
@@ -1250,7 +1110,7 @@
 Writes the string s into the SSL object.  Returns the number\n\
 of bytes written.");
 
-static PyObject *PySSL_SSLpending(PySSLObject *self)
+static PyObject *PySSL_SSLpending(PySSLSocket *self)
 {
     int count = 0;
 
@@ -1269,7 +1129,7 @@
 Returns the number of already decrypted bytes available for read,\n\
 pending on the connection.\n");
 
-static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args)
+static PyObject *PySSL_SSLread(PySSLSocket *self, PyObject *args)
 {
     PyObject *dest = NULL;
     Py_buffer buf;
@@ -1392,7 +1252,7 @@
 \n\
 Read up to len bytes from the SSL socket.");
 
-static PyObject *PySSL_SSLshutdown(PySSLObject *self)
+static PyObject *PySSL_SSLshutdown(PySSLSocket *self)
 {
     int err, ssl_err, sockstate, nonblocking;
     int zeros = 0;
@@ -1497,10 +1357,10 @@
     {NULL, NULL}
 };
 
-static PyTypeObject PySSL_Type = {
+static PyTypeObject PySSLSocket_Type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "ssl.SSLContext",                   /*tp_name*/
-    sizeof(PySSLObject),                /*tp_basicsize*/
+    "_ssl._SSLSocket",                  /*tp_name*/
+    sizeof(PySSLSocket),                /*tp_basicsize*/
     0,                                  /*tp_itemsize*/
     /* methods */
     (destructor)PySSL_dealloc,          /*tp_dealloc*/
@@ -1529,6 +1389,306 @@
     PySSLMethods,                       /*tp_methods*/
 };
 
+
+/*
+ * _SSLContext objects
+ */
+
+static PyObject *
+context_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    char *kwlist[] = {"protocol", NULL};
+    PySSLContext *self;
+    int proto_version = PY_SSL_VERSION_SSL23;
+    SSL_CTX *ctx = NULL;
+
+    if (!PyArg_ParseTupleAndKeywords(
+        args, kwds, "i:_SSLContext", kwlist,
+        &proto_version))
+        return NULL;
+
+    PySSL_BEGIN_ALLOW_THREADS
+    if (proto_version == PY_SSL_VERSION_TLS1)
+        ctx = SSL_CTX_new(TLSv1_method());
+    else if (proto_version == PY_SSL_VERSION_SSL3)
+        ctx = SSL_CTX_new(SSLv3_method());
+    else if (proto_version == PY_SSL_VERSION_SSL2)
+        ctx = SSL_CTX_new(SSLv2_method());
+    else if (proto_version == PY_SSL_VERSION_SSL23)
+        ctx = SSL_CTX_new(SSLv23_method());
+    else
+        proto_version = -1;
+    PySSL_END_ALLOW_THREADS
+
+    if (proto_version == -1) {
+        PyErr_SetString(PyExc_ValueError,
+                        "invalid protocol version");
+        return NULL;
+    }
+    if (ctx == NULL) {
+        PyErr_SetString(PySSLErrorObject,
+                        "failed to allocate SSL context");
+        return NULL;
+    }
+
+    assert(type != NULL && type->tp_alloc != NULL);
+    self = (PySSLContext *) type->tp_alloc(type, 0);
+    if (self == NULL) {
+        SSL_CTX_free(ctx);
+        return NULL;
+    }
+    self->ctx = ctx;
+    /* Defaults */
+    SSL_CTX_set_verify(self->ctx, SSL_VERIFY_NONE, NULL);
+    SSL_CTX_set_options(self->ctx, SSL_OP_ALL);
+
+    return (PyObject *)self;
+}
+
+static void
+context_dealloc(PySSLContext *self)
+{
+    SSL_CTX_free(self->ctx);
+    Py_TYPE(self)->tp_free(self);
+}
+
+static PyObject *
+set_ciphers(PySSLContext *self, PyObject *args)
+{
+    int ret;
+    const char *cipherlist;
+
+    if (!PyArg_ParseTuple(args, "s:set_ciphers", &cipherlist))
+        return NULL;
+    ret = SSL_CTX_set_cipher_list(self->ctx, cipherlist);
+    if (ret == 0) {
+        PyErr_SetString(PySSLErrorObject,
+                        "No cipher can be selected.");
+        return NULL;
+    }
+    Py_RETURN_NONE;
+}
+
+static PyObject *
+get_verify_mode(PySSLContext *self, void *c)
+{
+    switch (SSL_CTX_get_verify_mode(self->ctx)) {
+    case SSL_VERIFY_NONE:
+        return PyLong_FromLong(PY_SSL_CERT_NONE);
+    case SSL_VERIFY_PEER:
+        return PyLong_FromLong(PY_SSL_CERT_OPTIONAL);
+    case SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT:
+        return PyLong_FromLong(PY_SSL_CERT_REQUIRED);
+    }
+    PyErr_SetString(PySSLErrorObject,
+                    "invalid return value from SSL_CTX_get_verify_mode");
+    return NULL;
+}
+
+static int
+set_verify_mode(PySSLContext *self, PyObject *arg, void *c)
+{
+    int n, mode;
+    if (!PyArg_Parse(arg, "i", &n))
+        return -1;
+    if (n == PY_SSL_CERT_NONE)
+        mode = SSL_VERIFY_NONE;
+    else if (n == PY_SSL_CERT_OPTIONAL)
+        mode = SSL_VERIFY_PEER;
+    else if (n == PY_SSL_CERT_REQUIRED)
+        mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+    else {
+        PyErr_SetString(PyExc_ValueError,
+                        "invalid value for verify_mode");
+        return -1;
+    }
+    SSL_CTX_set_verify(self->ctx, mode, NULL);
+    return 0;
+}
+
+static PyObject *
+load_cert_chain(PySSLContext *self, PyObject *args, PyObject *kwds)
+{
+    char *kwlist[] = {"certfile", "keyfile", NULL};
+    PyObject *certfile, *keyfile = NULL;
+    PyObject *certfile_bytes = NULL, *keyfile_bytes = NULL;
+    int r;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds,
+        "O|O:load_cert_chain", kwlist,
+        &certfile, &keyfile))
+        return NULL;
+    if (keyfile == Py_None)
+        keyfile = NULL;
+    if (!PyUnicode_FSConverter(certfile, &certfile_bytes)) {
+        PyErr_SetString(PyExc_TypeError,
+                        "certfile should be a valid filesystem path");
+        return NULL;
+    }
+    if (keyfile && !PyUnicode_FSConverter(keyfile, &keyfile_bytes)) {
+        PyErr_SetString(PyExc_TypeError,
+                        "keyfile should be a valid filesystem path");
+        goto error;
+    }
+    PySSL_BEGIN_ALLOW_THREADS
+    r = SSL_CTX_use_certificate_chain_file(self->ctx,
+        PyBytes_AS_STRING(certfile_bytes));
+    PySSL_END_ALLOW_THREADS
+    if (r != 1) {
+        _setSSLError(NULL, 0, __FILE__, __LINE__);
+        goto error;
+    }
+    PySSL_BEGIN_ALLOW_THREADS
+    r = SSL_CTX_use_RSAPrivateKey_file(self->ctx,
+        PyBytes_AS_STRING(keyfile ? keyfile_bytes : certfile_bytes),
+        SSL_FILETYPE_PEM);
+    PySSL_END_ALLOW_THREADS
+    Py_XDECREF(keyfile_bytes);
+    Py_XDECREF(certfile_bytes);
+    if (r != 1) {
+        _setSSLError(NULL, 0, __FILE__, __LINE__);
+        return NULL;
+    }
+    PySSL_BEGIN_ALLOW_THREADS
+    r = SSL_CTX_check_private_key(self->ctx);
+    PySSL_END_ALLOW_THREADS
+    if (r != 1) {
+        _setSSLError(NULL, 0, __FILE__, __LINE__);
+        return NULL;
+    }
+    Py_RETURN_NONE;
+
+error:
+    Py_XDECREF(keyfile_bytes);
+    Py_XDECREF(certfile_bytes);
+    return NULL;
+}
+
+static PyObject *
+load_verify_locations(PySSLContext *self, PyObject *args, PyObject *kwds)
+{
+    char *kwlist[] = {"cafile", "capath", NULL};
+    PyObject *cafile = NULL, *capath = NULL;
+    PyObject *cafile_bytes = NULL, *capath_bytes = NULL;
+    const char *cafile_buf = NULL, *capath_buf = NULL;
+    int r;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds,
+        "|OO:load_verify_locations", kwlist,
+        &cafile, &capath))
+        return NULL;
+    if (cafile == Py_None)
+        cafile = NULL;
+    if (capath == Py_None)
+        capath = NULL;
+    if (cafile == NULL && capath == NULL) {
+        PyErr_SetString(PyExc_TypeError,
+                        "cafile and capath cannot be both omitted");
+        return NULL;
+    }
+    if (cafile && !PyUnicode_FSConverter(cafile, &cafile_bytes)) {
+        PyErr_SetString(PyExc_TypeError,
+                        "cafile should be a valid filesystem path");
+        return NULL;
+    }
+    if (capath && !PyUnicode_FSConverter(capath, &capath_bytes)) {
+        Py_DECREF(cafile_bytes);
+        PyErr_SetString(PyExc_TypeError,
+                        "capath should be a valid filesystem path");
+        return NULL;
+    }
+    if (cafile)
+        cafile_buf = PyBytes_AS_STRING(cafile_bytes);
+    if (capath)
+        capath_buf = PyBytes_AS_STRING(capath_bytes);
+    PySSL_BEGIN_ALLOW_THREADS
+    r = SSL_CTX_load_verify_locations(self->ctx, cafile_buf, capath_buf);
+    PySSL_END_ALLOW_THREADS
+    Py_XDECREF(cafile_bytes);
+    Py_XDECREF(capath_bytes);
+    if (r != 1) {
+        _setSSLError(NULL, 0, __FILE__, __LINE__);
+        return NULL;
+    }
+    Py_RETURN_NONE;
+}
+
+static PyObject *
+context_wrap_socket(PySSLContext *self, PyObject *args, PyObject *kwds)
+{
+    char *kwlist[] = {"sock", "server_side", NULL};
+    PySocketSockObject *sock;
+    int server_side = 0;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!i:_wrap_socket", kwlist,
+                                     PySocketModule.Sock_Type,
+                                     &sock, &server_side))
+        return NULL;
+
+    return (PyObject *) newPySSLSocket(self->ctx, sock, server_side);
+}
+
+static PyGetSetDef context_getsetlist[] = {
+    {"verify_mode", (getter) get_verify_mode,
+                    (setter) set_verify_mode, NULL},
+    {NULL},            /* sentinel */
+};
+
+static struct PyMethodDef context_methods[] = {
+    {"_wrap_socket", (PyCFunction) context_wrap_socket,
+                       METH_VARARGS | METH_KEYWORDS, NULL},
+    {"set_ciphers", (PyCFunction) set_ciphers,
+                    METH_VARARGS, NULL},
+    {"load_cert_chain", (PyCFunction) load_cert_chain,
+                        METH_VARARGS | METH_KEYWORDS, NULL},
+    {"load_verify_locations", (PyCFunction) load_verify_locations,
+                              METH_VARARGS | METH_KEYWORDS, NULL},
+    {NULL, NULL}        /* sentinel */
+};
+
+static PyTypeObject PySSLContext_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "_ssl._SSLContext",                        /*tp_name*/
+    sizeof(PySSLContext),                      /*tp_basicsize*/
+    0,                                         /*tp_itemsize*/
+    (destructor)context_dealloc,               /*tp_dealloc*/
+    0,                                         /*tp_print*/
+    0,                                         /*tp_getattr*/
+    0,                                         /*tp_setattr*/
+    0,                                         /*tp_reserved*/
+    0,                                         /*tp_repr*/
+    0,                                         /*tp_as_number*/
+    0,                                         /*tp_as_sequence*/
+    0,                                         /*tp_as_mapping*/
+    0,                                         /*tp_hash*/
+    0,                                         /*tp_call*/
+    0,                                         /*tp_str*/
+    0,                                         /*tp_getattro*/
+    0,                                         /*tp_setattro*/
+    0,                                         /*tp_as_buffer*/
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /*tp_flags*/
+    0,                                         /*tp_doc*/
+    0,                                         /*tp_traverse*/
+    0,                                         /*tp_clear*/
+    0,                                         /*tp_richcompare*/
+    0,                                         /*tp_weaklistoffset*/
+    0,                                         /*tp_iter*/
+    0,                                         /*tp_iternext*/
+    context_methods,                           /*tp_methods*/
+    0,                                         /*tp_members*/
+    context_getsetlist,                        /*tp_getset*/
+    0,                                         /*tp_base*/
+    0,                                         /*tp_dict*/
+    0,                                         /*tp_descr_get*/
+    0,                                         /*tp_descr_set*/
+    0,                                         /*tp_dictoffset*/
+    0,                                         /*tp_init*/
+    0,                                         /*tp_alloc*/
+    context_new,                               /*tp_new*/
+};
+
+
+
 #ifdef HAVE_OPENSSL_RAND
 
 /* helper routines for seeding the SSL PRNG */
@@ -1598,8 +1758,6 @@
 /* List of functions exported by this module. */
 
 static PyMethodDef PySSL_methods[] = {
-    {"sslwrap",             PySSL_sslwrap,
-     METH_VARARGS, ssl_doc},
     {"_test_decode_cert",       PySSL_test_decode_certificate,
      METH_VARARGS},
 #ifdef HAVE_OPENSSL_RAND
@@ -1708,7 +1866,9 @@
     unsigned int major, minor, fix, patch, status;
     PySocketModule_APIObject *socket_api;
 
-    if (PyType_Ready(&PySSL_Type) < 0)
+    if (PyType_Ready(&PySSLContext_Type) < 0)
+        return NULL;
+    if (PyType_Ready(&PySSLSocket_Type) < 0)
         return NULL;
 
     m = PyModule_Create(&_sslmodule);
@@ -1741,8 +1901,11 @@
         return NULL;
     if (PyDict_SetItemString(d, "SSLError", PySSLErrorObject) != 0)
         return NULL;
-    if (PyDict_SetItemString(d, "SSLType",
-                             (PyObject *)&PySSL_Type) != 0)
+    if (PyDict_SetItemString(d, "_SSLContext",
+                             (PyObject *)&PySSLContext_Type) != 0)
+        return NULL;
+    if (PyDict_SetItemString(d, "_SSLSocket",
+                             (PyObject *)&PySSLSocket_Type) != 0)
         return NULL;
     PyModule_AddIntConstant(m, "SSL_ERROR_ZERO_RETURN",
                             PY_SSL_ERROR_ZERO_RETURN);