【发布时间】:2017-11-04 17:44:37
【问题描述】:
我想在我的 echo 客户端/服务器程序中对客户端的服务器进行身份验证。我在
上使用python 2.7.12 和ssl 模块
Distributor ID: Ubuntu
Description: Ubuntu 14.04.5 LTS
Release: 14.04
Codename: trusty
我已经使用 openssl 命令生成了客户端和服务器的证书和密钥:
openssl req -new -x509 -days 365 -nodes -out client.pem -keyout client.key
openssl req -new -x509 -days 365 -nodes -out server.pem -keyout server.key
openssl库本身和python使用的openssl版本是一样的:
openssl version -a
OpenSSL 1.0.1f 6 Jan 2014
built on: Fri Sep 23 12:19:57 UTC 2016
platform: debian-amd64
options: bn(64,64) rc4(16x,int) des(idx,cisc,16,int) blowfish(idx)
compiler: cc -fPIC -DOPENSSL_PIC -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -m64 -DL_ENDIAN -DTERMIO -g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2 -Wl,-Bsymbolic-functions -Wl,-z,relro -Wa,--noexecstack -Wall -DMD32_REG_T=int -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DMD5_ASM -DAES_ASM -DVPAES_ASM -DBSAES_ASM -DWHIRLPOOL_ASM -DGHASH_ASM
OPENSSLDIR: "/usr/lib/ssl"
python -c "import ssl; print ssl.OPENSSL_VERSION"
OpenSSL 1.0.1f 6 Jan 2014
但是,下面的代码显示了一些错误,在服务器端:EOF occurred in violation of protocol (_ssl.c:1645)(但服务器仍然工作),在客户端:
Traceback (most recent call last):
File "/http_ssl_client.py", line 36, in <module>
if not cert or ('commonName', 'test') not in cert['subject'][4]: raise Exception("Invalid SSL cert for host %s. Check if this is a man-in-themiddle attack!" )
Exception: Invalid SSL cert for host %s. Check if this is a man-in-themiddle attack!
{'notBefore': u'Jun 3 11:54:21 2017 GMT', 'serialNumber': u'BBDCBEED69655B6E', 'notAfter': 'Jun 3 11:54:21 2018 GMT', 'version': 3L, 'subject': ((('countryName', u'pl'),), (('stateOrProvinceName', u'test'),), (('localityName', u'test'),), (('organizationName', u'test'),), (('organizationalUnitName', u'test'),), (('commonName', u'test'),), (('emailAddress', u'test'),)), 'issuer': ((('countryName', u'pl'),), (('stateOrProvinceName', u'test'),), (('localityName', u'test'),), (('organizationName', u'test'),), (('organizationalUnitName', u'test'),), (('commonName', u'test'),), (('emailAddress', u'test'),))}
服务器代码:
#!/bin/usr/env python
import socket
import ssl
def main():
HOST = '127.0.0.1'
PORT = 1234
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((HOST, PORT))
sock.listen(5)
while True:
conn = None
client_sock, addr = sock.accept()
try:
ssl_client = ssl.wrap_socket(client_sock, server_side=True, certfile="server.pem", keyfile="server.key", ssl_version=ssl.PROTOCOL_TLSv1_2)
data = ssl_client.read(1024)
print data
ssl_client.write(data)
except ssl.SSLError as e:
print(e)
finally:
if conn:
conn.close()
if __name__ == '__main__':
main()
客户:
#!/bin/usr/env python
import socket
import ssl
if __name__ == '__main__':
HOST = '127.0.0.1'
PORT = 1234
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.verify_mode = ssl.CERT_REQUIRED
context.load_verify_locations('server.pem')
if ssl.HAS_SNI:
secure_sock = context.wrap_socket(sock, server_hostname=HOST)
else:
secure_sock = context.wrap_socket(sock)
cert = secure_sock.getpeercert()
print cert
if not cert or ('commonName', 'test') not in cert['subject'][4]: raise Exception("Error" )
secure_sock.write('hello')
print secure_sock.read(1024)
secure_sock.close()
sock.close()
所有文件都在同一个目录中。
【问题讨论】:
-
当您完成向 SSL 套接字写入数据后,您应该调用
unwrap()方法向对等方发送关闭通知警报。只有这样你才应该关闭(或关闭)套接字。 -
@JamesKPolk:很好,但改变它并没有帮助,问题仍然存在:(
-
在尝试重现时,我注意到对于您的示例输入
('commonName', 'test') in cert['subject'][4]将始终为False,它应该是cert['subject'][5],但最好将其展平然后执行检查。您能否看一下正确的代码、正确的输入或解释为什么它应该保持这种状态? -
@TomaszPlaskota:这真的很奇怪。我不确定它应该保持这样,我只是想让它工作。我将此行更改为
if not cert or cert['subject'][4][0][1] != 'test': raise Exception("Error" ),现在它可以工作了。这里更重要的问题是我不确定我是否正确使用了证书。据我了解,我从客户端和服务器生成了一个自签名证书。但是,为了让客户端能够对服务器进行身份验证,我应该在客户端站点使用context.load_verify_locations('server.pem')。我在吗?