示例#1
0
def create_cert_req(keyType = crypto.TYPE_RSA,
                    bits = 1024,
                    messageDigest = "md5"):
    """
    Create certificate request.
    
    Returns: certificate request PEM text, private key PEM text
    """

    # Create certificate request
    req = crypto.X509Req()

    # Generate private key
    pkey = crypto.PKey()
    pkey.generate_key(keyType, bits)

    req.set_pubkey(pkey)
    req.sign(pkey, messageDigest)
    
    cert_req_pem = crypto.dump_certificate_request(crypto.FILETYPE_ASN1,req)
    key_pem = crypto.dump_privatekey(crypto.FILETYPE_PEM,pkey)

    # Nasty OpenSSL 1.0 Hack
    # OpenSSL 1.0 changes the headers from "RSA PRIVATE KEY" to "PRIVATE KEY"
    try:
        ssl_version = SSL.SSLeay_version(SSL.SSLEAY_VERSION)
        logger.debug('Using SSL: ' + ssl_version)
        if ssl_version.startswith("OpenSSL 1"):
            key_pem = re.sub(r'BEGIN PRIVATE KEY', r'BEGIN RSA PRIVATE KEY', key_pem)
            key_pem = re.sub(r'END PRIVATE KEY', r'END RSA PRIVATE KEY', key_pem)
    except Exception, e:
        logger.warn('Using older version of openSSL without SSLeay_version: %s' + e)
    def assertTlsHandshakeFailure(self,
                                  client_cert,
                                  client_key,
                                  ciphers=None,
                                  ssl_method=None):
        """
    Checks that the TLS handshake failure by varying the given parameters
    Args:
      client_cert: client certificate file in PEM format to use.
      client_key: associated key file in PEM format to use with the
        given |client_cert|.
      ciphers: optional cipher method
      ssl_method: optional ssl_method
    """
        url = urlparse.urlparse('https://' + self._sas_admin._base_url)
        client = socket.socket()
        client.connect((url.hostname, url.port or 443))
        logging.debug("OPENSSL version: %s" %
                      SSL.SSLeay_version(SSL.SSLEAY_VERSION))
        logging.debug('TLS handshake: connecting to: %s:%d', url.hostname,
                      url.port or 443)
        logging.debug('TLS handshake: privatekey_file=%s', client_key)
        logging.debug('TLS handshake: certificate_file=%s', client_cert)
        if ssl_method is not None:
            ctx = SSL.Context(ssl_method)
        else:
            ctx = SSL.Context(SSL.TLSv1_2_METHOD)
        if ciphers is not None:
            ctx.set_cipher_list(ciphers)
        else:
            # cipher 'AES128-GCM-SHA256' will be added by default if cipher arg is passed as None
            ctx.set_cipher_list(self._sas._tls_config.ciphers[0])

        ctx.use_certificate_file(client_cert)
        ctx.use_privatekey_file(client_key)

        client_ssl_informations = []

        def _InfoCb(conn, where, ok):
            client_ssl_informations.append(conn.get_state_string())
            logging.debug('TLS handshake info: %d|%d %s', where, ok,
                          conn.get_state_string())
            return ok

        ctx.set_info_callback(_InfoCb)

        client_ssl = SSL.Connection(ctx, client)
        client_ssl.set_connect_state()
        client_ssl.set_tlsext_host_name(url.hostname)

        try:
            client_ssl.do_handshake()
            logging.debug('TLS handshake: succeed')
            self.fail(
                msg=
                "TLS Handshake is success. but Expected:TLS handshake failure")
        except SSL.Error as e:
            self.assertEquals(client_ssl.get_peer_finished(), None)
        finally:
            client_ssl.close()
