예제 #1
0
class SslClient(object):
    """
    High level API implementing an insecure SSL client.
    """


    def __init__(self, sock=None, sslVersion=SSLV23, sslVerify=SSL_VERIFY_PEER, sslVerifyLocations=None):
        # A Python socket handles transmission of the data
        self._sock = sock
        self._handshakeDone = False

        # OpenSSL objects
        # SSL_CTX
        self._sslCtx = SSL_CTX(sslVersion)
        self._sslCtx.set_verify(sslVerify)
        if sslVerifyLocations:
            self._sslCtx.load_verify_locations(sslVerifyLocations)

        # SSL
        self._ssl = SSL(self._sslCtx)
        self._ssl.set_connect_state()
        # Specific servers do not reply to a client hello that is bigger than 255 bytes
        # See http://rt.openssl.org/Ticket/Display.html?id=2771&user=guest&pass=guest
        # So we make the default cipher list smaller (to make the client hello smaller)
        if sslVersion != SSLV2: # This makes SSLv2 fail
            self._ssl.set_cipher_list('HIGH:-aNULL:-eNULL:-3DES:-SRP:-PSK:-CAMELLIA')
        else:
            # Handshake workaround for SSL2 + IIS 7
            self.do_handshake = self.do_ssl2_iis_handshake

        # BIOs
        self._internalBio = BIO()
        self._networkBio = BIO()

        # http://www.openssl.org/docs/crypto/BIO_s_bio.html
        BIO.make_bio_pair(self._internalBio, self._networkBio)
        self._ssl.set_bio(self._internalBio)


    def do_handshake(self):
        if self._sock is None:
            # TODO: Auto create a socket ?
            raise IOError('Internal socket set to None; cannot perform handshake.')

        while True:
            try:
                if self._ssl.do_handshake() == 1:
                    self._handshakeDone = True
                    return True # Handshake was successful

            except WantReadError:
                # OpenSSL is expecting more data from the peer
                # Send available handshake data to the peer
                lenToRead = self._networkBio.pending()
                while lenToRead:
                    # Get the data from the SSL engine
                    handshakeDataOut = self._networkBio.read(lenToRead)
                    # Send it to the peer
                    self._sock.send(handshakeDataOut)
                    lenToRead = self._networkBio.pending()

                # Recover the peer's encrypted response
                handshakeDataIn = self._sock.recv(DEFAULT_BUFFER_SIZE)
                if len(handshakeDataIn) == 0:
                    raise IOError('Nassl SSL handshake failed: peer did not send data back.')
                # Pass the data to the SSL engine
                self._networkBio.write(handshakeDataIn)

            except WantX509LookupError:
                # Server asked for a client certificate and we didn't provide one
                raise ClientCertificateRequested(self._ssl.get_client_CA_list())


    def do_ssl2_iis_handshake(self):
        if self._sock is None:
            # TODO: Auto create a socket ?
            raise IOError('Internal socket set to None; cannot perform handshake.')

        while True:
            try:
                if self._ssl.do_handshake() == 1:
                    self._handshakeDone = True
                    return True # Handshake was successful

            except WantReadError:
                # OpenSSL is expecting more data from the peer
                # Send available handshake data to the peer
                lenToRead = self._networkBio.pending()
                while lenToRead:
                    # Get the data from the SSL engine
                    handshakeDataOut = self._networkBio.read(lenToRead)

                    if 'SSLv2 read server verify A' in self._ssl.state_string_long():
                        # Awful h4ck for SSLv2 when connecting to IIS7 (like in the 90s)
                        # OpenSSL sends the client's CMK and data message in the same packet without
                        # waiting for the server's response, causing IIS 7 to hang on the connection.
                        # This workaround forces our client to send the CMK message, then wait for the server's
                        # response, and then send the data packet
                        if '\x02' in handshakeDataOut[2]:  # Make sure we're looking at the CMK message
                            cmkSize = handshakeDataOut[0:2]
                            test1 =  int(handshakeDataOut[0].encode('hex'), base=16)
                            test2 = int(handshakeDataOut[1].encode('hex'), base=16)
                            test1 = (test1 & 0x7f) << 8
                            size = test1 + test2
                            # Manually split the two records to force them to be sent separately
                            cmkPacket = handshakeDataOut[0:size+2]
                            dataPacket = handshakeDataOut[size+2::]
                            self._sock.send(cmkPacket)
                            handshakeDataIn = self._sock.recv(DEFAULT_BUFFER_SIZE)
                            #print repr(handshakeDataIn)
                            if len(handshakeDataIn) == 0:
                                raise IOError('Nassl SSL handshake failed: peer did not send data back.')
                            # Pass the data to the SSL engine
                            self._networkBio.write(handshakeDataIn)
                            handshakeDataOut = dataPacket

                    # Send it to the peer
                    self._sock.send(handshakeDataOut)
                    lenToRead = self._networkBio.pending()

                handshakeDataIn = self._sock.recv(DEFAULT_BUFFER_SIZE)
                if len(handshakeDataIn) == 0:
                    raise IOError('Nassl SSL handshake failed: peer did not send data back.')
                # Pass the data to the SSL engine
                self._networkBio.write(handshakeDataIn)

            except WantX509LookupError:
                # Server asked for a client certificate and we didn't provide one
                raise ClientCertificateRequested(self._ssl.get_client_CA_list())



    def read(self, size):
        if not self._handshakeDone:
            raise IOError('SSL Handshake was not completed; cannot receive data.')

        while True:
            # Receive available encrypted data from the peer
            encData = self._sock.recv(DEFAULT_BUFFER_SIZE)
            # Pass it to the SSL engine
            self._networkBio.write(encData)

            try:
                # Try to read the decrypted data
                decData = self._ssl.read(size)
                return decData

            except WantReadError:
                # The SSL engine needs more data
                # before it can decrypt the whole message
                pass


    def write(self, data):
        """
        Returns the number of (encrypted) bytes sent.
        """
        if not self._handshakeDone:
            raise IOError('SSL Handshake was not completed; cannot send data.')

        # Pass the cleartext data to the SSL engine
        self._ssl.write(data)

        # Recover the corresponding encrypted data
        lenToRead = self._networkBio.pending()
        finalLen = lenToRead
        while lenToRead:
            encData = self._networkBio.read(lenToRead)
            # Send the encrypted data to the peer
            self._sock.send(encData)
            lenToRead = self._networkBio.pending()
            finalLen += lenToRead

        return finalLen


    def shutdown(self):
        self._handshakeDone = False
        try:
            self._ssl.shutdown()
        except OpenSSLError as e:
            # Ignore "uninitialized" exception
            if 'SSL_shutdown:uninitialized' not in str(e):
                raise


    def set_verify(self, verifyMode):
        """Set the OpenSSL verify mode."""
        return self._ssl.set_verify(verifyMode)


    def set_tlsext_host_name(self, nameIndication):
        """Set the hostname within the Server Name Indication extension in the client SSL Hello."""
        return self._ssl.set_tlsext_host_name(nameIndication)


    def get_peer_certificate(self):
        _x509 = self._ssl.get_peer_certificate()
        if _x509:
            return X509Certificate(_x509)
        else:
            return None


    def get_peer_cert_chain(self):
        """
        See the OpenSSL documentation for differences between get_peer_cert_chain() and get_peer_certificate().
        https://www.openssl.org/docs/ssl/SSL_get_peer_cert_chain.html
        """
        _x509_list = self._ssl.get_peer_cert_chain()
        final_list = []
        if _x509_list:
            for _x509 in _x509_list:
                final_list.append(X509Certificate(_x509))

        return final_list


    def set_cipher_list(self, cipherList):
        return self._ssl.set_cipher_list(cipherList)


    def get_cipher_list(self):
        return self._ssl.get_cipher_list()


    def get_current_cipher_name(self):
        return self._ssl.get_cipher_name()


    def get_current_cipher_bits(self):
        return self._ssl.get_cipher_bits()


    def use_private_key(self, certFile, certType, keyFile, keyType, keyPassword=''):

        self._ssl.use_certificate_file(certFile, certType)

        if isinstance(keyPassword, basestring):
            self._sslCtx.set_private_key_password(keyPassword)
        else:
            raise TypeError('keyPassword is not a string')

        self._ssl.use_PrivateKey_file(keyFile, keyType)
        return self._ssl.check_private_key()


    def get_certificate_chain_verify_result(self):
        verifyResult = self._ssl.get_verify_result()
        verifyResultStr = X509.verify_cert_error_string(verifyResult)
        return verifyResult, verifyResultStr


    def set_tlsext_status_ocsp(self):
        """Enable the OCSP Stapling extension."""
        return self._ssl.set_tlsext_status_type(TLSEXT_STATUSTYPE_ocsp)


    def get_tlsext_status_ocsp_resp(self):
        """Retrieve the server's OCSP Stapling status."""
        ocspResp = self._ssl.get_tlsext_status_ocsp_resp()
        if ocspResp:
            return OcspResponse(ocspResp)
        else:
            return None
