def wrap_socket(socket, certfile, keyfile, password=None):
    """
    Wraps an existing TCP socket and returns an SSLSocket object

    :param socket: The socket to wrap
    :param certfile: The server certificate file
    :param keyfile: The server private key file
    :param password: Password for the private key file (Python >= 3.3)
    :return: The wrapped socket
    :raise SSLError: Error wrapping the socket / loading the certificate
    :raise OSError: A password has been given, but ciphered key files are not
                    supported by the current version of Python
    """
    # Log warnings when some
    logger = logging.getLogger("ssl_wrap")

    def _password_support_error():
        """
        Logs a warning and raises an OSError if a password has been given but
        Python doesn't support ciphered key files.

        :raise OSError: If a password has been given
        """
        if password:
            logger.error(
                "The ssl.wrap_socket() fallback method doesn't "
                "support key files with a password."
            )
            raise OSError(
                "Can't decode the SSL key file: "
                "this version of Python doesn't support it"
            )

    try:
        # Prefer the default context factory, as it will be updated to reflect
        # security issues (Python >= 2.7.9 and >= 3.4)
        default_context = ssl.create_default_context()
    except AttributeError:
        default_context = None

    try:
        # Try to equivalent to create_default_context() in Python 3.5
        # Create an SSL context and set its options
        context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)

        if default_context is not None:
            # Copy options
            context.options = default_context.options
        else:
            # Set up the context as create_default_context() does in Python 3.5
            # SSLv2 considered harmful
            # SSLv3 has problematic security
            context.options |= ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3

        # disallow ciphers with known vulnerabilities
        context.set_ciphers(_RESTRICTED_SERVER_CIPHERS)

        try:
            # Load the certificate, with a password
            context.load_cert_chain(certfile, keyfile, password)
        except TypeError:
            # The "password" argument isn't supported
            # Check support for key file password
            _password_support_error()

            # Load the certificate, without the password argument
            context.load_cert_chain(certfile, keyfile)

        # Return the wrapped socket
        return context.wrap_socket(socket, server_side=True)

    except AttributeError as ex:
        # Log a warning to advise the user of possible security holes
        logger.warning(
            "Can't create a custom SSLContext. "
            "The server should be considered insecure."
        )
        logger.debug("Missing attribute: %s", ex)

    # Check support for key file password
    _password_support_error()

    # Fall back to the "old" wrap_socket method
    return ssl.wrap_socket(
        socket, server_side=True, certfile=certfile, keyfile=keyfile
    )
Wraps an existing TCP socket and returns an SSLSocket object

    :param socket: The socket to wrap
    :param certfile: The server certificate file
    :param keyfile: The server private key file
    :param password: Password for the private key file (Python >= 3.3)
    :return: The wrapped socket
    :raise SSLError: Error wrapping the socket / loading the certificate
    :raise OSError: A password has been given, but ciphered key files are not
                    supported by the current version of Python