示例#3
0
    def get_environ(self):
        """Return WSGI environ entries to be merged into each request."""
        ssl_environ = {
            'wsgi.url_scheme':
            'https',
            'HTTPS':
            'on',
            'SSL_VERSION_INTERFACE':
            '%s %s/%s Python/%s' % (
                cheroot_server.HTTPServer.version,
                OpenSSL.version.__title__,
                OpenSSL.version.__version__,
                sys.version,
            ),
            'SSL_VERSION_LIBRARY':
            SSL.SSLeay_version(SSL.SSLEAY_VERSION, ).decode(),
        }

        if self.certificate:
            # Server certificate attributes
            with open(self.certificate, 'rb') as cert_file:
                cert = crypto.load_certificate(
                    crypto.FILETYPE_PEM,
                    cert_file.read(),
                )

            ssl_environ.update({
                'SSL_SERVER_M_VERSION': cert.get_version(),
                'SSL_SERVER_M_SERIAL': cert.get_serial_number(),
                # 'SSL_SERVER_V_START':
                #   Validity of server's certificate (start time),
                # 'SSL_SERVER_V_END':
                #   Validity of server's certificate (end time),
            })

            for prefix, dn in [
                ('I', cert.get_issuer()),
                ('S', cert.get_subject()),
            ]:
                # X509Name objects don't seem to have a way to get the
                # complete DN string. Use str() and slice it instead,
                # because str(dn) == "<X509Name object '/C=US/ST=...'>"
                dnstr = str(dn)[18:-2]

                wsgikey = 'SSL_SERVER_%s_DN' % prefix
                ssl_environ[wsgikey] = dnstr

                # The DN should be of the form: /k1=v1/k2=v2, but we must allow
                # for any value to contain slashes itself (in a URL).
                while dnstr:
                    pos = dnstr.rfind('=')
                    dnstr, value = dnstr[:pos], dnstr[pos + 1:]
                    pos = dnstr.rfind('/')
                    dnstr, key = dnstr[:pos], dnstr[pos + 1:]
                    if key and value:
                        wsgikey = 'SSL_SERVER_%s_DN_%s' % (prefix, key)
                        ssl_environ[wsgikey] = value

        return ssl_environ
示例#4
0
def dump_system_info():
    mitmproxy_version = version.get_dev_version()

    data = [
        f"Mitmproxy: {mitmproxy_version}",
        f"Python:    {platform.python_version()}",
        "OpenSSL:   {}".format(SSL.SSLeay_version(SSL.SSLEAY_VERSION).decode()),
        f"Platform:  {platform.platform()}",
    ]
    return "\n".join(data)
def dump_system_info():
    git_describe = 'release version'
    with utils.chdir(
            os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))):
        try:
            c = ['git', 'describe', '--tags', '--long']
            git_describe = subprocess.check_output(c, stderr=subprocess.STDOUT)
            last_tag, tag_dist, commit = git_describe.decode().strip().rsplit(
                "-", 2)

            if last_tag.startswith('v'):
                # remove the 'v' prefix
                last_tag = last_tag[1:]
            if commit.startswith('g'):
                # remove the 'g' prefix added by recent git versions
                commit = commit[1:]

            # build the same version specifier as used for snapshots by rtool
            git_describe = "{version}dev{tag:04}-0x{commit}".format(
                version=last_tag,
                tag=int(tag_dist),
                commit=commit,
            )
        except:
            pass

    bin_indicator = ""  # PyInstaller builds indicator, if using precompiled binary
    if getattr(sys, 'frozen', False):
        bin_indicator = "Precompiled Binary"

    data = [
        "Mitmproxy version: {} ({}) {}".format(version.VERSION, git_describe,
                                               bin_indicator),
        "Python version: {}".format(platform.python_version()),
        "Platform: {}".format(platform.platform()),
        "SSL version: {}".format(
            SSL.SSLeay_version(SSL.SSLEAY_VERSION).decode()),
    ]
    d = platform.linux_distribution()
    t = "Linux distro: %s %s %s" % d
    if d[0]:  # pragma: no cover
        data.append(t)

    d = platform.mac_ver()
    t = "Mac version: %s %s %s" % d
    if d[0]:  # pragma: no cover
        data.append(t)

    d = platform.win32_ver()
    t = "Windows version: %s %s %s %s" % d
    if d[0]:  # pragma: no cover
        data.append(t)

    return "\n".join(data)