예제 #2
0
파일: SslClient.py 프로젝트: stefanb/nassl
class SslClient(object):
    """
    High level API implementing an insecure SSL client.
    """
    def __init__(self,
                 sock=None,
                 sslVersion=SSLV23,
                 sslVerify=SSL_VERIFY_PEER,
                 sslVerifyLocations=None):
        # A Python socket handles transmission of the data
        self._sock = sock
        self._handshakeDone = False

        # OpenSSL objects
        # SSL_CTX
        self._sslCtx = SSL_CTX(sslVersion)
        self._sslCtx.set_verify(sslVerify)
        if sslVerifyLocations:
            self._sslCtx.load_verify_locations(sslVerifyLocations)

        # SSL
        self._ssl = SSL(self._sslCtx)
        self._ssl.set_connect_state()
        # Specific servers do not reply to a client hello that is bigger than 255 bytes
        # See http://rt.openssl.org/Ticket/Display.html?id=2771&user=guest&pass=guest
        # So we make the default cipher list smaller (to make the client hello smaller)
        if sslVersion != SSLV2:  # This makes SSLv2 fail
            self._ssl.set_cipher_list(
                'HIGH:-aNULL:-eNULL:-3DES:-SRP:-PSK:-CAMELLIA')

        # BIOs
        self._internalBio = BIO()
        self._networkBio = BIO()

        # http://www.openssl.org/docs/crypto/BIO_s_bio.html
        BIO.make_bio_pair(self._internalBio, self._networkBio)
        self._ssl.set_bio(self._internalBio)

    def do_handshake(self):
        if (self._sock == None):
            # TODO: Auto create a socket ?
            raise IOError(
                'Internal socket set to None; cannot perform handshake.')

        while True:
            try:
                if self._ssl.do_handshake() == 1:
                    self._handshakeDone = True
                    return True  # Handshake was successful

            except WantReadError:
                # OpenSSL is expecting more data from the peer
                # Send available handshake data to the peer
                lenToRead = self._networkBio.pending()
                while lenToRead:
                    # Get the data from the SSL engine
                    handshakeDataOut = self._networkBio.read(lenToRead)
                    # Send it to the peer
                    self._sock.send(handshakeDataOut)
                    lenToRead = self._networkBio.pending()

                # Recover the peer's encrypted response
                handshakeDataIn = self._sock.recv(DEFAULT_BUFFER_SIZE)
                if len(handshakeDataIn) == 0:
                    raise IOError(
                        'Nassl SSL handshake failed: peer did not send data back.'
                    )
                # Pass the data to the SSL engine
                self._networkBio.write(handshakeDataIn)

            except WantX509LookupError:
                # Server asked for a client certificate and we didn't provide one
                raise ClientCertificateRequested(
                    self._ssl.get_client_CA_list())

    def read(self, size):
        if (self._handshakeDone == False):
            raise IOError(
                'SSL Handshake was not completed; cannot receive data.')

        while True:
            # Receive available encrypted data from the peer
            encData = self._sock.recv(DEFAULT_BUFFER_SIZE)
            # Pass it to the SSL engine
            self._networkBio.write(encData)

            try:
                # Try to read the decrypted data
                decData = self._ssl.read(size)
                return decData

            except WantReadError:
                # The SSL engine needs more data
                # before it can decrypt the whole message
                pass

    def write(self, data):
        """
        Returns the number of (encrypted) bytes sent.
        """
        if (self._handshakeDone == False):
            raise IOError('SSL Handshake was not completed; cannot send data.')

        # Pass the cleartext data to the SSL engine
        self._ssl.write(data)

        # Recover the corresponding encrypted data
        lenToRead = self._networkBio.pending()
        finalLen = lenToRead
        while lenToRead:
            encData = self._networkBio.read(lenToRead)
            # Send the encrypted data to the peer
            self._sock.send(encData)
            lenToRead = self._networkBio.pending()
            finalLen += lenToRead

        return finalLen

    def shutdown(self):
        self._handshakeDone = False
        try:
            self._ssl.shutdown()
        except OpenSSLError as e:
            # Ignore "uninitialized" exception
            if 'SSL_shutdown:uninitialized' not in str(e):
                raise

    def get_secure_renegotiation_support(self):
        return self._ssl.get_secure_renegotiation_support()

    def get_current_compression_method(self):
        return self._ssl.get_current_compression_method()

    @staticmethod
    def get_available_compression_methods():
        """
        Returns the list of SSL compression methods supported by SslClient.
        """
        return SSL.get_available_compression_methods()

    def set_verify(self, verifyMode):
        return self._ssl.set_verify(verifyMode)

    def set_tlsext_host_name(self, nameIndication):
        return self._ssl.set_tlsext_host_name(nameIndication)

    def get_peer_certificate(self):
        _x509 = self._ssl.get_peer_certificate()
        if _x509:
            return X509Certificate(_x509)
        else:
            return None

    def set_cipher_list(self, cipherList):
        return self._ssl.set_cipher_list(cipherList)

    def get_cipher_list(self):
        return self._ssl.get_cipher_list()

    def get_current_cipher_name(self):
        return self._ssl.get_cipher_name()

    def get_current_cipher_bits(self):
        return self._ssl.get_cipher_bits()

    def use_private_key(self,
                        certFile,
                        certType,
                        keyFile,
                        keyType,
                        keyPassword=''):

        self._ssl.use_certificate_file(certFile, certType)

        if isinstance(keyPassword, basestring):
            self._sslCtx.set_private_key_password(keyPassword)
        else:
            raise TypeError('keyPassword is not a string')

        self._ssl.use_PrivateKey_file(keyFile, keyType)
        return self._ssl.check_private_key()

    def get_certificate_chain_verify_result(self):
        verifyResult = self._ssl.get_verify_result()
        verifyResultStr = X509.verify_cert_error_string(verifyResult)
        return (verifyResult, verifyResultStr)

    def do_renegotiate(self):
        if (self._handshakeDone == False):
            raise IOError(
                'SSL Handshake was not completed; cannot renegotiate.')

        self._ssl.renegotiate()
        return self.do_handshake()

    def get_session(self):
        return self._ssl.get_session()

    def set_session(self, sslSession):
        return self._ssl.set_session(sslSession)

    def set_options(self, options):
        return self._ssl.set_options(options)

    def set_tlsext_status_ocsp(self):
        return self._ssl.set_tlsext_status_type(TLSEXT_STATUSTYPE_ocsp)

    def get_tlsext_status_ocsp_resp(self):
        ocspResp = self._ssl.get_tlsext_status_ocsp_resp()
        if ocspResp:
            return OcspResponse(ocspResp)
        else:
            return None
