Python SSL验证失败

时间:2018-02-23 01:46:02

标签: python ssl ssl-certificate lets-encrypt

我正在尝试设置Python TCP客户端 - 服务器会话,但客户端却抛出此错误:[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:777)

我正在使用新生成的LetsEncrypt证书。我已将fullchain.pem(公共证书)存储在我运行客户端的计算机上,但它无法验证其真实性。我确信fullchain.pem是公钥,privkey.pem是私钥,因此我不明白为什么这不起作用。我也尝试过将cert.pem用于公众,这也不起作用。是否有人能够提供有关验证失败原因的一些见解?以下是客户端和服务器程序。

客户端:

import sys
import socket
import os
import ssl
from backports.ssl_match_hostname import match_hostname, CertificateError


def connect(hostname, port, message):
    """
    Connects to the hostname on the specified port and sends a message

    :param hostname:
    :param port:
    :param message:
    :return:
    """

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # create a TCP socket
    try:
        s.connect((hostname, port))  # establish a TCP connection to the host
        #ca_certs_path = os.path.join(os.path.dirname(sys.argv[0]), 'fullchain.pem')
        ctx = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH)
        #sslsock = ctx.wrap_socket(s, ssl_version=ssl.PROTOCOL_SSLv23, cert_reqs=ssl.CERT_REQUIRED, ca_certs=ca_certs_path)
        sslsock = ctx.wrap_socket(s, server_hostname='encryptio.tk')

        # Check if the server really matches the hostname to which we are trying to connect
        match_hostname(sslsock.getpeercert(), hostname)

        sslsock.send(message.encode('utf-8'))  # send the message
    except Exception as e:
        sys.exit('[-]' + str(e))


def main():
    connect('encryptio.tk', 12000, 'Client says hello')  # replace '' with the hostname, 12000 as the port, and 'Hello' as the message


if __name__ == '__main__':
    main()

服务器:

import sys
import socket
import ssl
from backports.ssl_match_hostname import match_hostname, CertificateError

# Servers do not care whether clients connect with certificates

def listen(port):
    """
    Listens on the specified port and accepts incoming connections

    :param port:
    :return:
    """

    # server_socket - welcoming socket
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind(('', port))  # establish welcoming socket

    ca_certs_path = '/etc/letsencrypt/live/encryptio.tk/fullchain.pem'
    priv_certs_path = '/etc/letsencrypt/live/encryptio.tk/privkey.pem'

    # listen for TCP connection requests.
    server_socket.listen(1)  # parameter specifies the maximum number of queued connections (at least 1)

    ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
    ctx.load_cert_chain(certfile=ca_certs_path, keyfile=priv_certs_path)

    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    print('[*] The server is ready to receive')

    while 1:
        try:
            # When a client knocks on this door, the program invokes the accept() method for
            # server_socket, which creates a new socket in the server, called connec-
            # tion_ocket, dedicated to this particular client. The client and server then complete
            # the handshaking, creating a TCP connection between the client’s client_socket
            # and the server’s connection_socket
            #connection_socket, addr = server_socket.accept()

            #sslsock = ssl.wrap_socket(server_socket, ssl_version=ssl.PROTOCOL_SSLv23, cert_reqs=ssl.CERT_REQUIRED, server_side=True, certfile=ca_certs_path, keyfile=priv_certs_path)
            connection_socket, addr = server_socket.accept()
            sslsock = ctx.wrap_socket(connection_socket, server_side=True)
            data = sslsock.read()
            #message = connection_socket.recv(1500)

            print('[+] From' + str(addr) + ': ' + 
data.decode('utf-8'))
        except Exception as e:
            sys.exit('[-] ' +  str(e))

        #connection_socket.close()
        sslsock.shutdown(socket.SHUT_RDWR)
        sslsock.close()


def main():
    listen(12000)


if __name__ == '__main__':
    main()

编辑: 我尝试将客户端程序中的CA设置为根证书,现在我收到此错误:

[X509] PEM lib (_ssl.c:3053)

1 个答案:

答案 0 :(得分:0)

在您的客户中,您有:

 ca_certs_path = os.path.join(os.path.dirname(sys.argv[0]), 'fullchain.pem')
     sslsock = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_SSLv23, 
 cert_reqs=ssl.CERT_REQUIRED, ca_certs=ca_certs_path)

ca_certs中的ssl.wrap_socket默认值为无,这意味着如果您使用非默认ssl.CERT_REQUIRED,则必须提供有用的路径。只是,您提供的路径似乎是您在服务器中使用的证书链,即服务器证书和链证书,但不是根证书。相反,客户端上的ca_certs应该只包含根证书,因为这是信任锚,用于使用服务器发送的证书(即服务器证书和链)构建信任链。

您也可以使用系统范围的商店,而不是使用自己的CA商店。使用当前版本的Python,您只需使用以下代码,即使用默认CA存储创建SSL上下文并启用证书和主机名验证,然后将套接字升级到SSL并正确检查证书:

 s.connect((hostname, port))
 ctx = ssl.create_default_context()
 sslsock = ctx.wrap_socket(s, server_hostname='example.com')