Ignore:
Timestamp:
21/04/10 15:04:59 (11 years ago)
Author:
pjkersha
Message:

Completed port to PyOpenSSL from M2Crypto with all unit tests passed. Merge back into the main trunk as next step.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • TI12-security/branches/MyProxyClient-pyopenssl/myproxy/client.py

    r6829 r6835  
    2626import os 
    2727import socket 
    28 from M2Crypto import X509, RSA, EVP, m2, BIO, SSL, util 
    2928import base64 
     29import traceback 
     30from cStringIO import StringIO 
    3031from ConfigParser import SafeConfigParser 
     32 
     33from OpenSSL import crypto, SSL 
    3134 
    3235from myproxy.utils.openssl import OpenSSLConfig, OpenSSLConfigError 
     
    4346 
    4447 
    45 class _HostCheck(SSL.Checker.Checker): 
    46     """Override SSL.Checker.Checker to allow additional check of MyProxy  
    47     server identity.  If hostname doesn't match, allow match of host's   
    48     Distinguished Name against MYPROXY_SERVER_DN setting""" 
    49  
    50     def __init__(self,  
    51                  myProxyServerDN=os.environ.get('MYPROXY_SERVER_DN'), 
    52                  cnHostPfx='host/', 
    53                  **kw): 
    54         """Override parent class __init__ to enable setting of myProxyServerDN 
    55         setting 
    56          
    57         @type myProxyServerDN: string 
    58         @param myProxyServerDN: Set the expected Distinguished Name of the 
    59         MyProxy server to avoid errors matching hostnames.  This is useful 
    60         where the hostname is not fully qualified 
    61          
    62         @type cnHostPfx: string 
    63         @param cnHostPfx: globus host certificates are  
    64         generated by default with a 'host/' prefix to the host name.  Set 
    65         this keyword to '' or None to override and omit the prefix""" 
    66          
    67         SSL.Checker.Checker.__init__(self, **kw) 
    68          
    69         self.myProxyServerDN = myProxyServerDN 
    70         self.cnHostPfx = cnHostPfx 
    71          
    72          
    73     def __call__(self, peerCert, host=None): 
    74         """Carry out checks on server ID 
    75         @type peerCert: basestring 
    76         @param peerCert: MyProxy server host certificate as M2Crypto.X509.X509 
    77         instance 
    78         @type host: basestring 
    79         @param host: name of host to check 
    80         """ 
    81          
    82         # Globus host certificate has a "host/" prefix - see explanation in 
    83         # __init__.__doc__ 
    84         cnHostPfx = isinstance(self.cnHostPfx, basestring) \ 
    85                     and self.cnHostPfx or '' 
    86         host = None or cnHostPfx + self.host 
    87          
    88         try: 
    89             SSL.Checker.Checker.__call__(self, peerCert, host=host) 
    90              
    91         except SSL.Checker.WrongHost, e: 
    92             # Try match against DN set from MYPROXY_SERVER_DN / config 
    93             # file setting 
    94             peerCertDN = '/'+peerCert.get_subject().as_text().replace(', ','/') 
    95              
    96             # If they match drop the exception and return all OK instead 
    97             if peerCertDN != self.myProxyServerDN: 
    98                 raise 
    99              
    100         return True 
     48#class _HostCheck(SSL.Checker.Checker): 
     49#    """Override SSL.Checker.Checker to allow additional check of MyProxy  
     50#    server identity.  If hostname doesn't match, allow match of host's   
     51#    Distinguished Name against MYPROXY_SERVER_DN setting""" 
     52# 
     53#    def __init__(self,  
     54#                 myProxyServerDN=os.environ.get('MYPROXY_SERVER_DN'), 
     55#                 cnHostPfx='host/', 
     56#                 **kw): 
     57#        """Override parent class __init__ to enable setting of myProxyServerDN 
     58#        setting 
     59#         
     60#        @type myProxyServerDN: string 
     61#        @param myProxyServerDN: Set the expected Distinguished Name of the 
     62#        MyProxy server to avoid errors matching hostnames.  This is useful 
     63#        where the hostname is not fully qualified 
     64#         
     65#        @type cnHostPfx: string 
     66#        @param cnHostPfx: globus host certificates are  
     67#        generated by default with a 'host/' prefix to the host name.  Set 
     68#        this keyword to '' or None to override and omit the prefix""" 
     69#         
     70#        SSL.Checker.Checker.__init__(self, **kw) 
     71#         
     72#        self.myProxyServerDN = myProxyServerDN 
     73#        self.cnHostPfx = cnHostPfx 
     74#         
     75#         
     76#    def __call__(self, peerCert, host=None): 
     77#        """Carry out checks on server ID 
     78#        @type peerCert: basestring 
     79#        @param peerCert: MyProxy server host certificate as M2Crypto.X509.X509 
     80#        instance 
     81#        @type host: basestring 
     82#        @param host: name of host to check 
     83#        """ 
     84#         
     85#        # Globus host certificate has a "host/" prefix - see explanation in 
     86#        # __init__.__doc__ 
     87#        cnHostPfx = isinstance(self.cnHostPfx, basestring) \ 
     88#                    and self.cnHostPfx or '' 
     89#        host = None or cnHostPfx + self.host 
     90#         
     91#        try: 
     92#            SSL.Checker.Checker.__call__(self, peerCert, host=host) 
     93#             
     94#        except SSL.Checker.WrongHost, e: 
     95#            # Try match against DN set from MYPROXY_SERVER_DN / config 
     96#            # file setting 
     97#            peerCertDN = '/'+peerCert.get_subject().as_text().replace(', ','/') 
     98#             
     99#            # If they match drop the exception and return all OK instead 
     100#            if peerCertDN != self.myProxyServerDN: 
     101#                raise 
     102#             
     103#        return True 
    101104     
    102105     
     
    503506        not needed in the case of a proxy private key 
    504507        """ 
    505  
    506508        # Must be version 3 for MyProxy 
    507         context = SSL.Context(protocol='sslv3') 
    508  
     509        context = SSL.Context(SSL.SSLv3_METHOD) 
     510         
    509511        if self.caCertFilePath or self.caCertDir: 
    510             context.load_verify_locations(cafile=self.caCertFilePath, 
    511                                           capath=self.caCertDir) 
    512                              
    513             # Stop if peer's certificate can't be verified 
    514             context.set_allow_unknown_ca(False) 
    515         else: 
    516             context.set_allow_unknown_ca(True) 
     512            context.load_verify_locations(self.caCertFilePath, self.caCertDir) 
    517513             
    518514        if ownerCertFile: 
    519515            try: 
    520                 context.load_cert_chain(ownerCertFile, 
    521                                     keyfile=ownerKeyFile, 
    522                                     callback=lambda *ar, **kw: ownerPassphrase) 
    523             except Exception, e: 
     516#                context.use_certificate_file(ownerCertFile) 
     517                context.use_certificate_chain_file(ownerCertFile) 
     518                def pwdCallback(passphraseMaxLen,  
     519                                promptPassphraseTwice, 
     520                                passphrase): 
     521                    if passphrase is None: 
     522                        return True 
     523                    else: 
     524                        if len(passphrase) > passphraseMaxLen: 
     525                            log.error('Passphrase length %d is greater than ' 
     526                                      'the maximum length allowed %d', 
     527                                      len(passphrase), passphraseMaxLen) 
     528                            return '' 
     529                        return passphrase 
     530                     
     531                context.set_passwd_cb(pwdCallback, ownerPassphrase) 
     532                context.use_privatekey_file(ownerKeyFile) 
     533            except Exception: 
    524534                raise MyProxyClientConfigError("Loading certificate " 
    525535                                               "and private key for SSL " 
    526536                                               "connection [also check CA " 
    527                                                "certificate settings]: %s" % e)  
    528              
    529             # Verify peer's certificate 
    530             context.set_verify(SSL.verify_peer, 1)  
     537                                               "certificate settings]: %s" %  
     538                                               traceback.format_exc())  
     539             
     540        # Verify peer's certificate 
     541        def callback(connection, x509Cert, *arg, **kw): 
     542            return True 
     543         
     544        context.set_verify(SSL.VERIFY_PEER, callback)  
    531545         
    532546            
     
    534548        # globus doesn't handle this case, apparently, and instead 
    535549        # chokes in proxy delegation code 
    536         context.set_options(m2.SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) 
     550        context.set_options(SSL.OP_DONT_INSERT_EMPTY_FRAGMENTS) 
    537551         
    538552        # connect to myproxy server 
    539         conn = SSL.Connection(context, sock=socket.socket()) 
     553        conn = SSL.Connection(context, socket.socket()) 
    540554         
    541555        # Check server host identity - if host doesn't match use explicit 
    542556        # 'serverDN'  
    543557        # host/<hostname> one 
    544         hostCheck = _HostCheck(host=self.hostname, 
    545                                myProxyServerDN=self.serverDN, 
    546                                cnHostPfx=self.serverCNPrefix) 
    547         conn.set_post_connection_check_callback(hostCheck) 
     558#        hostCheck = _HostCheck(host=self.hostname, 
     559#                               myProxyServerDN=self.serverDN, 
     560#                               cnHostPfx=self.serverCNPrefix) 
     561#        conn.set_post_connection_check_callback(hostCheck) 
    548562         
    549563        return conn 
    550564         
    551     def _createKeys(self, nBitsForKey=PRIKEY_NBITS): 
     565    def _createKeyPair(self, nBitsForKey=PRIKEY_NBITS): 
    552566        """Generate key pair and return as PEM encoded string 
    553567        @type nBitsForKey: int 
     
    557571        @return: public/private key pair 
    558572        """ 
    559         keyPair = RSA.gen_key(nBitsForKey, 65537L,#m2.RSA_F4,  
    560                               callback=lambda *arg, **kw: None) 
     573        keyPair = crypto.PKey() 
     574        keyPair.generate_key(crypto.TYPE_RSA, nBitsForKey) 
    561575         
    562576        return keyPair 
     
    578592        # Check all required certifcate request DN parameters are set                 
    579593        # Create certificate request 
    580         req = X509.Request() 
     594        certReq = crypto.X509Req() 
    581595         
    582596        # Create public key object 
    583         pubKey = EVP.PKey() 
    584         pubKey.assign_rsa(keyPair) 
     597        certReq.set_pubkey(keyPair) 
    585598         
    586599        # Add the public key to the request 
    587         req.set_version(0) 
    588         req.set_pubkey(pubKey) 
    589          
    590         defaultReqDN = self._openSSLConfig.reqDN 
    591               
    592         # Set DN 
    593         x509Name = X509.X509_Name() 
    594         x509Name.CN = CN 
    595          
    596         if defaultReqDN: 
    597             x509Name.OU = defaultReqDN['OU'] 
    598             x509Name.O = defaultReqDN['O'] 
    599                          
    600         req.set_subject_name(x509Name) 
    601         req.sign(pubKey, messageDigest) 
    602  
    603         return req.as_der() 
     600        certReq.sign(keyPair, messageDigest) 
     601         
     602        derCertReq = crypto.dump_certificate_request(crypto.FILETYPE_ASN1,  
     603                                                     certReq) 
     604 
     605        return derCertReq 
    604606     
    605607    def _deserializeResponse(self, msg, *fieldNames): 
     
    648650         
    649651        @return list containing the equivalent to the input in PEM format""" 
    650         pemCerts = []         
     652        pemCerts = [] 
    651653        dat = inputDat 
    652654         
    653         while dat:     
     655        while dat: 
    654656            # find start of cert, get length         
    655657            ind = dat.find('\x30\x82') 
     
    661663            # extract der-format cert, and convert to pem 
    662664            derCert = dat[ind:ind+len+4] 
    663              
    664             x509 = X509.load_cert_der_string(derCert) 
    665             pemCert = x509.as_pem() 
    666              
     665            x509Cert = crypto.load_certificate(crypto.FILETYPE_ASN1, derCert) 
     666            pemCert = crypto.dump_certificate(crypto.FILETYPE_PEM, x509Cert)         
    667667            pemCerts.append(pemCert) 
    668668     
     
    10091009         
    10101010        # Send certificate and private key 
    1011         certTxt = X509.load_cert(certFile).as_pem() 
     1011        certTxt = open(certFile).read() 
    10121012        keyTxt = open(keyFile).read() 
    10131013         
     
    10511051            # If no key pair was passed, generate here 
    10521052            if keyPair is None: 
    1053                 keyPair = self._createKeys(nBitsForKey=nBitsForKey) 
     1053                keyPair = self._createKeyPair(nBitsForKey=nBitsForKey) 
    10541054                 
    10551055            certReq = self._createCertReq(username, keyPair) 
    10561056 
    10571057        if keyPair is not None:  
    1058             pemKeyPair = keyPair.as_pem(cipher=None,  
    1059                                         callback=util.no_passphrase_callback) 
     1058            pemKeyPair = crypto.dump_privatekey(crypto.FILETYPE_PEM, keyPair) 
    10601059         
    10611060        # Set-up SSL connection 
     
    11471146            for tries in range(MyProxyClient.MAX_RECV_TRIES): 
    11481147                dat += conn.recv(MyProxyClient.SERVER_RESP_BLK_SIZE) 
    1149         except SSL.SSLError: 
     1148        except SSL.SysCallError: 
    11501149            # Expect this exception when response content exhausted 
    11511150            pass 
Note: See TracChangeset for help on using the changeset viewer.