예제 #3
0
class SslClient(object):
    """High level API implementing an SSL client.
    """

    _DEFAULT_BUFFER_SIZE = 4096

    def __init__(
        self,
        sock=None,  # type: Optional[socket.socket]
        ssl_version=OpenSslVersionEnum.SSLV23,  # type: OpenSslVersionEnum
        ssl_verify=OpenSslVerifyEnum.PEER,  # type: OpenSslVerifyEnum
        ssl_verify_locations=None,  # type: Optional[Text]
        client_certchain_file=None,  # type: Optional[Text]
        client_key_file=None,  # type: Optional[Text]
        client_key_type=OpenSslFileTypeEnum.PEM,  # type: OpenSslFileTypeEnum
        client_key_password='',  # type: Text
        ignore_client_authentication_requests=False  # type: bool
    ):
        # type: (...) -> None

        # A Python socket handles transmission of the data
        self._sock = sock
        self._is_handshake_completed = False
        self._client_CA_list = []

        # OpenSSL objects
        # SSL_CTX
        self._ssl_ctx = SSL_CTX(ssl_version.value)
        self._ssl_ctx.set_verify(ssl_verify.value)
        if ssl_verify_locations:
            # Ensure the file exists
            with open(ssl_verify_locations):
                pass
            self._ssl_ctx.load_verify_locations(ssl_verify_locations)

        if client_certchain_file is not None:
            self._use_private_key(client_certchain_file, client_key_file,
                                  client_key_type.value, client_key_password)

        if ignore_client_authentication_requests:
            if client_certchain_file:
                raise ValueError(
                    'Cannot enable both client_certchain_file and ignore_client_authentication_requests'
                )

            self._ssl_ctx.set_client_cert_cb_NULL()

        # SSL
        self._ssl = SSL(self._ssl_ctx)
        self._ssl.set_connect_state()
        # Specific servers do not reply to a client hello that is bigger than 255 bytes
        # See http://rt.openssl.org/Ticket/Display.html?id=2771&user=guest&pass=guest
        # So we make the default cipher list smaller (to make the client hello smaller)
        if ssl_version != OpenSslVersionEnum.SSLV2:  # This makes SSLv2 fail
            self._ssl.set_cipher_list(
                'HIGH:-aNULL:-eNULL:-3DES:-SRP:-PSK:-CAMELLIA')
        else:
            # Handshake workaround for SSL2 + IIS 7
            self.do_handshake = self.do_ssl2_iis_handshake

        # BIOs
        self._internal_bio = BIO()
        self._network_bio = BIO()

        # http://www.openssl.org/docs/crypto/BIO_s_bio.html
        BIO.make_bio_pair(self._internal_bio, self._network_bio)
        self._ssl.set_bio(self._internal_bio)

    def do_handshake(self):
        # type: () -> None
        if self._sock is None:
            # TODO: Auto create a socket ?
            raise IOError(
                'Internal socket set to None; cannot perform handshake.')

        while True:
            try:
                self._ssl.do_handshake()
                self._is_handshake_completed = True
                # Handshake was successful
                return

            except WantReadError:
                # OpenSSL is expecting more data from the peer
                # Send available handshake data to the peer
                self._flush_ssl_engine()

                # Recover the peer's encrypted response
                handshake_data_in = self._sock.recv(self._DEFAULT_BUFFER_SIZE)
                if len(handshake_data_in) == 0:
                    raise IOError(
                        'Nassl SSL handshake failed: peer did not send data back.'
                    )
                # Pass the data to the SSL engine
                self._network_bio.write(handshake_data_in)

            except WantX509LookupError:
                # Server asked for a client certificate and we didn't provide one
                raise ClientCertificateRequested(self.get_client_CA_list())

    def do_ssl2_iis_handshake(self):
        # type: () -> None
        if self._sock is None:
            # TODO: Auto create a socket ?
            raise IOError(
                'Internal socket set to None; cannot perform handshake.')

        while True:
            try:
                self._ssl.do_handshake()
                self._is_handshake_completed = True
                # Handshake was successful
                return

            except WantReadError:
                # OpenSSL is expecting more data from the peer
                # Send available handshake data to the peer
                lengh_to_read = self._network_bio.pending()
                while lengh_to_read:
                    # Get the data from the SSL engine
                    handshake_data_out = self._network_bio.read(lengh_to_read)

                    if 'SSLv2 read server verify A' in self._ssl.state_string_long(
                    ):
                        # Awful h4ck for SSLv2 when connecting to IIS7 (like in the 90s)
                        # OpenSSL sends the client's CMK and data message in the same packet without
                        # waiting for the server's response, causing IIS 7 to hang on the connection.
                        # This workaround forces our client to send the CMK message, then wait for the server's
                        # response, and then send the data packet
                        if '\x02' in handshake_data_out[
                                2]:  # Make sure we're looking at the CMK message
                            # cmk_size = handshake_data_out[0:2]
                            test1 = int(handshake_data_out[0].encode('hex'),
                                        base=16)
                            test2 = int(handshake_data_out[1].encode('hex'),
                                        base=16)
                            test1 = (test1 & 0x7f) << 8
                            size = test1 + test2
                            # Manually split the two records to force them to be sent separately
                            cmk_packet = handshake_data_out[0:size + 2]
                            data_packet = handshake_data_out[size + 2::]
                            self._sock.send(cmk_packet)

                            handshake_data_in = self._sock.recv(
                                self._DEFAULT_BUFFER_SIZE)
                            # print repr(handshake_data_in)
                            if len(handshake_data_in) == 0:
                                raise IOError(
                                    'Nassl SSL handshake failed: peer did not send data back.'
                                )
                            # Pass the data to the SSL engine
                            self._network_bio.write(handshake_data_in)
                            handshake_data_out = data_packet

                    # Send it to the peer
                    self._sock.send(handshake_data_out)
                    lengh_to_read = self._network_bio.pending()

                handshake_data_in = self._sock.recv(self._DEFAULT_BUFFER_SIZE)
                if len(handshake_data_in) == 0:
                    raise IOError(
                        'Nassl SSL handshake failed: peer did not send data back.'
                    )
                # Pass the data to the SSL engine
                self._network_bio.write(handshake_data_in)

            except WantX509LookupError:
                # Server asked for a client certificate and we didn't provide one
                raise ClientCertificateRequested(self.get_client_CA_list())

    def read(self, size):
        # type: (int) -> bytes
        if not self._is_handshake_completed:
            raise IOError(
                'SSL Handshake was not completed; cannot receive data.')

        while True:
            # Receive available encrypted data from the peer
            encrypted_data = self._sock.recv(self._DEFAULT_BUFFER_SIZE)

            if len(encrypted_data) == 0:
                raise IOError('Could not read() - peer closed the connection.')

            # Pass it to the SSL engine
            self._network_bio.write(encrypted_data)

            try:
                # Try to read the decrypted data
                decrypted_data = self._ssl.read(size)
                return decrypted_data

            except WantReadError:
                # The SSL engine needs more data
                # before it can decrypt the whole message
                pass

    def write(self, data):
        # type: (bytes) -> int
        """Returns the number of (encrypted) bytes sent.
        """
        if not self._is_handshake_completed:
            raise IOError('SSL Handshake was not completed; cannot send data.')

        # Pass the cleartext data to the SSL engine
        self._ssl.write(data)

        # Recover the corresponding encrypted data
        final_length = self._flush_ssl_engine()

        return final_length

    def _flush_ssl_engine(self):
        # type: () -> int
        length_to_read = self._network_bio.pending()
        final_length = length_to_read
        while length_to_read:
            encrypted_data = self._network_bio.read(length_to_read)
            # Send the encrypted data to the peer
            self._sock.send(encrypted_data)
            length_to_read = self._network_bio.pending()
            final_length += length_to_read

        return final_length

    def shutdown(self):
        # type: () -> None
        self._is_handshake_completed = False
        try:
            self._ssl.shutdown()
            self._flush_ssl_engine()
        except OpenSSLError as e:
            # Ignore "uninitialized" exception
            if 'SSL_shutdown:uninitialized' not in str(
                    e) and 'shutdown while in init' not in str(e):
                raise

    def set_tlsext_host_name(self, name_indication):
        # type: (Text) -> None
        """Set the hostname within the Server Name Indication extension in the client SSL Hello.
        """
        self._ssl.set_tlsext_host_name(name_indication)

    def get_peer_certificate(self):
        # type: () -> Optional[X509Certificate]
        _x509 = self._ssl.get_peer_certificate()
        if _x509:
            return X509Certificate(_x509)
        else:
            return None

    def get_peer_cert_chain(self):
        # type: () -> List[X509Certificate]
        """See the OpenSSL documentation for differences between get_peer_cert_chain() and get_peer_certificate().
        https://www.openssl.org/docs/ssl/SSL_get_peer_cert_chain.html
        """
        x509_list = self._ssl.get_peer_cert_chain()
        final_list = []
        if x509_list:
            for x509_cert in x509_list:
                final_list.append(X509Certificate(x509_cert))
        return final_list

    def set_cipher_list(self, cipher_list):
        # type: (Text) -> None
        self._ssl.set_cipher_list(cipher_list)

    def get_cipher_list(self):
        # type: () -> List[Text]
        return self._ssl.get_cipher_list()

    def get_current_cipher_name(self):
        # type: () -> Text
        return self._ssl.get_cipher_name()

    def get_current_cipher_bits(self):
        # type: () -> int
        return self._ssl.get_cipher_bits()

    def _use_private_key(self, client_certchain_file, client_key_file,
                         client_key_type, client_key_password):
        # type: (Text, Text, OpenSslFileTypeEnum, Text) -> None
        """The certificate chain file must be in PEM format. Private method because it should be set via the
        constructor.
        """
        # Ensure the files exist
        with open(client_certchain_file):
            pass
        with open(client_key_file):
            pass

        self._ssl_ctx.use_certificate_chain_file(client_certchain_file)
        self._ssl_ctx.set_private_key_password(client_key_password)
        try:
            self._ssl_ctx.use_PrivateKey_file(client_key_file,
                                              client_key_type.value)
        except OpenSSLError as e:
            if 'bad password read' in str(e) or 'bad decrypt' in str(e):
                raise ValueError('Invalid Private Key')
            else:
                raise

        self._ssl_ctx.check_private_key()

    def get_certificate_chain_verify_result(self):
        # type: () -> Tuple[int, Text]
        verify_result = self._ssl.get_verify_result()
        verify_result_str = X509.verify_cert_error_string(verify_result)
        return verify_result, verify_result_str

    _TLSEXT_STATUSTYPE_ocsp = 1

    def set_tlsext_status_ocsp(self):
        # type: () -> None
        """Enable the OCSP Stapling extension.
        """
        self._ssl.set_tlsext_status_type(self._TLSEXT_STATUSTYPE_ocsp)

    def get_tlsext_status_ocsp_resp(self):
        # type: () -> Optional[OcspResponse]
        """Retrieve the server's OCSP Stapling status.
        """
        ocsp_response = self._ssl.get_tlsext_status_ocsp_resp()
        if ocsp_response:
            return OcspResponse(ocsp_response)
        else:
            return None

    def get_client_CA_list(self):
        # type: () -> List[Text]
        if not self._client_CA_list:
            self._client_CA_list = self._ssl.get_client_CA_list()
        return self._client_CA_list
