エコークライアント/サーバープログラムで相互認証を行いたいのですが。私はpython 2.7.12 and the
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
クライアントにサーバーを認証させ、サーバーにクライアントを認証させたい。ただし、以下のコードは、サーバー側でいくつかのエラーを示しています。
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()
ありがとうございました。
基本的に、サーバーはクライアントと証明書を共有する必要があり、その逆も同様です(_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()
_
見てください:
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()
を呼び出す必要がありますか?
いいえ、削除するのを忘れました:)
出力はまったく同じです。
ilario-pierbattista回答python 3:
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()