Changeset 1526


Ignore:
Timestamp:
20/09/06 17:18:33 (13 years ago)
Author:
pjkersha
Message:

Tests/xmlsec/XMLSecTest.py: class to test compatibility pyCrypto/M2Crypto encryption handler with pyXMLSec.
Uses enveloped style encryption. Works with decrypt4.py pyXMLSec e.g. on glue.

Tests/xmlsec/WS-Security/wsSecurity.py: added fixes from work with XMLSecTest.py above. Now correctly pads
data according block size used by target shared key encryption algorithm (follows
 http://www.w3.org/TR/xmlenc-core/#sec-Alg-Block).

Location:
TI12-security/trunk/python/Tests/xmlsec
Files:
1 added
1 edited

Legend:

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

    r1510 r1526  
    2323 
    2424# For shared key encryption 
    25 from Crypto.Cipher import AES 
     25from Crypto.Cipher import AES, DES3 
    2626import os 
    2727 
     
    5050# associated note 
    5151from xml.dom.ext.reader.PyExpat import Reader 
     52 
     53 
     54class _ENCRYPTION(ENCRYPTION): 
     55    '''Derived from ENCRYPTION class to add in extra 'tripledes-cbc' - is this 
     56    any different to 'des-cbc'?  ENCRYPTION class implies that it is the same 
     57    because it's assigned to 'BLOCK_3DES' ??''' 
     58    BLOCK_TRIPLEDES = "http://www.w3.org/2001/04/xmlenc#tripledes-cbc" 
    5259 
    5360 
     
    393400class EncryptionHandler(object): 
    394401    """Encrypt/Decrypt SOAP messages using WS-Security"""  
    395         
     402     
     403    # Map namespace URIs to Crypto algorithm module and mode  
     404    cryptoAlg = \ 
     405    { 
     406         _ENCRYPTION.WRAP_AES256:      {'module':       AES,  
     407                                        'mode':         AES.MODE_ECB, 
     408                                        'blockSize':    16}, 
     409          
     410         # CBC (Cipher Block Chaining) modes 
     411         _ENCRYPTION.BLOCK_AES256:     {'module':       AES,  
     412                                        'mode':         AES.MODE_CBC, 
     413                                        'blockSize':    16}, 
     414                                         
     415         _ENCRYPTION.BLOCK_TRIPLEDES:  {'module':       DES3,  
     416                                        'mode':         DES3.MODE_CBC, 
     417                                        'blockSize':    8}    
     418    } 
     419 
     420      
    396421    def __init__(self, 
    397422                 certFilePath=None,  
    398423                 priKeyFilePath=None,  
    399424                 priKeyPwd=None, 
    400                  chkSecurityTokRef=False): 
     425                 chkSecurityTokRef=False, 
     426                 encrNS=_ENCRYPTION.BLOCK_AES256): 
    401427         
    402428        self.__certFilePath = certFilePath 
     
    405431         
    406432        self.__chkSecurityTokRef = chkSecurityTokRef 
    407  
    408  
     433         
     434        # Algorithm for shared key encryption 
     435        try: 
     436            self.__encrAlg = self.cryptoAlg[encrNS] 
     437             
     438        except KeyError: 
     439            raise EncryptionError, \ 
     440        'Input encryption algorithm namespace "%s" is not supported' % encrNS 
     441 
     442        self.__encrNS = encrNS 
     443         
     444         
    409445    def encrypt(self, soapWriter): 
    410446        """Encrypt an outbound SOAP message 
     
    418454         
    419455        soapWriter.dom.setNamespaceAttribute('wsse', OASIS.WSSE) 
    420         soapWriter.dom.setNamespaceAttribute('xenc', ENCRYPTION.BASE) 
     456        soapWriter.dom.setNamespaceAttribute('xenc', _ENCRYPTION.BASE) 
    421457        soapWriter.dom.setNamespaceAttribute('ds', DSIG.BASE) 
    422458         
     
    427463        wsseElem.node.setAttribute('SOAP-ENV:mustUnderstand', "1") 
    428464         
    429         encrKeyElem = wsseElem.createAppendElement(ENCRYPTION.BASE,  
     465        encrKeyElem = wsseElem.createAppendElement(_ENCRYPTION.BASE,  
    430466                                                   'EncryptedKey') 
    431467         
    432468        # Encryption method used to encrypt the shared key 
    433         keyEncrMethodElem = encrKeyElem.createAppendElement(ENCRYPTION.BASE,  
     469        keyEncrMethodElem = encrKeyElem.createAppendElement(_ENCRYPTION.BASE,  
    434470                                                        'EncryptionMethod') 
    435471         
    436472        keyEncrMethodElem.node.setAttribute('Algorithm',  
    437                                             ENCRYPTION.KT_RSA_1_5) 
     473                                            _ENCRYPTION.KT_RSA_1_5) 
    438474 
    439475 
     
    461497 
    462498        # References to what has been encrypted 
    463         encrKeyCiphDataElem = encrKeyElem.createAppendElement(ENCRYPTION.BASE, 
    464                                                               'CipherData') 
     499        encrKeyCiphDataElem = encrKeyElem.createAppendElement( 
     500                                                          _ENCRYPTION.BASE, 
     501                                                          'CipherData') 
    465502         
    466503        encrKeyCiphValElem = encrKeyCiphDataElem.createAppendElement( 
    467                                                               ENCRYPTION.BASE, 
    468                                                               'CipherValue') 
     504                                                          _ENCRYPTION.BASE, 
     505                                                          'CipherValue') 
    469506 
    470507        # References to what has been encrypted 
    471         refListElem = encrKeyElem.createAppendElement(ENCRYPTION.BASE, 
     508        refListElem = encrKeyElem.createAppendElement(_ENCRYPTION.BASE, 
    472509                                                      'ReferenceList') 
    473510         
    474         dataRefElem = refListElem.createAppendElement(ENCRYPTION.BASE, 
     511        dataRefElem = refListElem.createAppendElement(_ENCRYPTION.BASE, 
    475512                                                      'DataReference') 
    476513        dataRefElem.node.setAttribute('URI', "#encrypted") 
     
    478515                      
    479516        # Add Encrypted data to SOAP body 
    480         encrDataElem = soapWriter.body.createAppendElement(ENCRYPTION.BASE,  
     517        encrDataElem = soapWriter.body.createAppendElement(_ENCRYPTION.BASE,  
    481518                                                           'EncryptedData') 
    482519        encrDataElem.node.setAttribute('Id', 'encrypted') 
    483         encrDataElem.node.setAttribute('Type', ENCRYPTION.BASE)   
     520        encrDataElem.node.setAttribute('Type', _ENCRYPTION.BASE)   
    484521               
    485522        # 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) 
     523        dataEncrMethodElem = encrDataElem.createAppendElement( 
     524                                                      _ENCRYPTION.BASE,  
     525                                                      'EncryptionMethod') 
     526         
     527        dataEncrMethodElem.node.setAttribute('Algorithm', self.__encrNS) 
    491528         
    492529        # Cipher data 
    493         ciphDataElem = encrDataElem.createAppendElement(ENCRYPTION.BASE, 
     530        ciphDataElem = encrDataElem.createAppendElement(_ENCRYPTION.BASE, 
    494531                                                        'CipherData') 
    495532         
    496         ciphValueElem = ciphDataElem.createAppendElement(ENCRYPTION.BASE, 
     533        ciphValueElem = ciphDataElem.createAppendElement(_ENCRYPTION.BASE, 
    497534                                                         'CipherValue') 
    498535 
     
    502539        data = dataElem.toxml() 
    503540      
    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         encryptedData = AES.new(sharedKey, AES.MODE_ECB).encrypt(data) 
     541        # Pad data to nearest multiple of encryption algorithm's block size     
     542        modData = len(data) % self.__encrAlg['blockSize'] 
     543        nPad = modData and self.__encrAlg['blockSize'] - modData or 0 
     544         
     545        # PAd with random junk but ... 
     546        data += os.urandom(nPad-1) 
     547         
     548        # Last byte should be number of padding bytes 
     549        # (http://www.w3.org/TR/xmlenc-core/#sec-Alg-Block) 
     550        data += chr(nPad)        
     551         
     552        # Generate shared key and input vector - for testing use hard-coded  
     553        # values to allow later comparison               
     554        sharedKey = os.urandom(self.__encrAlg['blockSize']) 
     555        iv = os.urandom(self.__encrAlg['blockSize']) 
     556         
     557        alg = self.__encrAlg['module'].new(sharedKey, 
     558                                           self.__encrAlg['mode'], 
     559                                           iv) 
     560  
     561        # Encrypt required elements - prepend input vector 
     562        encryptedData = alg.encrypt(iv + data) 
    513563        dataCiphValue = base64.encodestring(encryptedData).strip() 
    514564 
     
    543593        processorNss = \ 
    544594        { 
    545             'xenc':   ENCRYPTION.BASE, 
     595            'xenc':   _ENCRYPTION.BASE, 
    546596            'ds':     DSIG.BASE,  
    547597            'wsu':    WSU.UTILITY,  
     
    585635        keyAlgorithm = keyEncrMethodNode.getAttributeNodeNS(None,  
    586636                                                            "Algorithm").value 
    587         if keyAlgorithm != ENCRYPTION.KT_RSA_1_5: 
     637        if keyAlgorithm != _ENCRYPTION.KT_RSA_1_5: 
    588638            raise DecryptionError, \ 
    589639            'Encryption algorithm for wrapped key is "%s", expecting "%s"' % \ 
    590                 (keyAlgorithm, ENCRYPTION.KT_RSA_1_5) 
     640                (keyAlgorithm, _ENCRYPTION.KT_RSA_1_5) 
    591641 
    592642                                                             
     
    598648                                                contextNode=encrKeyNode,  
    599649                                                context=ctxt) 
    600             # Look for ds:X509* elements to check against X.509 cert input 
     650            # TODO: Look for ds:X509* elements to check against X.509 cert  
     651            # input 
    601652 
    602653 
     
    636687            dataAlgorithm = dataEncrMethodNode.getAttributeNodeNS(None,  
    637688                                                            "Algorithm").value 
    638             if dataAlgorithm != ENCRYPTION.WRAP_AES256: 
     689            try:         
     690                # Match algorithm name to Crypto module 
     691                CryptoAlg = self.cryptoAlg[dataAlgorithm] 
     692                 
     693            except KeyError: 
    639694                raise DecryptionError, \ 
    640                 'Encryption algorithm for data is "%s", expecting "%s"' % \ 
    641                     (keyAlgorithm, ENCRYPTION.WRAP_AES256) 
     695'Encryption algorithm for data is "%s", supported algorithms are:\n "%s"' % \ 
     696                    (keyAlgorithm, "\n".join(self.cryptoAlg.keys())) 
    642697 
    643698            # Get Data 
     
    648703            encryptedData = base64.decodestring(dataCiphVal) 
    649704             
    650             aes = AES.new(sharedKey, AES.MODE_ECB) 
    651             decryptedData = aes.decrypt(encryptedData) 
    652              
     705            alg = CryptoAlg['module'].new(sharedKey, CryptoAlg['mode']) 
     706            decryptedData = alg.decrypt(encryptedData) 
     707             
     708            # Strip prefix - assume is block size 
     709            decryptedData = decryptedData[CryptoAlg['blockSize']:] 
     710             
     711            # Strip any padding suffix - Last byte should be number of padding 
     712            # bytes 
     713            # (http://www.w3.org/TR/xmlenc-core/#sec-Alg-Block) 
     714            lastChar = decryptedData[-1] 
     715            nPad = ord(lastChar) 
     716             
     717            # Sanity check - there may be no padding at all - the last byte  
     718            # being the end of the encrypted XML? 
     719            # 
     720            # TODO: are there better sanity checks than this?! 
     721            if nPad < CryptoAlg['blockSize'] and nPad > 0 and \ 
     722               lastChar != '\n' and lastChar != '>': 
     723                 
     724                # Follow http://www.w3.org/TR/xmlenc-core/#sec-Alg-Block - 
     725                # last byte gives number of padding bytes 
     726                decryptedData = decryptedData[:-nPad] 
     727 
     728 
    653729            # Parse the encrypted data - inherit from Reader as a fudge to  
    654730            # enable relevant namespaces to be added prior to parse 
Note: See TracChangeset for help on using the changeset viewer.