예제 #4
0
파일: SslClient.py 프로젝트: bcyrill/nassl
class SslClient(object):
    """
    High level API implementing an insecure SSL client.
    """


    def __init__(self, sock=None, sslVersion=SSLV23, sslVerify=SSL_VERIFY_PEER, sslVerifyLocations=None):
        # A Python socket handles transmission of the data
        self._sock = sock
        self._handshakeDone = False

        # OpenSSL objects
        # SSL_CTX
        self._sslCtx = SSL_CTX(sslVersion)
        self._sslCtx.set_verify(sslVerify)
        if sslVerifyLocations:
            self._sslCtx.load_verify_locations(sslVerifyLocations)

        # SSL
        self._ssl = SSL(self._sslCtx)
        self._ssl.set_connect_state()
        # Specific servers do not reply to a client hello that is bigger than 255 bytes
        # See http://rt.openssl.org/Ticket/Display.html?id=2771&user=guest&pass=guest
        # So we make the default cipher list smaller (to make the client hello smaller)
        if sslVersion != SSLV2: # This makes SSLv2 fail
            self._ssl.set_cipher_list('HIGH:-aNULL:-eNULL:-3DES:-SRP:-PSK:-CAMELLIA')

        # BIOs
        self._internalBio = BIO()
        self._networkBio = BIO()

        # http://www.openssl.org/docs/crypto/BIO_s_bio.html
        BIO.make_bio_pair(self._internalBio, self._networkBio)
        self._ssl.set_bio(self._internalBio)


    def do_handshake(self):
        if self._sock is None:
            # TODO: Auto create a socket ?
            raise IOError('Internal socket set to None; cannot perform handshake.')

        while True:
            try:
                if self._ssl.do_handshake() == 1:
                    self._handshakeDone = True
                    return True # Handshake was successful

            except WantReadError:
                # OpenSSL is expecting more data from the peer
                # Send available handshake data to the peer
                lenToRead = self._networkBio.pending()
                while lenToRead:
                    # Get the data from the SSL engine
                    handshakeDataOut = self._networkBio.read(lenToRead)
                    # Send it to the peer
                    self._sock.send(handshakeDataOut)
                    lenToRead = self._networkBio.pending()

                # Recover the peer's encrypted response
                handshakeDataIn = self._sock.recv(DEFAULT_BUFFER_SIZE)
                if len(handshakeDataIn) == 0:
                    raise IOError('Nassl SSL handshake failed: peer did not send data back.')
                # Pass the data to the SSL engine
                self._networkBio.write(handshakeDataIn)

            except WantX509LookupError:
                # Server asked for a client certificate and we didn't provide one
                raise ClientCertificateRequested(self._ssl.get_client_CA_list())



    def read(self, size):
        if not self._handshakeDone:
            raise IOError('SSL Handshake was not completed; cannot receive data.')

        while True:
            # Receive available encrypted data from the peer
            encData = self._sock.recv(DEFAULT_BUFFER_SIZE)
            # Pass it to the SSL engine
            self._networkBio.write(encData)

            try:
                # Try to read the decrypted data
                decData = self._ssl.read(size)
                return decData

            except WantReadError:
                # The SSL engine needs more data
                # before it can decrypt the whole message
                pass


    def write(self, data):
        """
        Returns the number of (encrypted) bytes sent.
        """
        if not self._handshakeDone:
            raise IOError('SSL Handshake was not completed; cannot send data.')

        # Pass the cleartext data to the SSL engine
        self._ssl.write(data)

        # Recover the corresponding encrypted data
        lenToRead = self._networkBio.pending()
        finalLen = lenToRead
        while lenToRead:
            encData = self._networkBio.read(lenToRead)
            # Send the encrypted data to the peer
            self._sock.send(encData)
            lenToRead = self._networkBio.pending()
            finalLen += lenToRead

        return finalLen


    def shutdown(self):
        self._handshakeDone = False
        try:
            self._ssl.shutdown()
        except OpenSSLError as e:
            # Ignore "uninitialized" exception
            if 'SSL_shutdown:uninitialized' not in str(e):
                raise


    def get_secure_renegotiation_support(self):
        return self._ssl.get_secure_renegotiation_support()


    def get_current_compression_method(self):
        return self._ssl.get_current_compression_method()


    @staticmethod
    def get_available_compression_methods():
        """
        Returns the list of SSL compression methods supported by SslClient.
        """
        return SSL.get_available_compression_methods()


    def set_verify(self, verifyMode):
        return self._ssl.set_verify(verifyMode)


    def set_tlsext_host_name(self, nameIndication):
        return self._ssl.set_tlsext_host_name(nameIndication)


    def get_peer_certificate(self):
        _x509 = self._ssl.get_peer_certificate()
        if _x509:
            return X509Certificate(_x509)
        else:
            return None


    def set_cipher_list(self, cipherList):
        return self._ssl.set_cipher_list(cipherList)


    def get_cipher_list(self):
        return self._ssl.get_cipher_list()


    def get_current_cipher_name(self):
        return self._ssl.get_cipher_name()


    def get_current_cipher_bits(self):
        return self._ssl.get_cipher_bits()


    def use_private_key(self, certFile, certType, keyFile, keyType, keyPassword=''):

        self._ssl.use_certificate_file(certFile, certType)

        if isinstance(keyPassword, basestring):
            self._sslCtx.set_private_key_password(keyPassword)
        else:
            raise TypeError('keyPassword is not a string')

        self._ssl.use_PrivateKey_file(keyFile, keyType)
        return self._ssl.check_private_key()


    def get_certificate_chain_verify_result(self):
        verifyResult = self._ssl.get_verify_result()
        verifyResultStr = X509.verify_cert_error_string(verifyResult)
        return verifyResult, verifyResultStr


    def do_renegotiate(self):
        if not self._handshakeDone:
            raise IOError('SSL Handshake was not completed; cannot renegotiate.')

        self._ssl.renegotiate()
        return  self.do_handshake()


    def get_session(self):
        return self._ssl.get_session()


    def set_session(self, sslSession):
        return self._ssl.set_session(sslSession)


    def set_options(self, options):
        return self._ssl.set_options(options)


    def set_tlsext_status_ocsp(self):
        return self._ssl.set_tlsext_status_type(TLSEXT_STATUSTYPE_ocsp)


    def get_tlsext_status_ocsp_resp(self):
        ocspResp = self._ssl.get_tlsext_status_ocsp_resp()
        if ocspResp:
            return OcspResponse(ocspResp)
        else:
            return None
