web-dev-qa-db-ja.com

単純なECHOクライアント/サーバーでの相互ssl認証[Python /ソケット/ sslモジュール]、ssl.SSLEOFError:EOFプロトコル違反

エコークライアント/サーバープログラムで相互認証を行いたいのですが。私はpython 2.7.12 and thessl`モジュールを使用しています

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()

クライアントのコード:

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.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, server_side=False, certfile="client.pem", keyfile="client.key")
    else:
        secure_sock = context.wrap_socket(sock, server_side=False, certfile="client.pem", keyfile="client.key")

    cert = secure_sock.getpeercert()
    print cert

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

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

    secure_sock.close()
    sock.close()

ありがとうございました。

14
yak

基本的に、サーバーはクライアントと証明書を共有する必要があり、その逆も同様です(_ca_certs_パラメーターを確認)。コードの主な問題は、ハンドシェイクが実行されなかったことです。また、_Common Name_文字列の位置は、証明書で指定されたフィールドの数によって異なります。私は怠惰だったので、私のsubjectには4つのフィールドしかなく、_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_を使用した更新されたコードでは。

もう1つ:私が作成したコンテキストでload_verify_locationsを使用する代わりに、ca_certsを使用しています。どうして?これら2つの方法は同じですか?

私のせいで、ssl.wrap_socket()のパラメーターとして_ca_cert_を使用していたため、contextをまったく使用していませんでした。今はそれを使っています。

そして、もう1つ、本当に自分でsecure_sock.do_handshake()を呼び出す必要がありますか?

いいえ、削除するのを忘れました:)

出力はまったく同じです。

9

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()
1
Agustincl