示例#6
0
def ssl_version():
    if SSL:
        try:
            return SSL.SSLeay_version(SSL.SSLEAY_VERSION)
        except AttributeError:
            try:
                import ssl
                return ssl.OPENSSL_VERSION
            except (ImportError, AttributeError):
                return 'No OpenSSL installed'
    else:
        return None
示例#7
0
def dump_system_info():
    mitmproxy_version = version.VERSION
    here = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
    try:
        git_describe = subprocess.check_output(
            ['git', 'describe', '--tags', '--long'],
            stderr=subprocess.STDOUT,
            cwd=here,
        )
    except:
        pass
    else:
        last_tag, tag_dist, commit = git_describe.decode().strip().rsplit(
            "-", 2)

        commit = commit.lstrip(
            "g")  # remove the 'g' prefix added by recent git versions
        tag_dist = int(tag_dist)

        if tag_dist > 0:
            tag_dist = "dev{:04}".format(tag_dist)
        else:
            tag_dist = ""

        mitmproxy_version += "{tag_dist} ({commit})".format(
            tag_dist=tag_dist,
            commit=commit,
        )

    # PyInstaller builds indicator, if using precompiled binary
    if getattr(sys, 'frozen', False):
        bin_indicator = "binary"
    else:
        bin_indicator = ""

    data = [
        "Mitmproxy: {} {}".format(mitmproxy_version, bin_indicator),
        "Python:    {}".format(platform.python_version()),
        "OpenSSL:   {}".format(
            SSL.SSLeay_version(SSL.SSLEAY_VERSION).decode()),
        "Platform:  {}".format(platform.platform()),
    ]
    return "\n".join(data)
示例#8
0
def dump_system_info():
    mitmproxy_version = version.get_version(True, True)
    mitmproxy_version = re.sub(r"-0x([0-9a-f]+)", r" (commit \1)",
                               mitmproxy_version)

    # PyInstaller builds indicator, if using precompiled binary
    if getattr(sys, 'frozen', False):
        bin_indicator = "binary"
    else:
        bin_indicator = ""

    data = [
        "Mitmproxy: {} {}".format(mitmproxy_version, bin_indicator),
        "Python:    {}".format(platform.python_version()),
        "OpenSSL:   {}".format(
            SSL.SSLeay_version(SSL.SSLEAY_VERSION).decode()),
        "Platform:  {}".format(platform.platform()),
    ]
    return "\n".join(data)
示例#9
0
def sysinfo():
    data = [
        "Mitmproxy version: %s" % version.VERSION,
        "Python version: %s" % platform.python_version(),
        "Platform: %s" % platform.platform(),
        "SSL version: %s" % SSL.SSLeay_version(SSL.SSLEAY_VERSION).decode(),
    ]
    d = platform.linux_distribution()
    t = "Linux distro: %s %s %s" % d
    if d[0]:  # pragma: no-cover
        data.append(t)

    d = platform.mac_ver()
    t = "Mac version: %s %s %s" % d
    if d[0]:  # pragma: no-cover
        data.append(t)

    d = platform.win32_ver()
    t = "Windows version: %s %s %s %s" % d
    if d[0]:  # pragma: no-cover
        data.append(t)

    return "\n".join(data)
示例#10
0
import six

import flextls

openssl_enabled = False
version_pyopenssl = None
version_openssl = None
try:
    import OpenSSL
    from OpenSSL import SSL
    version_pyopenssl = OpenSSL.__version__
    version_openssl = SSL.SSLeay_version(0)
    if isinstance(version_openssl, six.binary_type):
        version_openssl = version_openssl.decode('ascii')
except ImportError:
    OpenSSL = None