예제 #5
0
class SslClient(object):
    """High level API implementing an SSL client.
    """


    def __init__(self, sock=None, ssl_version=SSLV23, ssl_verify=SSL_VERIFY_PEER, ssl_verify_locations=None,
                 client_certchain_file=None, client_key_file=None, client_key_type=SSL_FILETYPE_PEM,
                 client_key_password='', ignore_client_authentication_requests=False):
        # type: (socket.socket, int, int, str, str, str, int, str, bool) -> None

        # A Python socket handles transmission of the data
        self._sock = sock
        self._is_handshake_completed = False
        self._client_CA_list = []

        # OpenSSL objects
        # SSL_CTX
        self._ssl_ctx = SSL_CTX(ssl_version)
        self._ssl_ctx.set_verify(ssl_verify)
        if ssl_verify_locations:
            self._ssl_ctx.load_verify_locations(ssl_verify_locations)

        if client_certchain_file is not None:
            self._use_private_key(client_certchain_file, client_key_file, client_key_type, client_key_password)

        if ignore_client_authentication_requests:
            if client_certchain_file:
                raise ValueError('Cannot enable both client_certchain_file and ignore_client_authentication_requests')

            self._ssl_ctx.set_client_cert_cb_NULL()

        # SSL
        self._ssl = SSL(self._ssl_ctx)
        self._ssl.set_connect_state()
        # Specific servers do not reply to a client hello that is bigger than 255 bytes
        # See http://rt.openssl.org/Ticket/Display.html?id=2771&user=guest&pass=guest
        # So we make the default cipher list smaller (to make the client hello smaller)
        if ssl_version != SSLV2: # This makes SSLv2 fail
            self._ssl.set_cipher_list('HIGH:-aNULL:-eNULL:-3DES:-SRP:-PSK:-CAMELLIA')
        else:
            # Handshake workaround for SSL2 + IIS 7
            self.do_handshake = self.do_ssl2_iis_handshake

        # BIOs
        self._internal_bio = BIO()
        self._network_bio = BIO()

        # http://www.openssl.org/docs/crypto/BIO_s_bio.html
        BIO.make_bio_pair(self._internal_bio, self._network_bio)
        self._ssl.set_bio(self._internal_bio)


    def do_handshake(self):
        # type: () -> None
        if self._sock is None:
            # TODO: Auto create a socket ?
            raise IOError('Internal socket set to None; cannot perform handshake.')

        while True:
            try:
                self._ssl.do_handshake()
                self._is_handshake_completed = True
                # Handshake was successful
                return

            except WantReadError:
                # OpenSSL is expecting more data from the peer
                # Send available handshake data to the peer
                self._flush_ssl_engine()

                # Recover the peer's encrypted response
                handshake_data_in = self._sock.recv(DEFAULT_BUFFER_SIZE)
                if len(handshake_data_in) == 0:
                    raise IOError('Nassl SSL handshake failed: peer did not send data back.')
                # Pass the data to the SSL engine
                self._network_bio.write(handshake_data_in)

            except WantX509LookupError:
                # Server asked for a client certificate and we didn't provide one
                raise ClientCertificateRequested(self.get_client_CA_list())
                

    def do_ssl2_iis_handshake(self):
        # type: () -> None
        if self._sock is None:
            # TODO: Auto create a socket ?
            raise IOError('Internal socket set to None; cannot perform handshake.')

        while True:
            try:
                self._ssl.do_handshake()
                self._is_handshake_completed = True
                # Handshake was successful
                return

            except WantReadError:
                # OpenSSL is expecting more data from the peer
                # Send available handshake data to the peer
                lengh_to_read = self._network_bio.pending()
                while lengh_to_read:
                    # Get the data from the SSL engine
                    handshake_data_out = self._network_bio.read(lengh_to_read)

                    if 'SSLv2 read server verify A' in self._ssl.state_string_long():
                        # Awful h4ck for SSLv2 when connecting to IIS7 (like in the 90s)
                        # OpenSSL sends the client's CMK and data message in the same packet without
                        # waiting for the server's response, causing IIS 7 to hang on the connection.
                        # This workaround forces our client to send the CMK message, then wait for the server's
                        # response, and then send the data packet
                        if '\x02' in handshake_data_out[2]:  # Make sure we're looking at the CMK message
                            # cmk_size = handshake_data_out[0:2]
                            test1 =  int(handshake_data_out[0].encode('hex'), base=16)
                            test2 = int(handshake_data_out[1].encode('hex'), base=16)
                            test1 = (test1 & 0x7f) << 8
                            size = test1 + test2
                            # Manually split the two records to force them to be sent separately
                            cmk_packet = handshake_data_out[0:size+2]
                            data_packet = handshake_data_out[size+2::]
                            self._sock.send(cmk_packet)

                            handshake_data_in = self._sock.recv(DEFAULT_BUFFER_SIZE)
                            # print repr(handshake_data_in)
                            if len(handshake_data_in) == 0:
                                raise IOError('Nassl SSL handshake failed: peer did not send data back.')
                            # Pass the data to the SSL engine
                            self._network_bio.write(handshake_data_in)
                            handshake_data_out = data_packet

                    # Send it to the peer
                    self._sock.send(handshake_data_out)
                    lengh_to_read = self._network_bio.pending()

                handshake_data_in = self._sock.recv(DEFAULT_BUFFER_SIZE)
                if len(handshake_data_in) == 0:
                    raise IOError('Nassl SSL handshake failed: peer did not send data back.')
                # Pass the data to the SSL engine
                self._network_bio.write(handshake_data_in)

            except WantX509LookupError:
                # Server asked for a client certificate and we didn't provide one
                raise ClientCertificateRequested(self.get_client_CA_list())



    def read(self, size):
        # type: (int) -> str
        if not self._is_handshake_completed:
            raise IOError('SSL Handshake was not completed; cannot receive data.')

        while True:
            # Receive available encrypted data from the peer
            encrypted_data = self._sock.recv(DEFAULT_BUFFER_SIZE)

            if len(encrypted_data) == 0:
                raise IOError('Could not read() - peer closed the connection.')

            # Pass it to the SSL engine
            self._network_bio.write(encrypted_data)

            try:
                # Try to read the decrypted data
                decrypted_data = self._ssl.read(size)
                return decrypted_data

            except WantReadError:
                # The SSL engine needs more data
                # before it can decrypt the whole message
                pass


    def write(self, data):
        # type: (str) -> int
        """Returns the number of (encrypted) bytes sent.
        """
        if not self._is_handshake_completed:
            raise IOError('SSL Handshake was not completed; cannot send data.')

        # Pass the cleartext data to the SSL engine
        self._ssl.write(data)

        # Recover the corresponding encrypted data
        final_length = self._flush_ssl_engine()

        return final_length

    def _flush_ssl_engine(self):
        # type: () -> int
        length_to_read = self._network_bio.pending()
        final_length = length_to_read
        while length_to_read:
            encrypted_data = self._network_bio.read(length_to_read)
            # Send the encrypted data to the peer
            self._sock.send(encrypted_data)
            length_to_read = self._network_bio.pending()
            final_length += length_to_read

        return final_length


    def shutdown(self):
        # type: () -> None
        self._is_handshake_completed = False
        try:
            self._ssl.shutdown()
            self._flush_ssl_engine()
        except OpenSSLError as e:
            # Ignore "uninitialized" exception
            if 'SSL_shutdown:uninitialized' not in str(e) and 'shutdown while in init' not in str(e):
                raise


    def _set_verify(self, verify_mode):
        # type: (int) -> None
        """Set the OpenSSL verify mode. Private method because it should be set via the constructor.
        """
        self._ssl._set_verify(verify_mode)


    def set_tlsext_host_name(self, name_indication):
        # type: (str) -> None
        """Set the hostname within the Server Name Indication extension in the client SSL Hello.
        """
        self._ssl.set_tlsext_host_name(name_indication)


    def get_peer_certificate(self):
        # type: () -> Optional[X509Certificate]
        _x509 = self._ssl.get_peer_certificate()
        if _x509:
            return X509Certificate(_x509)
        else:
            return None


    def get_peer_cert_chain(self):
        # type: () -> List[X509Certificate]
        """See the OpenSSL documentation for differences between get_peer_cert_chain() and get_peer_certificate().
        https://www.openssl.org/docs/ssl/SSL_get_peer_cert_chain.html
        """
        x509_list = self._ssl.get_peer_cert_chain()
        final_list = []
        if x509_list:
            for x509_cert in x509_list:
                final_list.append(X509Certificate(x509_cert))

        return final_list


    def set_cipher_list(self, cipher_list):
        # type: (str) -> None
        self._ssl.set_cipher_list(cipher_list)


    def get_cipher_list(self):
        # type: () -> List[str]
        return self._ssl.get_cipher_list()


    def get_current_cipher_name(self):
        # type: () -> str
        return self._ssl.get_cipher_name()


    def get_current_cipher_bits(self):
        # type: () -> int
        return self._ssl.get_cipher_bits()


    def _use_private_key(self, client_certchain_file, client_key_file, client_key_type, client_key_password):
        # type: (str, str, int, str) -> None
        """The certificate chain file must be in PEM format. Private method because it should be set via the
        constructor.
        """
        self._ssl_ctx.use_certificate_chain_file(client_certchain_file)

        if isinstance(client_key_password, basestring):
            self._ssl_ctx.set_private_key_password(client_key_password)
        else:
            raise TypeError('client_key_password is not a string')

        try:
            self._ssl_ctx.use_PrivateKey_file(client_key_file, client_key_type)
        except OpenSSLError as e:
            if 'bad password read' in str(e) or 'bad decrypt' in str(e):
                raise ValueError('Invalid Private Key')
            else:
                raise

        self._ssl_ctx.check_private_key()


    def get_certificate_chain_verify_result(self):
        # type: () -> Tuple[int, str]
        verify_result = self._ssl.get_verify_result()
        verify_result_str = X509.verify_cert_error_string(verify_result)
        return verify_result, verify_result_str


    def set_tlsext_status_ocsp(self):
        # type: () -> None
        """Enable the OCSP Stapling extension.
        """
        self._ssl.set_tlsext_status_type(TLSEXT_STATUSTYPE_ocsp)


    def get_tlsext_status_ocsp_resp(self):
        # type: () -> Optional[OcspResponse]
        """Retrieve the server's OCSP Stapling status.
        """
        ocsp_response = self._ssl.get_tlsext_status_ocsp_resp()
        if ocsp_response:
            return OcspResponse(ocsp_response)
        else:
            return None


    def get_client_CA_list(self):
        # type: () -> List[str]
        if not self._client_CA_list:
            self._client_CA_list = self._ssl.get_client_CA_list()
        return self._client_CA_list


    def set_mode(self, mode):
        # type: (int) -> None
        self._ssl.set_mode(mode)