TypeError: wrap_socket() got an unexpected keyword argument 'keyfile'
There is no keyfile argument when calling context.wrap_socket(...). There is a keyfile argument when calling ssl.wrap_socket(...). The reason for this is that the context should be created with a keyfile if needed while ssl.wrap_socket(...) creates a new context.
For more details see documentation of context.wrap_socket vs. documentation of ssl.wrap_socket.
It is an eventlet bug. If it is possible you need to downgrade to Python 3.6.
https://github.com/eventlet/eventlet/issues/526
Nameko has a PR for this issue which is on pause until the above is fixed.
https://github.com/nameko/nameko/pull/644
I caught the same error with python 3.7, eventlet 0.25.2, requests 2.24.0. It works fine with requests 2.23.0
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, the Common Name string position depends on how many field did specified in the certificate. I had been lazy, so my subject has only 4 fiels, and Common Name is the last of them.
Now it works (feel free to ask for further details).
Server
#!/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()
Client
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()
Take a look:

Ps: I made the client print the server response.
Response to comments
On client's side you never used the context variable I've created. Does it mean it's unnecessary here?
Documentation says:
For more sophisticated applications, the
ssl.SSLContextclass helps manage settings and certificates, which can then be inherited by SSL sockets created through theSSLContext.wrap_socket()method.
I've updated the code to show you the differences: the server uses ssl.wrap_socket(), the client ssl.SSLContext.wrap_socket().
Second, what's the point in checking if ssl.HAS_SNI when the socket creation looks the same in if and else? With your approach I cant use server_hostname=HOST in socket wrapping method.
You are right, in the updated code I used server_hostname=HOST.
Another thing: you're using ca_certs instead of using load_verify_locations in context I created. Why? Are those 2 methods identical?
My fault, I was using ca_cert as parameter of ssl.wrap_socket(), so I didn't used the context at all. Now I use it.
And another thing: do you really need to call
secure_sock.do_handshake()by yourself?
Nope, I forgot to remove it :)
The output is exactly the same.
ilario-pierbattista Answer but in python 3:
- Check print function
- Check secure_sock.write(b'hello') in bytes
- Check function argument (config)
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()
The patch you're mentioning is for Python 3.2, and you're using Python 2.7. Issue 5639 also seems to indicate there is no plan to back-port SNI support for Python 2.7.
You could wrap the socket with pyOpenSSL instead (its Connection class has a set_tlsext_host_name since version 0.13. (I'm not sure which version comes with Debian 7.3, you might want to set up a virtualenv and upgrade to a newer version locally, if needed.)
There is an SNI example is the pyOpenSSL repository.
If you want your usage of wrap_socket to be more compatible with the trick were you replace the value of sock in an httplib connection, you could have a look at how urllib3 does this with pyOpenSSL. Essentially, it creates an OpenSSL.SSL.Connection from an existing socket, but since that connection isn't compatible with a socket, it wraps it into a class that implements the required method.
(By the way, in Python 2.7, urllib, urllib2 and httpconnection don't do any certificate verification at all, unless you implement it yourself by wrapping their sockets.)
EDIT:
Here is a version of your code that should work with Python 3.2. Unfortunately, the server_name parameter isn't in the plain ssl.wrap_socket, only in SSLContext.wrap_socket, but you can use SSLSocket directly.
import socket
import ssl
CA_BUNDLE_FILE="/etc/ssl/certs/ca-certificates.crt"
HOST = "sni.velox.ch"
s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s2 = ssl.SSLSocket(sock=s1, ca_certs=CA_BUNDLE_FILE,
cert_reqs=ssl.CERT_REQUIRED,
ssl_version=ssl.PROTOCOL_TLSv1,
server_hostname=HOST)
s2.connect((HOST, 443))
s2.send(bytes("GET / HTTP/1.1\n", "UTF-8"))
# This might need to be modified when using another port
s2.send(bytes("Host: %s\n" % (HOST,), "UTF-8"))
s2.send(bytes("\n", "UTF-8"))
# Certainly not the best way to read the response, but it works.
while True:
x = s2.read()
if not x:
break
print(x)
My env is:requests==2.7.0, python-2.7.5-34.el7.x86_64, gevent==1.0.2
Change python version to python-2.7.5-18.el7_1.1.x86_64, problem is solved.
on CentOS:
sudo rpm -Uvh --oldpackage python-devel-2.7.5-18.el7_1.1.x86_64.rpm python-libs-2.7.5-18.el7_1.1.x86_64.rpm python-2.7.5-18.el7_1.1.x86_64.rpm
pakages can search on google.