client ssl verification

Robin Becker robin at reportlab.com
Thu Mar 15 15:31:03 CET 2012


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




More information about the Python-list mailing list