简单的ECHO客户端/服务器[Python / sockets / ssl模块]中的相互ssl身份验证,ssl.SSLEOFError:EOF发生违反协议

时间:2017-06-03 11:04:50

标签: python authentication ssl pyopenssl

我想在echo客户端/服务器程序中进行相互身份验证。我在

上使用IContainer container = IContainer.make(); ssl`模块
python 2.7.12 and the

我使用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

在客户方:

Traceback (most recent call last):
  File "ssl_server.py", line 18, in <module>
    secure_sock = ssl.wrap_socket(client, server_side=True, certfile="server.pem", keyfile="server.key")
  File "/usr/lib/python2.7/ssl.py", line 933, in wrap_socket
    ciphers=ciphers)
  File "/usr/lib/python2.7/ssl.py", line 601, in __init__
    self.do_handshake()
  File "/usr/lib/python2.7/ssl.py", line 830, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLEOFError: EOF occurred in violation of protocol (_ssl.c:590)

服务器代码:

Traceback (most recent call last):
  File "ssl_client.py", line 18, in <module>
    secure_sock = context.wrap_socket(sock, server_hostname=HOST, server_side=False, certfile="client.pem", keyfile="client.key")
TypeError: wrap_socket() got an unexpected keyword argument 'certfile'

客户代码:

#!/bin/usr/env python
import socket
import ssl
import pprint

#server
if __name__ == '__main__':

    HOST = '127.0.0.1'
    PORT = 1234

    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind((HOST, PORT))
    server_socket.listen(10)

    client, fromaddr = server_socket.accept()
    secure_sock = ssl.wrap_socket(client, server_side=True, certfile="server.pem", keyfile="server.key")

    print repr(secure_sock.getpeername())
    print secure_sock.cipher()
    print pprint.pformat(secure_sock.getpeercert())
    cert = secure_sock.getpeercert()
    print cert

    # verify client
    if not cert or ('commonName', 'test') not in cert['subject'][4]: raise Exception("ERROR")

    try:
        data = secure_sock.read(1024)
        secure_sock.write(data)
    finally:
        secure_sock.close()
        server_socket.close()

谢谢。

2 个答案:

答案 0 :(得分:8)

基本上服务器需要与客户端共享他的证书,反之亦然(查看ca_certs参数)。您的代码的主要问题是握手从未执行过。此外,Common Name字符串位置取决于证书中指定的字段数。我一直很懒,所以我的subject只有4个fiels,Common Name是最后一个。

现在它有效(请随时询问更多细节)。

服务器

#!/bin/usr/env python
import socket
import ssl
import pprint

#server
if __name__ == '__main__':

    HOST = '127.0.0.1'
    PORT = 1234

    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind((HOST, PORT))
    server_socket.listen(10)

    client, fromaddr = server_socket.accept()
    secure_sock = ssl.wrap_socket(client, server_side=True, ca_certs = "client.pem", certfile="server.pem", keyfile="server.key", cert_reqs=ssl.CERT_REQUIRED,
                           ssl_version=ssl.PROTOCOL_TLSv1_2)

    print repr(secure_sock.getpeername())
    print secure_sock.cipher()
    print pprint.pformat(secure_sock.getpeercert())
    cert = secure_sock.getpeercert()
    print cert

    # verify client
    if not cert or ('commonName', 'test') not in cert['subject'][3]: raise Exception("ERROR")

    try:
        data = secure_sock.read(1024)
        secure_sock.write(data)
    finally:
        secure_sock.close()
        server_socket.close()

客户端

import socket
import ssl

# client
if __name__ == '__main__':

    HOST = '127.0.0.1'
    PORT = 1234

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setblocking(1);
    sock.connect((HOST, PORT))

    context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
    context.verify_mode = ssl.CERT_REQUIRED
    context.load_verify_locations('server.pem')
    context.load_cert_chain(certfile="client.pem", keyfile="client.key")

    if ssl.HAS_SNI:
        secure_sock = context.wrap_socket(sock, server_side=False, server_hostname=HOST)
    else:
        secure_sock = context.wrap_socket(sock, server_side=False)

    cert = secure_sock.getpeercert()
    print cert

    # verify server
    if not cert or ('commonName', 'test') not in cert['subject'][3]: raise Exception("ERROR")

    secure_sock.write('hello')
    print secure_sock.read(1024)

    secure_sock.close()
    sock.close()

看看:

Proof

Ps:我让客户打印服务器响应。

对评论的回应

  

在客户方面,您从未使用我创建的上下文变量。这是否意味着它不必要?

文档说:

  

对于更复杂的应用程序,ssl.SSLContext类有助于管理设置和证书,然后可以通过SSLContext.wrap_socket()方法创建的SSL套接字继承这些设置和证书。

我已更新代码以向您显示差异:服务器使用ssl.wrap_socket(),客户端ssl.SSLContext.wrap_socket()

  

第二,在if和else中,套接字创建看起来是否相同时检查ssl.HAS_SNI的重点是什么?使用您的方法,我无法在套接字换行方法中使用server_hostname = HOST。

你是对的,在我使用server_hostname=HOST的更新代码中。

  

另一件事:你在我创建的上下文中使用ca_certs而不是使用load_verify_locations。为什么?这两种方法是否相同?

我的错,我使用ca_cert作为ssl.wrap_socket()的参数,所以我根本没有使用context。现在我用它。

  

还有一件事:你真的需要自己打电话给secure_sock.do_handshake()吗?

不,我忘了删除它:)

输出完全相同。

答案 1 :(得分:0)

ilario-pierbattista回答,但在python 3中:

  • 检查打印功能
  • 以字节为单位检查secure_sock.write(b'hello')
  • 检查函数参数(配置)
def start_client_side(config):
    HOST = config['host']
    PORT = config['port']
    pemServer = config['serverpem']
    keyClient = config['clientkey']
    pemClient = config['clientpem']

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setblocking(1);
    sock.connect((HOST, PORT))

    context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
    context.verify_mode = ssl.CERT_REQUIRED
    context.load_verify_locations(pemServer)
    context.load_cert_chain(certfile=pemClient, keyfile=keyClient)

    if ssl.HAS_SNI:
        secure_sock = context.wrap_socket(sock, server_side=False, server_hostname=HOST)
    else:
        secure_sock = context.wrap_socket(sock, server_side=False)

    cert = secure_sock.getpeercert()
    print(pprint.pformat(cert))

    # verify server
    if not cert or ('commonName', 'server.utester.local') not in itertools.chain(*cert['subject']): raise Exception("ERROR")

    secure_sock.write(b'hello')
    print(secure_sock.read(1024))

    secure_sock.close()
    sock.close()

def start_server_side(config):
    HOST = config['host']
    PORT = config['port']
    pemServer = config['serverpem']
    keyServer = config['serverkey']
    pemClient = config['clientpem']

    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind((HOST, PORT))
    server_socket.listen(10)

    client, fromaddr = server_socket.accept()
    secure_sock = ssl.wrap_socket(client, server_side=True, ca_certs=pemClient, certfile=pemServer,
                                  keyfile=keyServer, cert_reqs=ssl.CERT_REQUIRED,
                                  ssl_version=ssl.PROTOCOL_TLSv1_2)

    print(repr(secure_sock.getpeername()))
    print(secure_sock.cipher())
    cert = secure_sock.getpeercert()
    print(pprint.pformat(cert))

    # verify client
    if not cert or ('commonName', 'client.utester.local') not in itertools.chain(*cert['subject']): raise Exception("ERROR")

    try:
        data = secure_sock.read(1024)
        secure_sock.write(data)
    finally:
        secure_sock.close()
        server_socket.close()