我正在尝试设置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)
答案 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')