Python简易信使SSL/TLS证书验证失败:IP地址不匹配问题求助
ssl.SSLCertVerificationError: IP地址不匹配,自签名证书验证失败
问题描述
开发Python简易信使程序时,引入SSL/TLS加密后出现以下报错:
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: IP address mismatch, certificate is not valid for 'XXX.XXX.X.XX'. (_ssl.c:997)
使用对应IP的自签名证书,多次重新生成仍无法解决。证书详情如下:
Certificate: Data: Version: 1 (0x0) Serial Number: 0e:0e:79:f2:c7:95:1f:8f:e4:ac:dc:d4:b3:63:83:2f:a2:64:70:fa Signature Algorithm: sha256WithRSAEncryption Issuer: C = AU, ST = Some-State, O = Internet Widgits Pty Ltd, CN = 192.168.X.1XX Validity Not Before: Jun 23 15:24:15 2023 GMT Not After : Jun 22 15:24:15 2024 GMT Subject: C = AU, ST = Some-State, O = Internet Widgits Pty Ltd, CN = 192.168.X.XX Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:a5:7d:94:14:9d:6d:cb:88:35:a7:f3:92:a8:28: c4:ac:3d:5b:ba:dd:1f:5d:c6:d8:a4:97:bb:c1:b9: 6f:c6:0d:af:27:a2:3f:61:fd:1c:75:9b:0b:09:bf: 35:25:44:c8:d8:44:14:2a:e3:c1:ee:4a:3f:d5:2f: 98:fc:bf:fd:63:dd:1e:62:78:f5:5d:69:f9:46:43: 9e:fb:32:e3:a8:a6:f2:da:c3:22:93:92:dc:9f:92: 6f:73:8c:12:19:34:18:31:1a:ba:69:91:63:d0:fa: 9a:47:52:21:04:f9:85:e7:c3:65:60:0f:f2:b3:0c: 00:37:03:8b:11:f7:53:aa:41:e9:e6:93:94:d4:eb: 32:40:2b:fd:09:e8:0a:a4:1d:24:3c:73:6d:c0:08: 5a:72:5e:41:a5:1f:61:21:65:8d:5b:16:48:ee:63: 86:19:c3:2c:98:28:6a:fb:0d:7f:fd:13:38:83:e4: 78:6e:5c:dd:2e:72:5f:5c:72:89:fc:1e:83:dc:a8: 46:bd:44:6c:12:45:2b:8b:85:18:b7:e0:26:9c:8a: 40:dd:f9:de:d7:21:fa:34:b4:72:e3:2b:d4:87:9f: 2f:5e:00:5b:56:24:97:ad:38:d2:ec:97:8a:f9:d5: 41:5c:98:25:f3:34:0c:56:d3:6d:80:8d:b4:e4:72: 5d:75 Exponent: 65537 (0x10001) Signature Algorithm: sha256WithRSAEncryption Signature Value: 64:29:2d:50:50:fa:43:5a:a4:44:77:86:87:2f:65:94:06:73: 20:b7:66:27:fd:c8:86:6c:ac:30:51:81:b7:6d:66:33:fb:d8: 92:4b:e6:99:8f:a1:04:4a:ba:1e:d5:0e:7f:e2:5b:f8:aa:0c: 8f:91:65:95:70:de:38:8a:cf:bf:2d:7f:59:c4:c0:31:1c:aa: af:a9:1e:31:69:ce:26:d0:2b:b7:32:fc:28:c6:57:37:aa:1b: 31:12:74:28:ed:63:57:58:7a:3f:c9:65:8c:54:b2:97:03:3f: 48:da:ff:9c:99:4e:11:67:78:55:a6:a7:bf:24:a4:d5:76:4f: 37:54:b1:01:00:f1:ac:f5:b8:74:51:fe:3d:37:4d:bd:7b:c8: 04:06:59:28:1d:49:86:80:21:eb:a3:87:a0:b2:f8:43:14:dd: ad:80:fe:6c:59:41:03:31:ff:65:0e:98:12:3f:66:45:1b:c9: 8c:05:97:ea:69:94:a4:2f:d3:28:b4:5e:54:91:2a:8e:40:ed: 7c:fe:30:bb:3b:75:99:9e:cf:b2:a8:71:eb:38:85:10:d2:0c: e1:bb:44:69:a1:43:01:0b:30:09:47:ae:3e:c3:98:b8:ce:e7: 65:b5:05:10:f0:c1:bc:11:a0:84:d4:00:8f:04:05:e4:b2:93: 40:94:f4:1e
服务端代码:
import socket import ssl # Set port number port = 443 HOST = '192.168.X.XX' # Load key and certificate keyfile = '/etc/ssl/private/serverkey.pem' certfile = '/etc/ssl/certs/servercert.pem' context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) context.load_cert_chain(certfile, keyfile) # Create socket and bind to port sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind((HOST, port)) sock.listen() print(f'Waiting for a connection...') # Accept incoming connections and wrap in SSL while True: conn, addr = sock.accept() ssl_conn = context.wrap_socket(conn, server_side=True) print(f'Connected by {addr}') # Receive and send messages while True: data = ssl_conn.recv(1024) if not data: break print(f'Received: {data.decode()}') message = input('Enter message to send: ') ssl_conn.sendall(message.encode())
客户端代码:
import socket import ssl # Set server hostname or IP address and port number server = '192.168.X.X' port = 443 # Load key and certificate keyfile = '/etc/ssl/private/serverkey.pem' certfile = '/etc/ssl/certs/servercert.pem' # Create TCP socket and connect to server sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((server, port)) # Create SSL context and wrap socket in SSL context = ssl.create_default_context() context.load_cert_chain(certfile, keyfile) context.load_verify_locations(cafile=certfile) #context.check_hostname = False ssl_sock = context.wrap_socket(sock, server_hostname=server) # Send and receive messages while True: message = input('Enter message to send: ') ssl_sock.sendall(message.encode()) data = ssl_sock.recv(1024) if not data: break print(f'Received: {data.decode()}') # Close SSL socket and TCP socket ssl_sock.close() sock.close()
解决方案
1. 修正客户端连接IP与证书一致性
客户端代码中server变量设置为192.168.X.X,但证书Subject的CN是192.168.X.XX,IP地址不匹配是直接诱因。将客户端的server值改为与服务端IP、证书CN一致的地址:
server = '192.168.X.XX'
2. 为自签名证书添加IP SAN字段(核心修复)
现代TLS验证已不再认可仅通过CN字段验证IP地址,必须在证书中添加**Subject Alternative Name(SAN)**字段明确指定IP地址。步骤如下:
- 创建
openssl.cnf配置文件:[req] distinguished_name = req_distinguished_name req_extensions = v3_req prompt = no [req_distinguished_name] C = AU ST = Some-State O = Internet Widgits Pty Ltd CN = 192.168.X.XX [v3_req] keyUsage = keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth subjectAltName = IP:192.168.X.XX - 使用配置文件重新生成证书和密钥:
openssl req -x509 -newkey rsa:2048 -nodes -keyout serverkey.pem -out servercert.pem -days 365 -config openssl.cnf - 将新生成的
serverkey.pem和servercert.pem替换到/etc/ssl/private/和/etc/ssl/certs/路径下。
3. 清理客户端冗余配置
客户端无需调用context.load_cert_chain,该方法用于双向认证场景(客户端向服务端提供自身证书),当前服务端未开启双向认证,删除该行即可。修正后的客户端代码:
import socket import ssl # Set server hostname or IP address and port number server = '192.168.X.XX' port = 443 # Create TCP socket and connect to server sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((server, port)) # Create SSL context and wrap socket in SSL context = ssl.create_default_context() context.load_verify_locations(cafile='/etc/ssl/certs/servercert.pem') ssl_sock = context.wrap_socket(sock, server_hostname=server) # Send and receive messages while True: message = input('Enter message to send: ') ssl_sock.sendall(message.encode()) data = ssl_sock.recv(1024) if not data: break print(f'Received: {data.decode()}') # Close SSL socket and TCP socket ssl_sock.close() sock.close()
4. 验证证书SAN字段
运行以下命令确认证书已包含正确的IP SAN:
openssl x509 -in /etc/ssl/certs/servercert.pem -text -noout | grep -A 3 "Subject Alternative Name"
输出应显示:
X509v3 Subject Alternative Name: IP Address:192.168.X.XX
内容的提问来源于stack exchange,提问作者Spaghetti
相关产品推荐
相关产品推荐

