I'm trying to do client ssl verification with code that looks like the sample
below. I am able to reach and read urls that are secure and have no client
certificate requirement OK. If I set explicit_check to True then verbose output
indicates that the server certs are being checked fine ie I see the correct cert
details and am able to check them.
However, when I try to reach an apache location like
<Location /media/secret>
sslverifyclient require
sslverifydepth 10
</Location>
I am getting an error from urllib2 that goes like this
urllib2.py", line 1148, in do_open
raise URLError(err)
URLError: <urlopen error [Errno 1] _ssl.c:1347: error:14094418:SSL
routines:SSL3_READ_BYTES:tlsv1 alert unknown ca
I am using the server.crt and server.key (both in PEM format) from the target
server itself; I reasoned that should be the easiest combo for the client &
server to match, but I am obviously wrong. Any obvious stupidities to be pointed
out? I suppose I could create a new cert/key based on a self signed ca, but that
would not work properly for the other parts of the server.
import socket, ssl, fnmatch, datetime, urllib2, httplib
verbose=False
# wraps https connections with ssl certificate verification
class SecuredHTTPSHandler(urllib2.HTTPSHandler):
def
__init__(self,key_file=None,cert_file=None,ca_certs=None,explicit_check=False):
class SecuredHTTPSConnection(httplib.HTTPSConnection):
def connect(self):
# overrides the version in httplib so that we do
# certificate verification
sock = socket.create_connection((self.host, self.port), self.timeout)
if self._tunnel_host:
self.sock = sock
self._tunnel()
# wrap the socket using verification with the root
# certs in ca_certs
if verbose:
print ca_certs, key_file, cert_file
self.sock = ssl.wrap_socket(sock,
cert_reqs=ssl.CERT_REQUIRED,
ca_certs=ca_certs,
keyfile=key_file,
certfile=cert_file,
)
if explicit_check:
cert = self.sock.getpeercert()
if verbose:
import pprint
pprint.pprint(cert)
for key,field in cert.iteritems():
if key=='subject':
sd = dict([x[0] for x in field])
certhost = sd.get('commonName')
if not fnmatch.fnmatch(self.host,certhost):
raise ssl.SSLError("Host name '%s' doesn't match certificate host
'%s'"
% (self.host, certhost))
if verbose:
print 'matched "%s" to "%s"' % (self.host,certhost)
elif key=='notAfter':
now = datetime.datetime.now()
crttime = datetime.datetime.strptime(field,'%b %d %H:%M:%S %Y %Z')
if verbose:
print 'crttime=%s now=%s' % (crttime,now)
if now>=crttime:
raise ssl.SSLError("Host '%s' certificate expired on %s"
% (self.host, field))
self.specialized_conn_class = SecuredHTTPSConnection
urllib2.HTTPSHandler.__init__(self)
def https_open(self, req):
return self.do_open(self.specialized_conn_class, req)
def secureDataGet(uri,ca_certs='cacert.pem',key_file=None,cert_file=None,
explicit_check=False):
https_handler = SecuredHTTPSHandler(key_file=key_file,cert_file=cert_file,
ca_certs=ca_certs,explicit_check=explicit_check)
url_opener = urllib2.build_opener(https_handler)
handle = url_opener.open(uri)
response = handle.readlines()
handle.close()
return response
--
Robin Becker
--
http://mail.python.org/mailman/listinfo/python-list