I would like to have a mutual authentication in my echo client/server program. I'm using python 2.7.12 and the
ssl` module on
Distributor ID: Ubuntu
Description: Ubuntu 14.04.5 LTS
Release: 14.04
Codename: trusty
I've generated client's and server's certificates and keys using the openssl
commands:
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
I want the client to authenticate the server, and I want the server to authenticate the client. However, the code below shows some errors, at server's side:
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)
At client' side:
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'
Server's code:
#!/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()
Client's code:
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()
Thank you.
Basically the server need to share with the client his certificate and vice versa (look the
ca_certs
parameter). The main problem with your code is that the handshake were never executed. Also, theCommon Name
string position depends on how many field did specified in the certificate. I had been lazy, so mysubject
has only 4 fiels, andCommon Name
is the last of them.Now it works (feel free to ask for further details).
Server
Client
Take a look:
Ps: I made the client print the server response.
Response to comments
Documentation says:
I've updated the code to show you the differences: the server uses
ssl.wrap_socket()
, the clientssl.SSLContext.wrap_socket()
.You are right, in the updated code I used
server_hostname=HOST
.My fault, I was using
ca_cert
as parameter ofssl.wrap_socket()
, so I didn't used thecontext
at all. Now I use it.Nope, I forgot to remove it :)
The output is exactly the same.