def convert_version2method(protocol_version):
    """
    Convert internal protocol version ID to OpenSSL method.

    :param Integer protocol_version: Version ID
    :return: OpenSSL method or None if not found
    :rtype: OpenSSL method or None
    """
    if protocol_version == flextls.registry.version.SSLv2:
        return SSL.SSLv2_METHOD
    if protocol_version == flextls.registry.version.SSLv3:
        return SSL.SSLv3_METHOD
    if protocol_version == flextls.registry.version.TLSv10:
示例#11
0
def main():
    arguments = docopt(__doc__, version=__version__)
    if arguments['info']:
        return info(arguments)

    # Get the address from the user.
    server = arguments['<host>']
    port   = 443
    if ':' in server:
        server, port = server.split(':', 1)
        port = int(port)

    threads = arguments['--threads']
    if threads is None:
        threads = 5
    else:
        threads = int(threads)

    ssl_version = SSL.SSLeay_version(SSL.SSLEAY_VERSION)
    if not isinstance(ssl_version, str):
        ssl_version = ssl_version.decode('ascii')

    print_box = lambda s, **kw: print('| ' + s.ljust(59) + '|', **kw)
    print("+" + "-" * 60 + "+")
    print_box("pySSLScan version %s" % (__version__,))
    print_box("  OpenSSL version: %s" % (ssl_version,))
    print_box("  Threads:       : %d" % (threads,))
    print_box("  Verbose:       : %s" % (bool(arguments['-v']),))
    print("+" + "-" * 60 + "+")
    print('')

    # Create host structure.
    host = HostInfo(server, port)

    # Note that this statement will wait for all executed things to finish.
    print("Scanning, please wait... ", end='')
    sys.stdout.flush()
    with futures.ThreadPoolExecutor(max_workers=threads) as executor:
        # Validate certificate chain for the server.
        executor.submit(validate_cert_chain, host)

        for method in SSL_METHODS:
            try:
                supported_ciphers = get_all_ciphers(method)
            except ValueError:
                print("Method not supported: %s" % (method,))
                continue

            # Test each individual cipher.
            for cipher in supported_ciphers:
                executor.submit(test_single_cipher, host, method, cipher)

            # Test for the preferred cipher suite for this method.
            executor.submit(test_preferred_cipher, host, method)

    print('done!\n')

    # Print results.
    for method in SSL_METHODS:
        print("Ciphers for %s:" % (method,))
        print("-" * 20)

        for cipher in sorted(host.accepted_ciphers_for(method)):
            print(' ' + cipher_as_str(cipher))

        if arguments['-v']:
            for cipher in sorted(host.rejected_ciphers_for(method)):
                print(' (rejected) ' + cipher_as_str(cipher))
            for cipher in sorted(host.errored_ciphers_for(method)):
                print(' (errored) ' + cipher_as_str(cipher))

        print('')

    print('Preferred Ciphers:')
    print('-' * 20)
    for method in SSL_METHODS:
        preferred = host.preferred_ciphers.get(method)
        if preferred is not None:
            print(" %s: %s" % (method, cipher_as_str(preferred)))
        else:
            print(" %s: None" % (method,))
    print('')

    # Lastly, print any problems found
    print('Problems:')
    print('-' * 20)

    if len(list(host.accepted_ciphers_for('SSLv2'))) > 0:
        print("- Host supports SSLv2")

    anon_ciphers = []
    weak_ciphers = []
    for method in SSL_METHODS:
        for cipher in sorted(host.accepted_ciphers_for(method)):
            if cipher.bits == 'Anon':
                anon_ciphers.append(cipher)
            elif cipher.bits == 'Unknown':
                # TODO: report?
                pass
            elif int(cipher.bits) < 128:
                weak_ciphers.append(cipher)

    if len(anon_ciphers) > 0:
        print('- Host supports anonymous cipher suites')
        for x in anon_ciphers:
            print('  - %s' % (x,))

    if len(weak_ciphers) > 0:
        print('- Host supports weak cipher suites')
        for x in weak_ciphers:
            print('  - %s' % (x,))
    def doTlsHandshake(self, base_url, client_cert, client_key, ciphers,
                       ssl_method):
        """Reports the success or failure of a TLS handshake.

    Args:
      base_url: Target host (defaults to port 443) or host:port.
      client_cert: Filename of the client certificate file in PEM format.
      client_key: Filename of the PEM-formatted key to use with |client_cert|.
      ciphers: List of cipher method strings.
      ssl_method: OpenSSL.SSL.*_METHOD value to use.

    Returns:
      True if the handshake succeeded, or False if it failed.
    """
        url = urlparse.urlparse('https://' + base_url)
        client = socket.socket()
        client.connect((url.hostname, url.port or 443))
        logging.debug("OPENSSL version: %s" %
                      SSL.SSLeay_version(SSL.SSLEAY_VERSION))
        logging.debug('TLS handshake: connecting to: %s:%d', url.hostname,
                      url.port or 443)
        logging.debug('TLS handshake: ciphers=%s', ':'.join(ciphers))
        logging.debug('TLS handshake: privatekey_file=%s', client_key)
        logging.debug('TLS handshake: certificate_file=%s', client_cert)
        logging.debug('TLS handshake: certificate_chain_file=%s',
                      self._sas._tls_config.ca_cert)

        ctx = SSL.Context(ssl_method)
        ctx.set_cipher_list(':'.join(ciphers))
        ctx.use_certificate_file(client_cert)
        ctx.use_privatekey_file(client_key)
        ctx.load_verify_locations(self._sas._tls_config.ca_cert)

        client_ssl_informations = []

        def _InfoCb(conn, where, ok):
            client_ssl_informations.append(conn.get_state_string())
            logging.debug('TLS handshake info: %d|%d %s', where, ok,
                          conn.get_state_string())
            return ok

        ctx.set_info_callback(_InfoCb)

        # only for potential debugging info...
        def _VerifyCb(conn, cert, errnum, depth, ok):
            certsubject = crypto.X509Name(cert.get_subject())
            commonname = certsubject.commonName
            logging.debug('TLS handshake verify: certificate: %s  -> %d',
                          commonname, ok)
            return ok

        ctx.set_verify(SSL.VERIFY_PEER, _VerifyCb)

        client_ssl = SSL.Connection(ctx, client)
        client_ssl.set_connect_state()
        client_ssl.set_tlsext_host_name(url.hostname)

        try:
            client_ssl.do_handshake()
            logging.debug('TLS handshake: succeed')
            handshake_ok = True
        except SSL.Error as e:
            logging.debug('TLS handshake: failed:\n%s',
                          '\n'.join(client_ssl_informations))
            handshake_ok = False
        finally:
            client_ssl.close()

        self.assertEqual(client_ssl.get_cipher_list(), ciphers)

        known_ssl_methods = {
            SSL.TLSv1_1_METHOD: 'TLSv1.1',
            SSL.TLSv1_2_METHOD: 'TLSv1.2',
        }
        self.assertEqual(client_ssl.get_protocol_version_name(),
                         known_ssl_methods[ssl_method])

        if handshake_ok:
            # tricky part: exact logged message depends of the version of openssl...
            cipher_check_regex = re.compile(r"change.cipher.spec", re.I)
            finished_check_regex = re.compile(
                r"negotiation finished|finish_client_handshake", re.I)

            def findIndexMatching(array, regex):
                for i, x in enumerate(array):
                    if regex.search(x):
                        return i
                return -1

            cipher_check_idx = findIndexMatching(client_ssl_informations,
                                                 cipher_check_regex)
            finished_check_idx = findIndexMatching(client_ssl_informations,
                                                   finished_check_regex)
            self.assertTrue(cipher_check_idx > 0)
            self.assertTrue(finished_check_idx > 0)
            self.assertTrue(finished_check_idx > cipher_check_idx)

        return handshake_ok
