Ignore:
Timestamp:
22/08/06 14:05:18 (13 years ago)
Author:
pjkersha
Message:

Working version using multiple references and # style wsu:Id links to document sections to be signed. Both
SOAP message content and X.509 token are signed.

File:
1 edited

Legend:

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

    r1425 r1440  
    1010import cElementTree as ElementTree 
    1111import ZSI 
    12 from ZSI.wstools.Namespaces import DSIG, OASIS, GLOBUS, WSA200403, BEA, SOAP 
     12from ZSI.wstools.Namespaces import DSIG, OASIS, GLOBUS, WSU, WSA200403, BEA, \ 
     13                                   SOAP 
    1314 
    1415# XML Parsing 
     
    2425#from ZSI.TC import _get_global_element_declaration as GED 
    2526 
    26 _attrs = lambda E: E._get_attributes() or [] 
    27 _children = lambda E: E._get_childNodes() or [] 
    28 _sorter = lambda n1, n2: cmp(n1._get_nodeName(), n2._get_nodeName()) 
    29  
    3027 
    3128#class BST(str): 
     
    4845        self._sig_algo = GLOBUS.SIG 
    4946        self._dig_algo = DSIG.DIGEST_SHA1 
    50         self._contextID = None 
     47        self._docCtxtID = None 
    5148        self._expire_time = 300 
    5249        self._setProtocol = False 
     
    8784        """ 
    8885 
    89         context_id = self._contextID 
     86        docCtxt_id = self._docCtxtID 
    9087 
    9188        if self._sign_headers:  
     
    201198    def sign(self, certFilePath, priKeyFilePath, priKeyPwd=None): 
    202199 
     200        # Use wsse:TransformationParameters with ds:Transforms?? 
    203201        msgTmpl = """<?xml version="1.0" encoding="UTF-8"?> 
    204202<!-- 
    205203SOAP Message with WSSE Signature 
    206204--> 
    207 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
     205<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"  
     206        xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"  
     207        xmlns:xsd="http://www.w3.org/2001/XMLSchema"  
     208        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
    208209    <soapenv:Header> 
    209         <wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/04/secext" xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/04/utility" soapenv:mustUnderstand="1"> 
     210        <wsse:Security  
     211                xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/04/secext"  
     212                xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility"  
     213                soapenv:mustUnderstand="1"> 
     214            <wsse:BinarySecurityToken 
     215                wsu:Id="binaryToken" 
     216                ValueType="wsse:X509v3" 
     217                EncodingType="wsse:Base64Binary"> 
     218%s 
     219            </wsse:BinarySecurityToken> 
    210220            <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> 
    211221                <ds:SignedInfo> 
    212                     <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> 
    213                     <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> 
    214                     <ds:Reference URI="./wsseSign-doc.xml"> 
    215                         <ds:Transforms> 
    216                             <ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> 
    217                         </ds:Transforms> 
    218                         <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> 
    219                         <ds:DigestValue>%s</ds:DigestValue> 
    220                     </ds:Reference> 
     222                    <ds:CanonicalizationMethod  
     223                    Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> 
     224                    <ds:SignatureMethod  
     225                    Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> 
    221226                </ds:SignedInfo> 
    222227                <ds:SignatureValue>%s</ds:SignatureValue> 
    223228                <ds:KeyInfo> 
    224                     <ds:X509Data> 
    225                         <ds:X509Certificate> 
    226 %s 
    227                         </ds:X509Certificate> 
    228                     </ds:X509Data> 
     229                    <wsse:SecurityTokenReference> 
     230                        <wsse:Reference URI="#binaryToken"/> 
     231                    </wsse:SecurityTokenReference> 
    229232                </ds:KeyInfo> 
    230233            </ds:Signature> 
    231234        </wsse:Security> 
    232235    </soapenv:Header> 
    233     <soapenv:Body xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/04/utility" wsu:Id="msgBody"> 
     236    <soapenv:Body xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility"  
     237            wsu:Id="body"> 
    234238    Hello, World! 
    235239    </soapenv:Body> 
     
    237241 
    238242 
     243        # Add X.509 cert as binary security token 
     244        x509Cert = X509.load_cert(certFilePath) 
     245         
     246        x509CertPat = re.compile(\ 
     247            '-----BEGIN CERTIFICATE-----\n?(.*?)\n?-----END CERTIFICATE-----', 
     248            re.S) 
     249        x509CertStr = x509CertPat.findall(x509Cert.as_pem())[0] 
     250         
     251        msgTxt = msgTmpl % (x509CertStr, "%s") 
     252        msgDoc = NonvalidatingReader.parseStream(StringIO(msgTxt)) 
     253         
     254        # Namespaces for XPath searches 
     255        processorNss = \ 
     256        { 
     257            'ds':     DSIG.BASE,  
     258            'wsu':    WSU.UTILITY,  
     259            'wsse':   OASIS.WSSE,  
     260            'soapenv':"http://schemas.xmlsoap.org/soap/envelope/"  
     261        } 
     262        docCtxt = XPath.Context.Context(msgDoc, processorNss=processorNss) 
     263 
     264        import pdb;pdb.set_trace() 
     265                
    239266        # 1) Reference Generation 
    240267        # 
    241         # Reference - hard code as local file for now 
    242         refURIstream = open("./wsseSign-doc.xml") 
    243  
    244         # Canonicalize reference 
    245         refDom = NonvalidatingReader.parseStream(refURIstream) 
    246         c14nRef = Canonicalize(refDom) 
    247          
    248         # Calculate digest for reference and base 64 encode 
    249         # 
    250         # Nb. encodestring adds a trailing newline char 
    251         digestValue = base64.encodestring(sha(c14nRef).digest()).strip() 
    252          
    253          
     268        refTmpl = \ 
     269"""         <ds:Reference xmlns:ds="http://www.w3.org/2000/09/xmldsig#" URI="%s"> 
     270                <ds:Transforms> 
     271                    <ds:Transform  
     272                    Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> 
     273                </ds:Transforms> 
     274                <ds:DigestMethod  
     275                Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> 
     276                <ds:DigestValue>%s</ds:DigestValue> 
     277            </ds:Reference> 
     278""" 
     279 
     280        # Find references 
     281        signedInfoNode = XPath.Compile('//ds:SignedInfo').evaluate(docCtxt)[0] 
     282         
     283        idNodes = XPath.Compile('//*[@wsu:Id]').evaluate(docCtxt) 
     284        for idNode in idNodes: 
     285             
     286            # Set URI attribute to point to reference to be signed 
     287            uri = u"#" + idNode.attributes[(WSU.UTILITY, u'Id')].value 
     288             
     289            # Canonicalize reference 
     290            c14nRef = Canonicalize(idNode) 
     291             
     292            # Calculate digest for reference and base 64 encode 
     293            # 
     294            # Nb. encodestring adds a trailing newline char 
     295            digestValue = base64.encodestring(sha(c14nRef).digest()).strip() 
     296 
     297            refTxt = refTmpl % (uri, digestValue) 
     298            refNode = NonvalidatingReader.parseStream(StringIO(refTxt)) 
     299 
     300            # Add the new child node  
     301            signedInfoNode.appendChild(refNode) 
     302 
     303        
    254304        # 2) Signature Generation 
    255305        # 
    256         # Add digest into SignedInfo 
    257         msgTxt = msgTmpl % (digestValue, '%s', '%s') 
    258  
    259         import pdb;pdb.set_trace() 
    260306 
    261307        # Test against signature generated by pyXMLSec version 
     
    263309        #dom = NonvalidatingReader.parseStream(StringIO(xmlTxt)) 
    264310         
    265         # Parse SignedInfo into DOM object ready for canonicalization function 
    266         dom = NonvalidatingReader.parseStream(StringIO(msgTxt)) 
    267          
    268         # Namespaces 
    269         processorNss = \ 
    270         { 
    271             'ds':     DSIG.BASE,  
    272             'wsu':    OASIS.UTILITY,  
    273             'wsse':   OASIS.WSSE,  
    274             'wsa':    WSA200403.ADDRESS, 
    275             'soapenv':"http://schemas.xmlsoap.org/soap/envelope/",  
    276             'wsc':    BEA.SECCONV, 
    277             'ns1':    "http://counter.com", 
    278         } 
    279         context = XPath.Context.Context(dom, processorNss=processorNss) 
    280           
    281         # Get the SignedInfo node and canonicalize  
    282         # 
    283         # Nb. When extracted the code adds the namespace attrinute to the 
     311        # Canonicalize the signedInfo node 
     312        # 
     313        # Nb. When extracted the code adds the namespace attribute to the 
    284314        # signedInfo!  This has important consequences for validation - 
    285315        # 
     
    294324        # to include namespace declarations for namespaces referenced in a doc 
    295325        # subset - yes to 2) 
    296         signedInfoNode = XPath.Compile('//ds:SignedInfo').evaluate(context)[0] 
    297         #signedInfoNode = dom.childNodes[0] 
    298326        c14nSignedInfo = Canonicalize(signedInfoNode) 
    299327 
     
    310338        b64EncSignatureValue = base64.encodestring(signatureValue).strip() 
    311339 
    312         # Get X.509 cert for inclusion into KeyInfo 
    313         x509Cert = X509.load_cert(certFilePath) 
    314          
    315         x509CertPat = re.compile(\ 
    316             '-----BEGIN CERTIFICATE-----\n?(.*?)\n?-----END CERTIFICATE-----', 
    317             re.S) 
    318         x509CertStr = x509CertPat.findall(x509Cert.as_pem())[0] 
    319          
    320         signedMsgTxt = msgTxt % (b64EncSignatureValue, x509CertStr) 
     340        signedMsgTxt = Canonicalize(msgDoc) % b64EncSignatureValue 
     341         
    321342         
    322343        # Extract RSA public key from the cert 
     
    334355         
    335356        # Parse file 
    336         node = NonvalidatingReader.parseStream(StringIO(xmlTxt)) 
     357        doc = NonvalidatingReader.parseStream(StringIO(xmlTxt)) 
    337358         
    338359        # Set namespaces for XPath queries 
     
    340361        { 
    341362            'ds':     DSIG.BASE,  
    342             'wsu':    OASIS.UTILITY,  
     363            'wsu':    WSU.UTILITY,  
    343364            'wsse':   OASIS.WSSE,  
    344365            'wsa':    WSA200403.ADDRESS, 
    345             'soapenv':"http://schemas.xmlsoap.org/soap/envelope/",  
    346             'wsc':    BEA.SECCONV, 
    347             'ns1':    "http://counter.com", 
     366            'soapenv':"http://schemas.xmlsoap.org/soap/envelope/"  
    348367        } 
    349         context = XPath.Context.Context(node, processorNss=processorNss) 
     368        docCtxt = XPath.Context.Context(doc, processorNss=processorNss) 
    350369         
    351370        # Two stagfe process: reference validation followed by signature  
     
    353372         
    354373        # 1) Reference Validation        
    355         refNode = XPath.Compile('//ds:Reference').evaluate(context)[0] 
    356          
    357         # Get the URI for the signed info 
    358         refURI = refNode.getAttributeNodeNS(None, 'URI').value 
    359          
    360          
    361         # Make a lambda to pick out node child or children with a given name 
     374        refNodes = XPath.Compile('//ds:Reference').evaluate(docCtxt) 
     375             
     376        # Make a lambda to pick out node child or children with a given  
     377        # name 
    362378        getElements = lambda node, nameList: \ 
    363             [n for n in node.childNodes if str(n.localName) in nameList]  
    364              
    365         transformsNode = getElements(refNode, "Transforms")[0] 
    366         transforms = getElements(transformsNode, "Transform") 
    367  
    368         algorithm = transforms[0].getAttributeNodeNS(None, "Algorithm").value 
    369          
    370         # Add extra keyword for Exclusive canonicalization method 
    371         kw = {} 
    372         if algorithm == DSIG.C14N_EXCL: 
    373             try: 
    374                 inclusiveNS = transforms[0].getElement(DSIG.C14N_EXCL,  
    375                                                    "InclusiveNamespaces") 
    376                 kw['unsuppressedPrefixes'] = \ 
    377                 inclusiveNS.getAttributeValue(None, "PrefixList").split() 
    378             except: 
     379                [n for n in node.childNodes if str(n.localName) in nameList]  
     380         
     381        for refNode in refNodes: 
     382            # Get the URI for the reference 
     383            refURI = refNode.getAttributeNodeNS(None, 'URI').value 
     384                             
     385            transformsNode = getElements(refNode, "Transforms")[0] 
     386            transforms = getElements(transformsNode, "Transform") 
     387     
     388            algorithm = transforms[0].getAttributeNodeNS(None,  
     389                                                         "Algorithm").value 
     390             
     391            # Add extra keyword for Exclusive canonicalization method 
     392            kw = {} 
     393            if algorithm == DSIG.C14N_EXCL: 
     394                try: 
     395                    inclusiveNS = transforms[0].getElement(DSIG.C14N_EXCL,  
     396                                                       "InclusiveNamespaces") 
     397                    kw['unsuppressedPrefixes'] = \ 
     398                    inclusiveNS.getAttributeValue(None, "PrefixList").split() 
     399                except: 
     400                    raise Exception, \ 
     401                'failed to handle transform (%s) in <ds:Reference URI="%s">'%\ 
     402                        (transforms[0], uri) 
     403         
     404            # Canonicalize the reference data and calculate the digest 
     405            if refURI[0] != "#": 
    379406                raise Exception, \ 
    380             'failed to handle transform (%s) in <ds:Reference URI="%s">'%\ 
    381                     (transforms[0], uri) 
    382            
    383         # For testing, URI is a local file - change later to a ref within the 
    384         # document    
    385         uriNode = NonvalidatingReader.parseStream(open(refURI)) 
    386      
    387         # Canonicalize the reference data and calculate the digest 
    388         c14nRef = Canonicalize(uriNode, **kw) 
    389         digestValue = base64.encodestring(sha(c14nRef).digest()).strip() 
    390          
    391         # Extract the digest value that was stored             
    392         digestNode = getElements(refNode, "DigestValue")[0] 
    393         nodeDigestValue = str(digestNode.childNodes[0].nodeValue).strip()    
    394          
    395         # Reference validates if the two digest values are the same 
    396         if digestValue != nodeDigestValue: 
    397             raise Exception, "DigestValues do not match: URI=%s" % uri 
     407                    "Expecting # identifier for Reference URI \"%s\"" % refURI 
     408                     
     409            # XPath reference 
     410            uriXPath = '//*[@wsu:Id="%s"]' % refURI[1:] 
     411            uriNode = XPath.Compile(uriXPath).evaluate(docCtxt)[0] 
     412 
     413            c14nRef = Canonicalize(uriNode, **kw) 
     414            digestValue = base64.encodestring(sha(c14nRef).digest()).strip() 
     415             
     416            # Extract the digest value that was stored             
     417            digestNode = getElements(refNode, "DigestValue")[0] 
     418            nodeDigestValue = str(digestNode.childNodes[0].nodeValue).strip()    
     419             
     420            # Reference validates if the two digest values are the same 
     421            if digestValue != nodeDigestValue: 
     422                raise Exception, "DigestValues do not match: URI=%s" % uri 
    398423         
    399424         
    400425        # 2) Signature Validation 
    401         signedInfoNode = XPath.Compile('//ds:SignedInfo').evaluate(context)[0] 
     426        signedInfoNode = XPath.Compile('//ds:SignedInfo').evaluate(docCtxt)[0] 
    402427 
    403428        # Get the canonicalization method - change later to check this and 
     
    407432                                              
    408433        algorithm = c14nMethodNode.getAttributeNodeNS(None, 'Algorithm').value 
    409  
     434        if algorithm != DSIG.C14N: 
     435            raise Exception, \ 
     436                "Only \"%s\" canonicalization algorithm supported" % DSIG.C14N 
     437                 
    410438        # Canonicalize the SignedInfo node and take digest 
    411439        c14nSignedInfo = Canonicalize(signedInfoNode)         
     
    415443        # calculated 
    416444        signatureValueNode = \ 
    417                     XPath.Compile('//ds:SignatureValue').evaluate(context)[0] 
     445                    XPath.Compile('//ds:SignatureValue').evaluate(docCtxt)[0] 
    418446 
    419447        # Remove base 64 encoding 
Note: See TracChangeset for help on using the changeset viewer.