Changeset 1508


Ignore:
Timestamp:
11/09/06 17:15:23 (13 years ago)
Author:
pjkersha
Message:

Working EncryptionHandler? encrypt method. Equivalent decrypt nearly complete. Moved calls to handler code in
wsServer.py from echo to _Dispatch because the latter needs to know what method to call and so must operate on
the decrypted message.

Location:
TI12-security/trunk/python/Tests/xmlsec/WS-Security
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • TI12-security/trunk/python/Tests/xmlsec/WS-Security/wsClient.py

    r1469 r1508  
    2424def main(): 
    2525     
     26    priKeyPwd = open('../../tmp2').read().strip() 
     27    certFilePath = '../../Junk-cert.pem' 
     28    priKeyFilePath = '../../Junk-key.pem' 
     29     
    2630    # Signature handler object is passed to binding 
    27     signatureHandler = wsSecurity.SignatureHandler(\ 
    28                                 certFilePath='../../Junk-cert.pem', 
    29                                 priKeyFilePath='../../Junk-key.pem', 
    30                                 priKeyPwd=open('../../tmp2').read().strip()) 
     31    signatureHandler = wsSecurity.SignatureHandler(certFilePath=certFilePath, 
     32                                                priKeyFilePath=priKeyFilePath, 
     33                                                priKeyPwd=priKeyPwd) 
     34     
     35    encryptionHandler = wsSecurity.EncryptionHandler( 
     36                                                certFilePath=certFilePath, 
     37                                                priKeyFilePath=priKeyFilePath, 
     38                                                priKeyPwd=priKeyPwd) 
    3139 
     40    # Added in encr_handler keyword as a quick fudge - encr_handler.encrypt 
     41    # gets called after sig_handler.sign and encr_handler.decrypt gets called 
     42    # before sig_handler.verify 
     43    # 
     44    # Maybe there should be a generic handler keyword for signature and 
     45    # encryption so that it can customised - sign then encrypt or encrypt 
     46    # then sign or any other combination... 
    3247    binding = Binding(url='http://localhost:8080/wsServer.py', 
    33                       sig_handler=signatureHandler) 
     48                      #sig_handler=signatureHandler, [leave out whilst testing 
     49                      # encryption] 
     50                      encr_handler=encryptionHandler) 
    3451                  
    3552    echoRequest = echoRequestWrapper() 
  • TI12-security/trunk/python/Tests/xmlsec/WS-Security/wsSecurity.py

    r1500 r1508  
    2222import base64 
    2323 
     24# For shared key encryption 
     25from Crypto.Cipher import AES 
     26import os 
     27 
    2428import ZSI 
    25 from ZSI.wstools.Namespaces import DSIG, OASIS, GLOBUS, WSU, WSA200403, BEA, \ 
     29from ZSI.wstools.Namespaces import DSIG, ENCRYPTION, OASIS, WSU, WSA200403, \ 
    2630                                   SOAP 
    2731                                    
     
    4650# associated note 
    4751from xml.dom.ext.reader.PyExpat import Reader 
     52 
     53 
     54def getElements(node, nameList): 
     55    '''DOM Helper function for getting child elements from a given node''' 
     56    # Avoid sub-string matches 
     57    nameList = isinstance(nameList, basestring) and [nameList] or nameList 
     58    return [n for n in node.childNodes if str(n.localName) in nameList] 
    4859 
    4960 
     
    8192        soapWriter._header.setNamespaceAttribute('ds', DSIG.BASE) 
    8293         
     94        # TODO: Put in a check to make sure <wsse:security> isn't already  
     95        # present in header 
    8396        wsseElem = soapWriter._header.createAppendElement(OASIS.WSSE,  
    8497                                                         'Security') 
     
    245258        """Verify signature""" 
    246259         
    247         element = ElementProxy(None, message=parsedSOAP.header) 
    248260        processorNss = \ 
    249261        { 
     
    276288                                  context=ctxt) 
    277289            
    278         # Make a lambda to pick out node child or children with a given  
    279         # name 
    280         getElements = lambda node, nameList: \ 
    281                 [n for n in node.childNodes if str(n.localName) in nameList]  
    282          
    283290        for refNode in refNodes: 
    284291            # Get the URI for the reference 
     
    376383 
    377384 
     385class EncryptionError(Exception): 
     386    """Flags an error in the encryption process""" 
     387 
     388class DecryptionError(Exception): 
     389    """Raised from EncryptionHandler.decrypt if an error occurs with the 
     390    decryption process""" 
     391 
     392 
    378393class EncryptionHandler(object): 
    379394    """Encrypt/Decrypt SOAP messages using WS-Security"""  
     
    382397                 certFilePath=None,  
    383398                 priKeyFilePath=None,  
    384                  priKeyPwd=None): 
     399                 priKeyPwd=None, 
     400                 chkSecurityTokRef=False): 
    385401         
    386402        self.__certFilePath = certFilePath 
    387403        self.__priKeyFilePath = priKeyFilePath 
    388404        self.__priKeyPwd = priKeyPwd 
     405         
     406        self.__chkSecurityTokRef = chkSecurityTokRef 
    389407 
    390408 
    391409    def encrypt(self, soapWriter): 
    392         """Encrypt an outbound SOAP message""" 
    393          
    394         import pdb;pdb.set_trace() 
     410        """Encrypt an outbound SOAP message 
     411         
     412        Use Key Wrapping - message is encrypted using a shared key which  
     413        itself is encrypted with the public key provided by the X.509 cert. 
     414        certFilePath""" 
    395415         
    396416        # Use X.509 Cert to encrypt 
    397417        x509Cert = X509.load_cert(self.__certFilePath) 
    398418         
    399         # Extract RSA public key from the cert 
     419        soapWriter.dom.setNamespaceAttribute('wsse', OASIS.WSSE) 
     420        soapWriter.dom.setNamespaceAttribute('xenc', ENCRYPTION.BASE) 
     421        soapWriter.dom.setNamespaceAttribute('ds', DSIG.BASE) 
     422         
     423        # TODO: Put in a check to make sure <wsse:security> isn't already  
     424        # present in header 
     425        wsseElem = soapWriter._header.createAppendElement(OASIS.WSSE,  
     426                                                         'Security') 
     427        wsseElem.node.setAttribute('SOAP-ENV:mustUnderstand', "1") 
     428         
     429        encrKeyElem = wsseElem.createAppendElement(ENCRYPTION.BASE,  
     430                                                   'EncryptedKey') 
     431         
     432        # Encryption method used to encrypt the shared key 
     433        keyEncrMethodElem = encrKeyElem.createAppendElement(ENCRYPTION.BASE,  
     434                                                        'EncryptionMethod') 
     435         
     436        keyEncrMethodElem.node.setAttribute('Algorithm',  
     437                                            ENCRYPTION.KT_RSA_1_5) 
     438 
     439 
     440        # Key Info 
     441        KeyInfoElem = encrKeyElem.createAppendElement(DSIG.BASE, 'KeyInfo') 
     442         
     443        secTokRefElem = KeyInfoElem.createAppendElement(OASIS.WSSE,  
     444                                                  'SecurityTokenReference') 
     445         
     446        x509IssSerialElem = secTokRefElem.createAppendElement(DSIG.BASE,  
     447                                                          'X509IssuerSerial') 
     448 
     449         
     450        x509IssNameElem = x509IssSerialElem.createAppendElement(DSIG.BASE,  
     451                                                          'X509IssuerName') 
     452        x509IssNameElem.createAppendTextNode(x509Cert.get_issuer().as_text()) 
     453 
     454         
     455        x509IssSerialNumElem = x509IssSerialElem.createAppendElement( 
     456                                                  DSIG.BASE,  
     457                                                  'X509IssuerSerialNumber') 
     458         
     459        x509IssSerialNumElem.createAppendTextNode( 
     460                                          str(x509Cert.get_serial_number())) 
     461 
     462        # References to what has been encrypted 
     463        encrKeyCiphDataElem = encrKeyElem.createAppendElement(ENCRYPTION.BASE, 
     464                                                              'CipherData') 
     465         
     466        encrKeyCiphValElem = encrKeyCiphDataElem.createAppendElement( 
     467                                                              ENCRYPTION.BASE, 
     468                                                              'CipherValue') 
     469 
     470        # References to what has been encrypted 
     471        refListElem = encrKeyElem.createAppendElement(ENCRYPTION.BASE, 
     472                                                      'ReferenceList') 
     473         
     474        dataRefElem = refListElem.createAppendElement(ENCRYPTION.BASE, 
     475                                                      'DataReference') 
     476        dataRefElem.node.setAttribute('URI', "#encrypted") 
     477 
     478                      
     479        # Add Encrypted data to SOAP body 
     480        encrDataElem = soapWriter.body.createAppendElement(ENCRYPTION.BASE,  
     481                                                           'EncryptedData') 
     482        encrDataElem.node.setAttribute('Id', 'encrypted') 
     483        encrDataElem.node.setAttribute('Type', ENCRYPTION.BASE)   
     484               
     485        # Encryption method used to encrypt the target data 
     486        dataEncrMethodElem = encrDataElem.createAppendElement(ENCRYPTION.BASE,  
     487                                                        'EncryptionMethod') 
     488         
     489        dataEncrMethodElem.node.setAttribute('Algorithm',  
     490                                             ENCRYPTION.WRAP_AES256) 
     491         
     492        # Cipher data 
     493        ciphDataElem = encrDataElem.createAppendElement(ENCRYPTION.BASE, 
     494                                                        'CipherData') 
     495         
     496        ciphValueElem = ciphDataElem.createAppendElement(ENCRYPTION.BASE, 
     497                                                         'CipherValue') 
     498 
     499 
     500        # Get elements from SOAP body for encryption 
     501        dataElem = soapWriter.body.node.childNodes[0] 
     502        data = dataElem.toxml() 
     503      
     504        # AES algorithm requires data length to be a multiple of 16 - pad as 
     505        # required     
     506        modData = len(data) % 16 
     507        nPad = modData and 16 - modData or 0 
     508        data += ' '*nPad         
     509               
     510        # Encrypt required elements 
     511        sharedKey = os.urandom(32) 
     512        print "Shared key = %s" % sharedKey 
     513        encryptedData = AES.new(sharedKey, AES.MODE_ECB).encrypt(data) 
     514        dataCiphValue = base64.encodestring(encryptedData).strip() 
     515 
     516        ciphValueElem.createAppendTextNode(dataCiphValue) 
     517         
     518         
     519        # ! Delete unencrypted message body elements ! 
     520        soapWriter.body.node.removeChild(dataElem) 
     521 
     522         
     523        # Use X.509 cert public key to encrypt the shared key - Extract key 
     524        # from the cert 
    400525        rsaPubKey = x509Cert.get_pubkey().get_rsa() 
    401  
    402         data = "The Larch" 
    403         encryptedData = rsaPubKey.public_encrypt(data, RSA.pkcs1_padding) 
    404          
    405         return encryptedData 
    406  
    407         
    408     def decrypt(self, parsedSOAP, encryptedData=None): 
     526         
     527        # Encrypt the shared key 
     528        encryptedSharedKey = rsaPubKey.public_encrypt(sharedKey,  
     529                                                      RSA.pkcs1_padding) 
     530         
     531        encrKeyCiphVal = base64.encodestring(encryptedSharedKey).strip() 
     532         
     533        # Add the encrypted shared key to the EncryptedKey section in the SOAP 
     534        # header 
     535        encrKeyCiphValElem.createAppendTextNode(encrKeyCiphVal) 
     536 
     537        print soapWriter.dom.node.toprettyxml() 
     538        import pdb;pdb.set_trace() 
     539         
     540         
     541    def decrypt(self, parsedSOAP): 
    409542        """Decrypt an inbound SOAP message""" 
    410543         
    411         # Read Private key to decrypt the data      
     544        processorNss = \ 
     545        { 
     546            'xenc':   ENCRYPTION.BASE, 
     547            'ds':     DSIG.BASE,  
     548            'wsu':    WSU.UTILITY,  
     549            'wsse':   OASIS.WSSE,  
     550            'soapenv':"http://schemas.xmlsoap.org/soap/envelope/"  
     551        } 
     552        ctxt = Context(parsedSOAP.dom, processorNss=processorNss) 
     553         
     554        refListNodes = xpath.Evaluate('//xenc:ReferenceList',  
     555                                      contextNode=parsedSOAP.dom,  
     556                                      context=ctxt) 
     557        if len(refListNodes) > 1: 
     558            raise DecryptionError, 'Expecting a single ReferenceList element' 
     559         
     560        try: 
     561            refListNode = refListNodes[0] 
     562        except: 
     563            # Message wasn't encrypted - is this OK or is a check needed for 
     564            # encryption info in SOAP body - enveloped form? 
     565            return 
     566 
     567 
     568        # Check for wrapped key encryption 
     569        encrKeyNodes = xpath.Evaluate('//xenc:EncryptedKey',  
     570                                      contextNode=parsedSOAP.dom,  
     571                                      context=ctxt) 
     572        if len(encrKeyNodes) > 1: 
     573            raise DecryptionError, 'This implementation can only handle ' + \ 
     574                                   'single EncryptedKey element' 
     575         
     576        try: 
     577            encrKeyNode = encrKeyNodes[0] 
     578        except: 
     579            # Shared key encryption used - leave out for the moment 
     580            raise DecryptionError, 'This implementation can only handle ' + \ 
     581                                   'wrapped key encryption' 
     582 
     583         
     584        # Check encryption method 
     585        keyEncrMethodNode = getElements(encrKeyNode, 'EncryptionMethod')[0]      
     586        keyAlgorithm = keyEncrMethodNode.getAttributeNodeNS(None,  
     587                                                            "Algorithm").value 
     588        if keyAlgorithm != ENCRYPTION.KT_RSA_1_5: 
     589            raise DecryptionError, \ 
     590            'Encryption algorithm for wrapped key is "%s", expecting "%s"' % \ 
     591                (keyAlgorithm, ENCRYPTION.KT_RSA_1_5) 
     592 
     593                                                             
     594        if self.__chkSecurityTokRef and self.__certFilePath: 
     595              
     596            # Check input cert. against SecurityTokenReference 
     597            securityTokRefXPath = '/ds:KeyInfo/wsse:SecurityTokenReference' 
     598            securityTokRefNode = xpath.Evaluate(securityTokRefXPath,  
     599                                                contextNode=encrKeyNode,  
     600                                                context=ctxt) 
     601            # Look for ds:X509* elements to check against X.509 cert input 
     602 
     603 
     604        # Look for cipher data for wrapped key 
     605        keyCiphDataNode = getElements(encrKeyNode, 'CipherData')[0] 
     606        keyCiphValNode = getElements(keyCiphDataNode, 'CipherValue')[0] 
     607 
     608        keyCiphVal = str(keyCiphValNode.childNodes[0].nodeValue) 
     609        encryptedKey = base64.decodestring(keyCiphVal) 
     610 
     611        # Read RSA Private key in order to decrypt wrapped key   
    412612        priKeyFile = BIO.File(open(self.__priKeyFilePath))                                             
    413613        priKey = RSA.load_key_bio(priKeyFile,  
    414614                                  callback=lambda *ar, **kw: self.__priKeyPwd) 
    415615         
    416         decryptedData = priKey.private_decrypt(encryptedData,  
    417                                                RSA.pkcs1_padding) 
    418          
    419         print decryptedData 
     616        sharedKey = priKey.private_decrypt(encryptedKey, RSA.pkcs1_padding) 
     617         
     618 
     619        # Check list of data elements that have been encrypted 
     620        for dataRefNode in refListNode.childNodes: 
     621 
     622            # Get the URI for the reference 
     623            dataRefURI = dataRefNode.getAttributeNodeNS(None, 'URI').value                             
     624            if dataRefURI[0] != "#": 
     625                raise VerifyError, \ 
     626                    "Expecting # identifier for DataReference URI \"%s\"" % \ 
     627                    dataRefURI 
     628 
     629            # XPath reference - need to check for wsu namespace qualified? 
     630            #uriXPath = '//*[@wsu:Id="%s"]' % dataRefURI[1:] 
     631            uriXPath = '//*[@Id="%s"]' % dataRefURI[1:] 
     632            uriNode = xpath.Evaluate(uriXPath,  
     633                                     contextNode=parsedSOAP.dom,  
     634                                     context=ctxt)[0] 
     635                 
     636            dataEncrMethodNode = getElements(uriNode, 'EncryptionMethod')[0]      
     637            dataAlgorithm = dataEncrMethodNode.getAttributeNodeNS(None,  
     638                                                            "Algorithm").value 
     639            if dataAlgorithm != ENCRYPTION.WRAP_AES256: 
     640                raise DecryptionError, \ 
     641                'Encryption algorithm for data is "%s", expecting "%s"' % \ 
     642                    (keyAlgorithm, ENCRYPTION.WRAP_AES256) 
     643 
     644            # Get Data 
     645            dataCiphDataNode = getElements(uriNode, 'CipherData')[0] 
     646            dataCiphValNode = getElements(dataCiphDataNode, 'CipherValue')[0] 
     647         
     648            dataCiphVal = str(dataCiphValNode.childNodes[0].nodeValue) 
     649            encryptedData = base64.decodestring(dataCiphVal) 
     650             
     651            aes = AES.new(sharedKey, AES.MODE_ECB) 
     652            decryptedData = aes.decrypt(encryptedData) 
     653             
     654            # Parse the data and add to the EncryptedData parent 
     655             
     656            # Remove the EncryptedData Element 
     657             
     658             
     659            import pdb;pdb.set_trace() 
     660            print decryptedData 
     661         
     662             
     663 
    420664 
    421665         
  • TI12-security/trunk/python/Tests/xmlsec/WS-Security/wsServer.py

    r1469 r1508  
    3333    """example service simply returns message sent to it""" 
    3434    
    35     import pdb;pdb.set_trace() 
    36     signatureHandler = SignatureHandler(\ 
    37                             certFilePath='../../Junk-cert.pem', 
    38                             priKeyFilePath='../../Junk-key.pem', 
    39                             priKeyPwd=open('../../tmp2').read().strip()) 
    40     signatureHandler.verify(ps) 
    41  
    4235    request = ps.Parse(echoRequestWrapper) 
    4336    response = echoResponseWrapper()     
     
    6457 
    6558    ''' 
     59     
     60    # WS-Secuirty handler called here 
     61#    signatureHandler = SignatureHandler(\ 
     62#                            certFilePath='../../Junk-cert.pem', 
     63#                            priKeyFilePath='../../Junk-key.pem', 
     64#                            priKeyPwd=open('../../tmp2').read().strip()) 
     65#    signatureHandler.verify(ps) 
     66    # Test decryption 
     67    encryptionHandler = EncryptionHandler(\ 
     68                            certFilePath='../../Junk-cert.pem', 
     69                            priKeyFilePath='../../Junk-key.pem', 
     70                            priKeyPwd=open('../../tmp2').read().strip()) 
     71    encryptionHandler.decrypt(ps) 
     72 
     73     
    6674    global _client_binding 
    6775    try: 
Note: See TracChangeset for help on using the changeset viewer.