示例#13
0
def main():
    """
    Launch tests to check required modules and OS-specific dependencies.
    Exit with a relevant error code.
    """
    exit_code = 0
    import sys
    print 'python %s' % (sys.version,)

    try:
        import zlib
        print 'zlib %s' % (zlib.__version__,)
    except:
        sys.stderr.write('"zlib" missing.\n')
        exit_code = 1

    try:
        import _hashlib
        import ssl
        _hashlib
        print 'stdlib ssl %s' % (ssl.OPENSSL_VERSION,)
    except:
        sys.stderr.write('standard "ssl" missing.\n')
        exit_code = 2

    # cryptography module and latest pyOpenSSL are only available on
    # systems with cffi.
    if BUILD_CFFI:
        try:
            from cryptography.hazmat.backends.openssl.backend import backend
            import cryptography
            openssl_version = backend.openssl_version_text()
            print 'cryptography %s - OpenSSL %s' % (
                cryptography.__version__, openssl_version)

            if chevah_os == 'windows':
                # Check OpenSSL version on windows.
                expecting = u'OpenSSL 1.0.2g  1 Mar 2016'
                if openssl_version != expecting:
                    sys.stderr.write('Expecting %s got %s.\n' % (
                        expecting, openssl_version))
                    exit_code = 3
        except Exception as error:
            sys.stderr.write('"cryptography" failure. %s\n' % (error,))
            exit_code = 14

    try:
        from OpenSSL import SSL, crypto, rand, __version__ as pyopenssl_version
        crypto
        rand
        print 'pyopenssl %s - OpenSSL %s' % (
            pyopenssl_version,
            SSL.SSLeay_version(SSL.SSLEAY_VERSION),
            )
    except Exception as error:
        sys.stderr.write('"OpenSSL" missing. %s\n' % (error,))
        exit_code = 3

    try:
        import Crypto
        print 'PyCrypto %s' % (Crypto.__version__,)
    except:
        sys.stderr.write('"PyCrypto" missing.\n')
        exit_code = 4

    if os.name != 'nt':
        # Module only available on Linux / Unix
        try:
            import crypt
            crypt
        except:
            sys.stderr.write('"crypt" missing.\n')
            exit_code = 5

        try:
            import setproctitle
            setproctitle
        except:
            sys.stderr.write('"setproctitle" missing.\n')
            exit_code = 7

        try:
            from Crypto.PublicKey import _fastmath
            _fastmath
        except:
            sys.stderr.write('"Crypto.PublicKey._fastmath" missing. No GMP?\n')
            exit_code = 10

    try:
        from ctypes import CDLL
        import ctypes
        CDLL
        print 'ctypes %s' % (ctypes.__version__,)
    except:
        sys.stderr.write('"ctypes - CDLL" missing. %s\n')
        exit_code = 8

    try:
        from ctypes.util import find_library
        find_library
    except:
        sys.stderr.write('"ctypes.utils - find_library" missing.\n')
        exit_code = 9

    # Windows specific modules.
    if os.name == 'nt':
        try:
            from ctypes import windll
            windll
        except:
            sys.stderr.write('"ctypes - windll" missing.\n')
            exit_code = 1
        try:
            import sqlite3
            sqlite3
        except:
            sys.stderr.write('"sqlite3" missing.\n')
            exit_code = 6

    else:
        # Linux and Unix checks.
        try:
            import crypt
            crypt
        except:
            sys.stderr.write('"crypt" missing.\n')
            exit_code = 5

        try:
            import pysqlite2
            pysqlite2
        except:
            sys.stderr.write('"pysqlite2" missing.\n')
            exit_code = 6

        try:
            import setproctitle
            print 'setproctitle %s' % (setproctitle.__version__,)
        except:
            sys.stderr.write('"setproctitle" missing.\n')
            exit_code = 7

        try:
            from Crypto.PublicKey import _fastmath
            _fastmath
        except:
            sys.stderr.write('Crypto.PublicKey._fastmath missing. No GMP?\n')
            exit_code = 10


    if ( platform_system == 'linux' ) or ( platform_system == 'sunos' ):
        try:
            import spwd
            spwd
        except:
            sys.stderr.write('"spwd" missing.\n')
            exit_code = 1

    # We compile the readline module using libedit only on selected platforms.
    if test_for_readline:
        try:
            import readline
            readline.get_history_length()
        except:
            sys.stderr.write('"readline" missing.\n')
            exit_code = 13

        exit_code = test_dependencies() | exit_code

    sys.exit(exit_code)
    def assertTlsHandshakeSucceed(self, base_url, ciphers, client_cert,
                                  client_key):
        """Checks that the TLS handshake succeed with the given parameters.

    Attempts to establish a TLS session with the given |base_url|, using the
    given |ciphers| list and the given certificate key pair.
    Checks that he SAS UUT response must satisfy all of the following conditions:
    - The SAS UUT agrees to use a cipher specified in the |ciphers| list
    - The SAS UUT agrees to use TLS Protocol Version 1.2
    - Valid Finished message is returned by the SAS UUT immediately following
      the ChangeCipherSpec message
    """
        url = urlparse.urlparse('https://' + base_url)
        client = socket.socket()
        client.connect((url.hostname, url.port or 443))
        logging.debug("OPENSSL version: %s" %
                      SSL.SSLeay_version(SSL.SSLEAY_VERSION))
        logging.debug('TLS handshake: connecting to: %s:%d', url.hostname,
                      url.port or 443)
        logging.debug('TLS handshake: ciphers=%s', ':'.join(ciphers))
        logging.debug('TLS handshake: privatekey_file=%s', client_key)
        logging.debug('TLS handshake: certificate_file=%s', client_cert)
        logging.debug('TLS handshake: certificate_chain_file=%s',
                      self._sas._tls_config.ca_cert)

        ctx = SSL.Context(SSL.TLSv1_2_METHOD)
        ctx.set_cipher_list(':'.join(ciphers))
        ctx.use_certificate_file(client_cert)
        ctx.use_privatekey_file(client_key)
        ctx.load_verify_locations(self._sas._tls_config.ca_cert)

        client_ssl_informations = []

        def _InfoCb(conn, where, ok):
            client_ssl_informations.append(conn.get_state_string())
            logging.debug('TLS handshake info: %d|%d %s', where, ok,
                          conn.get_state_string())
            return ok

        ctx.set_info_callback(_InfoCb)

        # only for potential debugging info...
        def _VerifyCb(conn, cert, errnum, depth, ok):
            certsubject = crypto.X509Name(cert.get_subject())
            commonname = certsubject.commonName
            logging.debug('TLS handshake verify: certificate: %s  -> %d',
                          commonname, ok)
            return ok

        ctx.set_verify(SSL.VERIFY_PEER, _VerifyCb)

        client_ssl = SSL.Connection(ctx, client)
        client_ssl.set_connect_state()
        client_ssl.set_tlsext_host_name(url.hostname)

        try:
            client_ssl.do_handshake()
            logging.debug('TLS handshake: succeed')
        except SSL.Error as e:
            logging.exception('TLS handshake: failed:\n%s',
                              '\n'.join(client_ssl_informations))
            raise AssertionError('TLS handshake: failure: %s' % e.message)
        finally:
            client_ssl.close()

        self.assertEqual(client_ssl.get_cipher_list(), ciphers)
        self.assertEqual(client_ssl.get_protocol_version_name(), 'TLSv1.2')

        # tricky part: exact logged message depends of the version of openssl...
        cipher_check_regex = re.compile(r"change.cipher.spec", re.I)
        finished_check_regex = re.compile(
            r"negotiation finished|finish_client_handshake", re.I)

        def findIndexMatching(array, regex):
            for i, x in enumerate(array):
                if regex.search(x):
                    return i
            return -1

        cipher_check_idx = findIndexMatching(client_ssl_informations,
                                             cipher_check_regex)
        finished_check_idx = findIndexMatching(client_ssl_informations,
                                               finished_check_regex)
        self.assertTrue(cipher_check_idx > 0)
        self.assertTrue(finished_check_idx > 0)
        self.assertTrue(finished_check_idx > cipher_check_idx)