source: TI12-security/trunk/python/ndg.security.common/ndg/security/common/wsSecurity.py @ 2136

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.common/ndg/security/common/wsSecurity.py@2136
Revision 2136, 48.7 KB checked in by pjkersha, 12 years ago (diff)

python/ndg.security.server/setup.py:

  • comment out Twisted from install - won't do egg install
  • updated long description

python/ndg.security.server/ndg/security/server/AttAuthority/server-config.tac:

  • added verifyingCertFilePath keyword to SignatureHandler? initialisation
  • added SSL capability

python/conf/attAuthorityProperties.xml,
python/ndg.security.test/ndg/security/test/AttAuthority/siteAAttAuthorityProperties.xml,
python/ndg.security.test/ndg/security/test/AttAuthority/siteBAttAuthorityProperties.xml,
python/ndg.security.server/ndg/security/server/AttAuthority/init.py:
added element names for reading SSL settings from properties file.

python/ndg.security.server/ndg/security/server/SessionMgr/server-config.tac:
added verifyingCertFilePath keyword to SignatureHandler? initialisation

python/conf/sessionMgrProperties.xml,
python/ndg.security.test/ndg/security/test/SessionMgr/sessionMgrProperties.xml,
python/ndg.security.server/ndg/security/server/SessionMgr/init.py:
added clntCertFile properties file element name for setting certificate for
verifying incoming SOAP messages.

python/ndg.security.server/ndg/security/server/SessionMgr/Makefile:
corrected typo.

python/ndg.security.server/ndg/security/server/MyProxy.py:
Put OpenSSLConfig and OpenSSLConfigError classes into their own package
'openssl' so that they can also be used by the Certificate Authority client.

python/www/html/certificateAuthority.wsdl,
python/ndg.security.server/ndg/security/server/ca/CertificateAuthority_services_server.py,
python/ndg.security.common/ndg/security/common/ca/CertificateAuthority_services_types.py,
python/ndg.security.common/ndg/security/common/ca/CertificateAuthority_services.py: updated operations to issueCert, revokeCert and getCRL.

python/ndg.security.test/ndg/security/test/AttAuthority/attAuthorityClientTest.cfg: changed address of service to connect to.

python/ndg.security.test/ndg/security/test/SessionMgr/sessionMgrClientTest.cfg:
alternative username connection settings

python/ndg.security.common/ndg/security/common/AttAuthority/init.py:
fixed typos in error message and comments.

ython/ndg.security.common/ndg/security/common/XMLSec.py: changed call to
getAttributeNodeNS to getAttributeNode for retrieving reference element URI
attribute.

python/ndg.security.common/ndg/security/common/ca/init.py: code for
Certificate Authority client

python/ndg.security.common/ndg/security/common/wsSecurity.py:

  • tidied up imports
  • added properties for setting keywords to reference and SignedInfo? C14N
  • changed sign method so that it is truely configurable allow use of inclusive or exclusive C14N based on the keywords set for reference and SignedInfo? C14N calls.
  • swapped calls to getAttributeNodeNS with getAttributeNode where appropriate.

java/DEWS/AttAuthority/appClientModule/META-INF/ibm-webservicesclient-bnd.xmi,
java/DEWS/AttAuthority/build/classes/META-INF/ibm-webservicesclient-bnd.xmi:
updated to that request generator correctly places X.509 cert in
BinarySecurityToken? element.

java/DEWS/AttAuthority/appClientModule/Main.java,
java/DEWS/AttAuthority/appClientjava/DEWS/AttAuthority/appClientModule/META-INF/ibm-webservicesclient-bnd.xmiModule/Main.java:
include calls to getX509Cert and getAttCert methods.

java/DEWS/SessionMgr/build/classes/META-INF/ibm-webservicesclient-bnd.xmi,
java/DEWS/SessionMgr/appClientModule/META-INF/ibm-webservicesclient-bnd.xmi:
updates for testing Session MAnager client

java/DEWS/SessionMgr/appClientModule/Main.java: switched username setting.

  • Property svn:executable set to *
Line 
1#!/bin/env python
2
3"""WS-Security test class includes digital signature handler
4
5NERC Data Grid Project
6
7P J Kershaw 01/09/06
8
9Copyright (C) 2006 CCLRC & NERC
10
11This software may be distributed under the terms of the Q Public License,
12version 1.0 or later.
13"""
14
15reposID = '$Id:$'
16
17import re
18
19# Digest and signature/verify
20from sha import sha
21from M2Crypto import X509, BIO, RSA
22import base64
23
24# For shared key encryption
25from Crypto.Cipher import AES, DES3
26import os
27
28import ZSI
29from ZSI.wstools.Namespaces import DSIG, ENCRYPTION, OASIS, WSU, WSA200403, \
30                                   SOAP, SCHEMA # last included for xsi
31                                   
32from ZSI.TC import ElementDeclaration,TypeDefinition
33from ZSI.generate.pyclass import pyclass_type
34
35from ZSI.wstools.Utility import DOMException
36from ZSI.wstools.Utility import NamespaceError, MessageInterface, ElementProxy
37
38# Canonicalization
39from ZSI.wstools.c14n import Canonicalize
40
41from xml.dom import Node
42from xml.xpath.Context import Context
43from xml import xpath
44
45# Include for re-parsing doc ready for canonicalization in sign method - see
46# associated note
47from xml.dom.ext.reader.PyExpat import Reader
48
49
50from ndg.security.common.X509 import X509Cert, X509CertParse, X509CertRead
51
52
53class _ENCRYPTION(ENCRYPTION):
54    '''Derived from ENCRYPTION class to add in extra 'tripledes-cbc' - is this
55    any different to 'des-cbc'?  ENCRYPTION class implies that it is the same
56    because it's assigned to 'BLOCK_3DES' ??'''
57    BLOCK_TRIPLEDES = "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"
58
59class _WSU(WSU):
60    '''Try different utility namespace for use with WebSphere'''
61    #UTILITY = "http://schemas.xmlsoap.org/ws/2003/06/utility"
62    UTILITY = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
63   
64def getElements(node, nameList):
65    '''DOM Helper function for getting child elements from a given node'''
66    # Avoid sub-string matches
67    nameList = isinstance(nameList, basestring) and [nameList] or nameList
68    return [n for n in node.childNodes if str(n.localName) in nameList]
69
70
71class VerifyError(Exception):
72    """Raised from SignatureHandler.verify if an error occurs in the signature
73    verification"""
74   
75class InvalidSignature(Exception):
76    """Raised from verify method for an invalid signature"""
77
78class SignatureError(Exception):
79    """Flag if an error occurs during signature generation"""
80       
81class SignatureHandler(object):
82    """class to handle signature and verification of signature with
83    WS-Security
84   
85    @type __beginCert: string
86    @param __beginCert: delimiter for beginning of base64 encoded portion of
87    a PEM encoded X.509 certificate
88    @type __endCert: string
89    @cvar: __endCert: equivalent end delimiter
90   
91    @type __x509CertPat: regular expression pattern object
92    @cvar __x509CertPat: regular expression for extracting the base64 encoded
93    portion of a PEM encoded X.509 certificate"""
94   
95    __beginCert = '-----BEGIN CERTIFICATE-----\n'
96    __endCert = '\n-----END CERTIFICATE-----'
97    __x509CertPat = re.compile(__beginCert + \
98                               '?(.*?)\n?-----END CERTIFICATE-----',
99                               re.S)
100   
101   
102    #_________________________________________________________________________
103    def __init__(self,
104                 verifyingCert=None,
105                 verifyingCertFilePath=None,
106                 signingCert=None,
107                 signingCertFilePath=None, 
108                 signingPriKey=None,
109                 signingPriKeyFilePath=None, 
110                 signingPriKeyPwd=None,
111                 refC14nKw={'unsuppressedPrefixes': ['xmlns', 
112                                                  'xsi', 
113                                                  'xsd', 
114                                                  'SOAP-ENV', 
115                                                  'wsu', 
116                                                  'wsse', 
117                                                  'ns1']},
118                # Added 'ec' to list P J Kershaw 01/02/07
119                signedInfoC14nKw = {'unsuppressedPrefixes': ['xsi', 
120                                                             'xsd', 
121                                                             'SOAP-ENV', 
122                                                             'ds', 
123                                                             'wsse', 
124                                                             'ec']}):
125
126        # Set keywords for canonicalization of SignedInfo and reference
127        # elements
128        self.__setRefC14nKw(refC14nKw)
129        self.__setSignedInfoC14nKw(signedInfoC14nKw)
130           
131
132        self.__setVerifyingCert(verifyingCert)
133        self.__setVerifyingCertFilePath(verifyingCertFilePath)
134       
135        self.__setSigningCert(signingCert)
136        self.__setSigningCertFilePath(signingCertFilePath)
137
138        # MUST be set before __setSigningPriKeyFilePath / __setSigningPriKey
139        # are called
140        self.__setSigningPriKeyPwd(signingPriKeyPwd)
141       
142        if signingPriKey is not None:
143            # Don't allow None for private key setting
144            self.__setSigningPriKey(signingPriKey)
145           
146        self.__setSigningPriKeyFilePath(signingPriKeyFilePath)
147       
148
149    #_________________________________________________________________________
150    def __checkC14nKw(self, Kw):
151        """Check keywords for canonicalization in signing process - generic
152        method for setting keywords for reference element and SignedInfo
153        element c14n"""
154       
155        # Check for dict/None - Set to None in order to use inclusive
156        # canonicalization
157        if Kw is not None and not isinstance(Kw, dict):
158            # Otherwise keywords must be a dictionary
159            raise AttributeError, \
160                "Expecting dictionary type for reference c14n keywords"
161               
162        elif Kw.get('unsuppressedPrefixes') and \
163             not isinstance(Kw['unsuppressedPrefixes'], list) and \
164             not isinstance(Kw['unsuppressedPrefixes'], tuple):
165            raise AttributeError, \
166                'Expecting list or tuple of prefix names for "%s" keyword' % \
167                'unsuppressedPrefixes'
168       
169               
170    #_________________________________________________________________________
171    def __setRefC14nKw(self, Kw):
172        """Set keywords for canonicalization of reference elements in the
173        signing process"""
174        self.__checkC14nKw(Kw)                   
175        self.__refC14nKw = Kw
176       
177    refC14nKw = property(fset=__setRefC14nKw,
178                         doc="Keywords for c14n of reference elements")
179       
180               
181    #_________________________________________________________________________
182    def __setSignedInfoC14nKw(self, Kw):
183        """Set keywords for canonicalization of SignedInfo element in the
184        signing process"""
185        self.__checkC14nKw(Kw)                   
186        self.__signedInfoC14nKw = Kw
187       
188    signedInfoC14nKw = property(fset=__setSignedInfoC14nKw,
189                                doc="Keywords for c14n of SignedInfo element")
190
191
192    #_________________________________________________________________________
193    def __refC14nIsExcl(self):
194        return isinstance(self.__refC14nKw, dict) and \
195               self.__refC14nKw.get('unsuppressedPrefixes') and \
196               len(self.__refC14nKw['unsuppressedPrefixes']) > 0
197               
198    refC14nIsExcl = property(fget=__refC14nIsExcl,
199    doc="Return True/False c14n for reference elements set to exclusive type")
200     
201
202    #_________________________________________________________________________
203    def __signedInfoC14nIsExcl(self):
204        return isinstance(self.__signedInfoC14nKw, dict) and \
205               self.__signedInfoC14nKw.get('unsuppressedPrefixes') and \
206               len(self.__signedInfoC14nKw['unsuppressedPrefixes']) > 0
207               
208    signedInfoC14nIsExcl = property(fget=__signedInfoC14nIsExcl,
209    doc="Return True/False c14n for SignedInfo element set to exclusive type")
210   
211   
212    #_________________________________________________________________________
213    def __setCert(self, cert):
214        """filter and convert input cert to signing verifying cert set
215        property methods.  For signingCert, set to None if it is not to be
216        included in the SOAP header.  For verifyingCert, set to None if this
217        cert can be expected to be retrieved from the SOAP header of the
218        message to be verified
219       
220        @type: ndg.security.common.X509.X509Cert / M2Crypto.X509.X509 /
221        string or None
222        @param cert: X.509 certificate. 
223       
224        @rtype ndg.security.common.X509.X509Cert
225        @return X.509 certificate object"""
226       
227        if cert is None or isinstance(cert, X509Cert):
228            # ndg.security.common.X509.X509Cert type / None
229            return cert
230           
231        elif isinstance(cert, X509.X509):
232            # M2Crypto.X509.X509 type
233            return X509Cert(m2CryptoX509=cert)
234           
235        elif isinstance(cert, basestring):
236            return X509CertParse(cert)
237       
238        else:
239            raise AttributeError, "X.509 Cert. must be type: " + \
240                "ndg.security.common.X509.X509Cert, M2Crypto.X509.X509 or " +\
241                "a base64 encoded string"
242
243   
244    #_________________________________________________________________________
245    def __getVerifyingCert(self):
246        '''Return X.509 cert object corresponding to cert used to verify the
247        signature in the last call to verify
248       
249         * Cert will correspond to one used in the LATEST call to verify, on
250         the next call it will be replaced
251         * if verify hasn't been called, the cert will be None
252       
253        @rtype: M2Crypto.X509.X509
254        @return: certificate object
255        '''
256        return self.__verifyingCert
257
258
259    #_________________________________________________________________________
260    def __setVerifyingCert(self, verifyingCert):
261        "Set property method for X.509 cert. used to verify a signature"
262        self.__verifyingCert = self.__setCert(verifyingCert)
263   
264        # Reset file path as it may no longer apply
265        self.__verifyingCertFilePath = None
266       
267    verifyingCert = property(fset=__setVerifyingCert,
268                             fget=__getVerifyingCert,
269                             doc="Set X.509 Cert. for verifying signature")
270
271
272    #_________________________________________________________________________
273    def __setVerifyingCertFilePath(self, verifyingCertFilePath):
274        "Set method for Service X.509 cert. file path property"
275       
276        if isinstance(verifyingCertFilePath, basestring):
277            self.__verifyingCert = X509CertRead(verifyingCertFilePath)
278           
279        elif verifyingCertFilePath is not None:
280            raise AttributeError, \
281            "Verifying X.509 Cert. file path must be None or a valid string"
282       
283        self.__verifyingCertFilePath = verifyingCertFilePath
284       
285    verifyingCertFilePath = property(fset=__setVerifyingCertFilePath,
286                    doc="file path of X.509 Cert. for verifying signature")
287
288
289    #_________________________________________________________________________
290    def __setSigningCert(self, signingCert):
291        "Set property method for X.509 cert. to be included with signature"
292        self.__signingCert = self.__setCert(signingCert)
293   
294        # Reset file path as it may no longer apply
295        self.__signingCertFilePath = None
296       
297    signingCert = property(fset=__setSigningCert,
298                             doc="Set X.509 Cert. to include signature")
299
300 
301    #_________________________________________________________________________
302    def __setSigningCertFilePath(self, signingCertFilePath):
303        "Set signature X.509 cert property method"
304       
305        if isinstance(signingCertFilePath, basestring):
306            self.__signingCert = X509CertRead(signingCertFilePath)
307           
308        elif signingCertFilePath is not None:
309            raise AttributeError, \
310                "Signature X.509 cert. file path must be a valid string"
311       
312        self.__signingCertFilePath = signingCertFilePath
313       
314       
315    signingCertFilePath = property(fset=__setSigningCertFilePath,
316                   doc="File path X.509 cert. to include with signed message")
317
318 
319    #_________________________________________________________________________
320    def __setSigningPriKeyPwd(self, signingPriKeyPwd):
321        "Set method for private key file password used to sign message"
322        if signingPriKeyPwd is not None and \
323           not isinstance(signingPriKeyPwd, basestring):
324            raise AttributeError, \
325                "Signing private key password must be None or a valid string"
326       
327        self.__signingPriKeyPwd = signingPriKeyPwd
328       
329    signingPriKeyPwd = property(fset=__setSigningPriKeyPwd,
330             doc="Password protecting private key file used to sign message")
331
332 
333    #_________________________________________________________________________
334    def __setSigningPriKey(self, signingPriKey):
335        """Set method for client private key
336       
337        Nb. if input is a string, signingPriKeyPwd will need to be set if
338        the key is password protected.
339       
340        @type signingPriKey: M2Crypto.RSA.RSA / string
341        @param signingPriKey: private key used to sign message"""
342       
343        if isinstance(signingPriKey, basestring):
344            pwdCallback = lambda *ar, **kw: self.__signingPriKeyPwd
345            self.__signingPriKey = RSA.load_key_string(signingPriKey,
346                                                       callback=pwdCallback)
347
348        elif isinstance(signingPriKey, RSA.RSA):
349            self.__signingPriKey = signingPriKey
350                   
351        else:
352            raise AttributeError, "Signing private key must be a valid " + \
353                                  "M2Crypto.RSA.RSA type or a string"
354               
355    signingPriKey = property(fset=__setSigningPriKey,
356                             doc="Private key used to sign outbound message")
357
358 
359    #_________________________________________________________________________
360    def __setSigningPriKeyFilePath(self, signingPriKeyFilePath):
361        """Set method for client private key file path
362       
363        signingPriKeyPwd MUST be set prior to a call to this method"""
364        if isinstance(signingPriKeyFilePath, basestring):                           
365            try:
366                # Read Private key to sign with   
367                priKeyFile = BIO.File(open(signingPriKeyFilePath)) 
368                pwdCallback = lambda *ar, **kw: self.__signingPriKeyPwd                                           
369                self.__signingPriKey = RSA.load_key_bio(priKeyFile, 
370                                                        callback=pwdCallback)           
371            except Exception, e:
372                raise AttributeError, \
373                                "Setting private key for signature: %s" % e
374       
375        elif signingPriKeyFilePath is not None:
376            raise AttributeError, \
377                        "Private key file path must be a valid string or None"
378       
379        self.__signingPriKeyFilePath = signingPriKeyFilePath
380       
381    signingPriKeyFilePath = property(fset=__setSigningPriKeyFilePath,
382                      doc="File path for private key used to sign message")
383
384
385    #_________________________________________________________________________
386    def sign(self, soapWriter):
387        '''Sign the message body and binary security token of a SOAP message
388       
389        @type soapWriter: ZSI.writer.SoapWriter
390        @param soapWriter: ZSI object to write SOAP message
391        '''
392       
393        # Namespaces for XPath searches
394        processorNss = \
395        {
396            'ds':     DSIG.BASE, 
397            'wsu':    _WSU.UTILITY, 
398            'wsse':   OASIS.WSSE, 
399            'soapenv':"http://schemas.xmlsoap.org/soap/envelope/" 
400        }
401
402        # Add X.509 cert as binary security token stripping BEGIN CERT and
403        # END CERT delimiters
404        x509CertStr = self.__class__.__x509CertPat.findall(\
405                                           self.__signingCert.toString())[0]
406
407        soapWriter._header.setNamespaceAttribute('wsse', OASIS.WSSE)
408        soapWriter._header.setNamespaceAttribute('wsu', _WSU.UTILITY)
409        soapWriter._header.setNamespaceAttribute('ds', DSIG.BASE)
410       
411        if self.refC14nIsExcl or self.signedInfoC14nIsExcl:
412            soapWriter._header.setNamespaceAttribute('ec', DSIG.C14N_EXCL)
413       
414        # Check <wsse:security> isn't already present in header
415        ctxt = Context(soapWriter.dom.node, processorNss=processorNss)
416        wsseNodes = xpath.Evaluate('//wsse:security', 
417                                   contextNode=soapWriter.dom.node, 
418                                   context=ctxt)
419        if len(wsseNodes) > 1:
420            raise SignatureError, 'wsse:Security element is already present'
421
422        # Add WSSE element
423        wsseElem = soapWriter._header.createAppendElement(OASIS.WSSE, 
424                                                         'Security')
425        wsseElem.setNamespaceAttribute('wsse', OASIS.WSSE)
426       
427        # Recipient MUST parse and check this signature
428        wsseElem.node.setAttribute('SOAP-ENV:mustUnderstand', "1")
429       
430        # Binary Security Token element will contain the X.509 cert
431        # corresponding to the private key used to sing the message
432        binSecTokElem = wsseElem.createAppendElement(OASIS.WSSE, 
433                                                     'BinarySecurityToken')
434       
435        # Value and encoding types to suite WebSphere WSSE implementation
436        valueType = \
437"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509"
438        binSecTokElem.node.setAttribute('ValueType', valueType)
439
440        encodingType = \
441"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
442        binSecTokElem.node.setAttribute('EncodingType', encodingType)
443       
444        # Add ID so that the binary token can be included in the signature
445        binSecTokElem.node.setAttribute('wsu:Id', "binaryToken")
446
447        binSecTokElem.createAppendTextNode(x509CertStr)
448
449       
450        # Signature
451        signatureElem = wsseElem.createAppendElement(DSIG.BASE, 'Signature')
452        signatureElem.setNamespaceAttribute('ds', DSIG.BASE)
453       
454        # Signature - Signed Info
455        signedInfoElem = signatureElem.createAppendElement(DSIG.BASE, 
456                                                           'SignedInfo')
457       
458        # Signed Info - Canonicalization method
459        c14nMethodElem = signedInfoElem.createAppendElement(DSIG.BASE,
460                                                    'CanonicalizationMethod')
461       
462        # Set based on 'signedInfoIsExcl' property
463        c14nAlgOpt = (DSIG.C14N, DSIG.C14N_EXCL)
464        signedInfoC14nAlg = c14nAlgOpt[self.signedInfoC14nIsExcl]
465       
466        c14nMethodElem.node.setAttribute('Algorithm', signedInfoC14nAlg)
467       
468        if self.signedInfoC14nIsExcl:
469            c14nInclNamespacesElem = c14nMethodElem.createAppendElement(\
470                                                    signedInfoC14nAlg,
471                                                    'InclusiveNamespaces')
472            c14nInclNamespacesElem.node.setAttribute('PrefixList', 
473                            ' '.join(self.__signedInfoC14nKw['unsuppressedPrefixes']))
474       
475        # Signed Info - Signature method
476        sigMethodElem = signedInfoElem.createAppendElement(DSIG.BASE,
477                                                    'SignatureMethod')
478        sigMethodElem.node.setAttribute('Algorithm', DSIG.SIG_RSA_SHA1)
479       
480        # Signature - Signature value
481        signatureValueElem = signatureElem.createAppendElement(DSIG.BASE, 
482                                                             'SignatureValue')
483       
484        # Key Info
485        KeyInfoElem = signatureElem.createAppendElement(DSIG.BASE, 'KeyInfo')
486        secTokRefElem = KeyInfoElem.createAppendElement(OASIS.WSSE, 
487                                                  'SecurityTokenReference')
488       
489        # Reference back to the binary token included earlier
490        wsseRefElem = secTokRefElem.createAppendElement(OASIS.WSSE, 
491                                                        'Reference')
492        wsseRefElem.node.setAttribute('URI', "#binaryToken")
493       
494        # Add Reference to body so that it can be included in the signature
495        soapWriter.body.node.setAttribute('wsu:Id', "body")
496        soapWriter.body.node.setAttribute('xmlns:wsu', _WSU.UTILITY)
497
498        # Serialize and re-parse prior to reference generation - calculating
499        # canonicalization based on soapWriter.dom.node seems to give an
500        # error: the order of wsu:Id attribute is not correct
501        docNode = Reader().fromString(str(soapWriter))
502        ctxt = Context(docNode, processorNss=processorNss)
503        refNodes = xpath.Evaluate('//*[@wsu:Id]', 
504                                  contextNode=docNode, 
505                                  context=ctxt)
506       
507        # Set based on 'signedInfoIsExcl' property
508        refC14nAlg = c14nAlgOpt[self.refC14nIsExcl]
509       
510        # 1) Reference Generation
511        #
512        # Find references
513        for refNode in refNodes:
514           
515            # Set URI attribute to point to reference to be signed
516            #uri = u"#" + refNode.getAttribute('wsu:Id')
517            uri = u"#" + refNode.attributes[(_WSU.UTILITY, 'Id')].value
518           
519            # Canonicalize reference
520            refC14n = Canonicalize(refNode, **self.__refC14nKw)
521           
522            # Calculate digest for reference and base 64 encode
523            #
524            # Nb. encodestring adds a trailing newline char
525            digestValue = base64.encodestring(sha(refC14n).digest()).strip()
526
527
528            # Add a new reference element to SignedInfo
529            refElem = signedInfoElem.createAppendElement(DSIG.BASE, 
530                                                         'Reference')
531            refElem.node.setAttribute('URI', uri)
532           
533            # Use ds:Transforms or wsse:TransformationParameters?
534            transformsElem = refElem.createAppendElement(DSIG.BASE, 
535                                                        'Transforms')
536            transformElem = transformsElem.createAppendElement(DSIG.BASE, 
537                                                               'Transform')
538
539            # Set Canonicalization algorithm type
540            transformElem.node.setAttribute('Algorithm', refC14nAlg)
541            if self.refC14nIsExcl:
542                # Exclusive C14N requires inclusive namespace elements
543                inclNamespacesElem = transformElem.createAppendElement(\
544                                                                                   refC14nAlg,
545                                                       'InclusiveNamespaces')
546                inclNamespacesElem.node.setAttribute('PrefixList',
547                                        ' '.join(self.__refC14nKw['unsuppressedPrefixes']))
548           
549            # Digest Method
550            digestMethodElem = refElem.createAppendElement(DSIG.BASE, 
551                                                           'DigestMethod')
552            digestMethodElem.node.setAttribute('Algorithm', DSIG.DIGEST_SHA1)
553           
554            # Digest Value
555            digestValueElem = refElem.createAppendElement(DSIG.BASE, 
556                                                          'DigestValue')
557            digestValueElem.createAppendTextNode(digestValue)
558
559   
560        # 2) Signature Generation
561        #       
562        # Canonicalize the signedInfo node
563        c14nSignedInfo = Canonicalize(signedInfoElem.node, 
564                                      **self.__signedInfoC14nKw)
565
566        # Calculate digest of SignedInfo
567        signedInfoDigestValue = sha(c14nSignedInfo).digest().strip()
568       
569        # Sign using the private key and base 64 encode the result
570        signatureValue = self.__signingPriKey.sign(signedInfoDigestValue)
571        b64EncSignatureValue = base64.encodestring(signatureValue).strip()
572
573        # Add to <SignatureValue>
574        signatureValueElem.createAppendTextNode(b64EncSignatureValue)
575
576
577    def verify(self, parsedSOAP):
578        """Verify signature
579       
580        @type parsedSOAP: ZSI.parse.ParsedSoap
581        @param parsedSOAP: object contain parsed SOAP message received from
582        sender"""
583       
584        processorNss = \
585        {
586            'ds':     DSIG.BASE, 
587            'wsu':    _WSU.UTILITY, 
588            'wsse':   OASIS.WSSE, 
589            'soapenv':"http://schemas.xmlsoap.org/soap/envelope/" 
590        }
591        ctxt = Context(parsedSOAP.dom, processorNss=processorNss)
592       
593
594        signatureNodes = xpath.Evaluate('//ds:Signature', 
595                                        contextNode=parsedSOAP.dom, 
596                                        context=ctxt)
597        if len(signatureNodes) > 1:
598            raise VerifyError, 'Multiple ds:Signature elements found'
599       
600        try:
601            signatureNodes = signatureNodes[0]
602        except:
603            # Message wasn't signed
604            return
605       
606        # Two stage process: reference validation followed by signature
607        # validation
608       
609        # 1) Reference Validation
610       
611        # Check for canonicalization set via ds:CanonicalizationMethod -
612        # Use this later as a back up in case no Canonicalization was set in
613        # the transforms elements
614        c14nMethodNode = xpath.Evaluate('//ds:CanonicalizationMethod', 
615                                        contextNode=parsedSOAP.dom, 
616                                        context=ctxt)[0]
617       
618        refNodes = xpath.Evaluate('//ds:Reference', 
619                                  contextNode=parsedSOAP.dom, 
620                                  context=ctxt)
621
622        for refNode in refNodes:
623            # Get the URI for the reference
624            refURI = refNode.getAttributeNode('URI').value
625                         
626            try:
627                transformsNode = getElements(refNode, "Transforms")[0]
628                transforms = getElements(transformsNode, "Transform")
629   
630                refAlgorithm = \
631                            transforms[0].getAttributeNode("Algorithm").value
632            except Exception, e:
633                raise VerifyError, \
634            'failed to get transform algorithm for <ds:Reference URI="%s">'%\
635                        (refURI, str(e))
636               
637            # Add extra keyword for Exclusive canonicalization method
638            refC14nKw = {}
639            if refAlgorithm == DSIG.C14N_EXCL:
640                try:
641                    inclusiveNS = getElements(transforms[0], 
642                                              "InclusiveNamespaces")
643                   
644                    pfxListAttNode = \
645                                inclusiveNS[0].getAttributeNode('PrefixList')
646                           
647                    refC14nKw['unsuppressedPrefixes'] = \
648                                                pfxListAttNode.value.split()
649                except:
650                    raise VerifyError, \
651                'failed to handle transform (%s) in <ds:Reference URI="%s">'%\
652                        (transforms[0], refURI)
653       
654            # Canonicalize the reference data and calculate the digest
655            if refURI[0] != "#":
656                raise VerifyError, \
657                    "Expecting # identifier for Reference URI \"%s\"" % refURI
658                   
659            # XPath reference
660            uriXPath = '//*[@wsu:Id="%s"]' % refURI[1:]
661            uriNode = xpath.Evaluate(uriXPath, 
662                                     contextNode=parsedSOAP.dom, 
663                                     context=ctxt)[0]
664
665            refC14n = Canonicalize(uriNode, **refC14nKw)
666            digestValue = base64.encodestring(sha(refC14n).digest()).strip()
667           
668            # Extract the digest value that was stored           
669            digestNode = getElements(refNode, "DigestValue")[0]
670            nodeDigestValue = str(digestNode.childNodes[0].nodeValue).strip()   
671           
672            # Reference validates if the two digest values are the same
673            if digestValue != nodeDigestValue:
674                raise InvalidSignature, \
675                        'Digest Values do not match for URI: "%s"' % refURI
676               
677        # 2) Signature Validation
678        signedInfoNode = xpath.Evaluate('//ds:SignedInfo',
679                                        contextNode=parsedSOAP.dom, 
680                                        context=ctxt)[0]
681
682        # Get algorithm used for canonicalization of the SignedInfo
683        # element.  Nb. This is NOT necessarily the same as that used to
684        # canonicalize the reference elements checked above!
685        signedInfoC14nAlg = c14nMethodNode.getAttributeNode("Algorithm").value
686        signedInfoC14nKw = {}
687        if signedInfoC14nAlg == DSIG.C14N_EXCL:
688            try:
689                inclusiveNS = getElements(c14nMethodNode,
690                                          "InclusiveNamespaces")
691               
692                pfxListAttNode = inclusiveNS[0].getAttributeNode('PrefixList')
693                signedInfoC14nKw['unsuppressedPrefixes'] = \
694                                                pfxListAttNode.value.split()
695                                                     
696            except Exception, e:
697                raise VerifyError, \
698            'failed to handle exclusive canonicalisation for SignedInfo: %s'%\
699                        str(e)
700
701        # Canonicalize the SignedInfo node and take digest
702        c14nSignedInfo = Canonicalize(signedInfoNode, **signedInfoC14nKw)       
703        signedInfoDigestValue = sha(c14nSignedInfo).digest()
704       
705        # Get the signature value in order to check against the digest just
706        # calculated
707        signatureValueNode = xpath.Evaluate('//ds:SignatureValue',
708                                            contextNode=parsedSOAP.dom, 
709                                            context=ctxt)[0]
710
711        # Remove base 64 encoding
712        b64EncSignatureValue = \
713                    str(signatureValueNode.childNodes[0].nodeValue).strip()
714                   
715        signatureValue = base64.decodestring(b64EncSignatureValue)
716
717
718        # Look for X.509 Cert in wsse:BinarySecurityToken node
719        try:
720            binSecTokNode = xpath.Evaluate('//wsse:BinarySecurityToken',
721                                           contextNode=parsedSOAP.dom,
722                                           context=ctxt)[0]
723        except:
724            # Signature may not have included the Binary Security Token in
725            # which case the verifying cert will need to have been set
726            # elsewhere
727            binSecTokNode = None
728            pass 
729       
730        #import pdb;pdb.set_trace()       
731        if binSecTokNode:
732            try:
733                x509CertTxt=str(binSecTokNode.childNodes[0]._get_nodeValue())
734               
735                # Convert parsed cert text into form that can be read
736                # by X.509 string parser
737                if x509CertTxt[65] != "\n":
738                    # Expecting cert split into lines of length 64 bytes
739                    x509CertSpl = re.split('(.{64})', x509CertTxt)
740                    x509CertTxt = '\n'.join([i for i in x509CertSpl if i])
741                   
742                b64EncX509Cert = self.__class__.__beginCert + x509CertTxt + \
743                         self.__class__.__endCert
744                             
745                self.__setVerifyingCert(b64EncX509Cert)
746            except Exception, e:
747                raise VerifyError, "Error extracting BinarySecurityToken " + \
748                                   "from WSSE header: " + str(e)
749
750        if self.__verifyingCert is None:
751            raise VerifyError, "No certificate set for verification " + \
752                "of the signature"
753       
754        # Extract RSA public key from the cert
755        rsaPubKey = self.__verifyingCert.m2CryptoX509.get_pubkey().get_rsa()
756       
757        # Apply the signature verification
758        try:
759            verify = rsaPubKey.verify(signedInfoDigestValue, signatureValue)
760        except RSA.RSAError, e:
761            raise VerifyError, "Error in Signature: " + str(e)
762       
763        if not verify:
764            raise InvalidSignature, "Invalid signature"
765       
766        #print "Signature OK"
767
768
769class EncryptionError(Exception):
770    """Flags an error in the encryption process"""
771
772class DecryptionError(Exception):
773    """Raised from EncryptionHandler.decrypt if an error occurs with the
774    decryption process"""
775
776
777class EncryptionHandler(object):
778    """Encrypt/Decrypt SOAP messages using WS-Security""" 
779   
780    # Map namespace URIs to Crypto algorithm module and mode
781    cryptoAlg = \
782    {
783         _ENCRYPTION.WRAP_AES256:      {'module':       AES, 
784                                        'mode':         AES.MODE_ECB,
785                                        'blockSize':    16},
786         
787         # CBC (Cipher Block Chaining) modes
788         _ENCRYPTION.BLOCK_AES256:     {'module':       AES, 
789                                        'mode':         AES.MODE_CBC,
790                                        'blockSize':    16},
791                                       
792         _ENCRYPTION.BLOCK_TRIPLEDES:  {'module':       DES3, 
793                                        'mode':         DES3.MODE_CBC,
794                                        'blockSize':    8}   
795    }
796
797     
798    def __init__(self,
799                 signingCertFilePath=None, 
800                 signingPriKeyFilePath=None, 
801                 signingPriKeyPwd=None,
802                 chkSecurityTokRef=False,
803                 encrNS=_ENCRYPTION.BLOCK_AES256):
804       
805        self.__signingCertFilePath = signingCertFilePath
806        self.__signingPriKeyFilePath = signingPriKeyFilePath
807        self.__signingPriKeyPwd = signingPriKeyPwd
808       
809        self.__chkSecurityTokRef = chkSecurityTokRef
810       
811        # Algorithm for shared key encryption
812        try:
813            self.__encrAlg = self.cryptoAlg[encrNS]
814           
815        except KeyError:
816            raise EncryptionError, \
817        'Input encryption algorithm namespace "%s" is not supported' % encrNS
818
819        self.__encrNS = encrNS
820       
821       
822    def encrypt(self, soapWriter):
823        """Encrypt an outbound SOAP message
824       
825        Use Key Wrapping - message is encrypted using a shared key which
826        itself is encrypted with the public key provided by the X.509 cert.
827        signingCertFilePath"""
828       
829        # Use X.509 Cert to encrypt
830        x509Cert = X509.load_cert(self.__signingCertFilePath)
831       
832        soapWriter.dom.setNamespaceAttribute('wsse', OASIS.WSSE)
833        soapWriter.dom.setNamespaceAttribute('xenc', _ENCRYPTION.BASE)
834        soapWriter.dom.setNamespaceAttribute('ds', DSIG.BASE)
835       
836        # TODO: Put in a check to make sure <wsse:security> isn't already
837        # present in header
838        wsseElem = soapWriter._header.createAppendElement(OASIS.WSSE, 
839                                                         'Security')
840        wsseElem.node.setAttribute('SOAP-ENV:mustUnderstand', "1")
841       
842        encrKeyElem = wsseElem.createAppendElement(_ENCRYPTION.BASE, 
843                                                   'EncryptedKey')
844       
845        # Encryption method used to encrypt the shared key
846        keyEncrMethodElem = encrKeyElem.createAppendElement(_ENCRYPTION.BASE, 
847                                                        'EncryptionMethod')
848       
849        keyEncrMethodElem.node.setAttribute('Algorithm', 
850                                            _ENCRYPTION.KT_RSA_1_5)
851
852
853        # Key Info
854        KeyInfoElem = encrKeyElem.createAppendElement(DSIG.BASE, 'KeyInfo')
855       
856        secTokRefElem = KeyInfoElem.createAppendElement(OASIS.WSSE, 
857                                                  'SecurityTokenReference')
858       
859        x509IssSerialElem = secTokRefElem.createAppendElement(DSIG.BASE, 
860                                                          'X509IssuerSerial')
861
862       
863        x509IssNameElem = x509IssSerialElem.createAppendElement(DSIG.BASE, 
864                                                          'X509IssuerName')
865        x509IssNameElem.createAppendTextNode(x509Cert.get_issuer().as_text())
866
867       
868        x509IssSerialNumElem = x509IssSerialElem.createAppendElement(
869                                                  DSIG.BASE, 
870                                                  'X509IssuerSerialNumber')
871       
872        x509IssSerialNumElem.createAppendTextNode(
873                                          str(x509Cert.get_serial_number()))
874
875        # References to what has been encrypted
876        encrKeyCiphDataElem = encrKeyElem.createAppendElement(
877                                                          _ENCRYPTION.BASE,
878                                                          'CipherData')
879       
880        encrKeyCiphValElem = encrKeyCiphDataElem.createAppendElement(
881                                                          _ENCRYPTION.BASE,
882                                                          'CipherValue')
883
884        # References to what has been encrypted
885        refListElem = encrKeyElem.createAppendElement(_ENCRYPTION.BASE,
886                                                      'ReferenceList')
887       
888        dataRefElem = refListElem.createAppendElement(_ENCRYPTION.BASE,
889                                                      'DataReference')
890        dataRefElem.node.setAttribute('URI', "#encrypted")
891
892                     
893        # Add Encrypted data to SOAP body
894        encrDataElem = soapWriter.body.createAppendElement(_ENCRYPTION.BASE, 
895                                                           'EncryptedData')
896        encrDataElem.node.setAttribute('Id', 'encrypted')
897        encrDataElem.node.setAttribute('Type', _ENCRYPTION.BASE) 
898             
899        # Encryption method used to encrypt the target data
900        dataEncrMethodElem = encrDataElem.createAppendElement(
901                                                      _ENCRYPTION.BASE, 
902                                                      'EncryptionMethod')
903       
904        dataEncrMethodElem.node.setAttribute('Algorithm', self.__encrNS)
905       
906        # Cipher data
907        ciphDataElem = encrDataElem.createAppendElement(_ENCRYPTION.BASE,
908                                                        'CipherData')
909       
910        ciphValueElem = ciphDataElem.createAppendElement(_ENCRYPTION.BASE,
911                                                         'CipherValue')
912
913
914        # Get elements from SOAP body for encryption
915        dataElem = soapWriter.body.node.childNodes[0]
916        data = dataElem.toxml()
917     
918        # Pad data to nearest multiple of encryption algorithm's block size   
919        modData = len(data) % self.__encrAlg['blockSize']
920        nPad = modData and self.__encrAlg['blockSize'] - modData or 0
921       
922        # PAd with random junk but ...
923        data += os.urandom(nPad-1)
924       
925        # Last byte should be number of padding bytes
926        # (http://www.w3.org/TR/xmlenc-core/#sec-Alg-Block)
927        data += chr(nPad)       
928       
929        # Generate shared key and input vector - for testing use hard-coded
930        # values to allow later comparison             
931        sharedKey = os.urandom(self.__encrAlg['blockSize'])
932        iv = os.urandom(self.__encrAlg['blockSize'])
933       
934        alg = self.__encrAlg['module'].new(sharedKey,
935                                           self.__encrAlg['mode'],
936                                           iv)
937 
938        # Encrypt required elements - prepend input vector
939        encryptedData = alg.encrypt(iv + data)
940        dataCiphValue = base64.encodestring(encryptedData).strip()
941
942        ciphValueElem.createAppendTextNode(dataCiphValue)
943       
944       
945        # ! Delete unencrypted message body elements !
946        soapWriter.body.node.removeChild(dataElem)
947
948       
949        # Use X.509 cert public key to encrypt the shared key - Extract key
950        # from the cert
951        rsaPubKey = x509Cert.get_pubkey().get_rsa()
952       
953        # Encrypt the shared key
954        encryptedSharedKey = rsaPubKey.public_encrypt(sharedKey, 
955                                                      RSA.pkcs1_padding)
956       
957        encrKeyCiphVal = base64.encodestring(encryptedSharedKey).strip()
958       
959        # Add the encrypted shared key to the EncryptedKey section in the SOAP
960        # header
961        encrKeyCiphValElem.createAppendTextNode(encrKeyCiphVal)
962
963#        print soapWriter.dom.node.toprettyxml()
964#        import pdb;pdb.set_trace()
965       
966       
967    def decrypt(self, parsedSOAP):
968        """Decrypt an inbound SOAP message"""
969       
970        processorNss = \
971        {
972            'xenc':   _ENCRYPTION.BASE,
973            'ds':     DSIG.BASE, 
974            'wsu':    _WSU.UTILITY, 
975            'wsse':   OASIS.WSSE, 
976            'soapenv':"http://schemas.xmlsoap.org/soap/envelope/" 
977        }
978        ctxt = Context(parsedSOAP.dom, processorNss=processorNss)
979       
980        refListNodes = xpath.Evaluate('//xenc:ReferenceList', 
981                                      contextNode=parsedSOAP.dom, 
982                                      context=ctxt)
983        if len(refListNodes) > 1:
984            raise DecryptionError, 'Expecting a single ReferenceList element'
985       
986        try:
987            refListNode = refListNodes[0]
988        except:
989            # Message wasn't encrypted - is this OK or is a check needed for
990            # encryption info in SOAP body - enveloped form?
991            return
992
993
994        # Check for wrapped key encryption
995        encrKeyNodes = xpath.Evaluate('//xenc:EncryptedKey', 
996                                      contextNode=parsedSOAP.dom, 
997                                      context=ctxt)
998        if len(encrKeyNodes) > 1:
999            raise DecryptionError, 'This implementation can only handle ' + \
1000                                   'single EncryptedKey element'
1001       
1002        try:
1003            encrKeyNode = encrKeyNodes[0]
1004        except:
1005            # Shared key encryption used - leave out for the moment
1006            raise DecryptionError, 'This implementation can only handle ' + \
1007                                   'wrapped key encryption'
1008
1009       
1010        # Check encryption method
1011        keyEncrMethodNode = getElements(encrKeyNode, 'EncryptionMethod')[0]     
1012        keyAlgorithm = keyEncrMethodNode.getAttributeNode("Algorithm").value
1013        if keyAlgorithm != _ENCRYPTION.KT_RSA_1_5:
1014            raise DecryptionError, \
1015            'Encryption algorithm for wrapped key is "%s", expecting "%s"' % \
1016                (keyAlgorithm, _ENCRYPTION.KT_RSA_1_5)
1017
1018                                                           
1019        if self.__chkSecurityTokRef and self.__signingCertFilePath:
1020             
1021            # Check input cert. against SecurityTokenReference
1022            securityTokRefXPath = '/ds:KeyInfo/wsse:SecurityTokenReference'
1023            securityTokRefNode = xpath.Evaluate(securityTokRefXPath, 
1024                                                contextNode=encrKeyNode, 
1025                                                context=ctxt)
1026            # TODO: Look for ds:X509* elements to check against X.509 cert
1027            # input
1028
1029
1030        # Look for cipher data for wrapped key
1031        keyCiphDataNode = getElements(encrKeyNode, 'CipherData')[0]
1032        keyCiphValNode = getElements(keyCiphDataNode, 'CipherValue')[0]
1033
1034        keyCiphVal = str(keyCiphValNode.childNodes[0].nodeValue)
1035        encryptedKey = base64.decodestring(keyCiphVal)
1036
1037        # Read RSA Private key in order to decrypt wrapped key 
1038        priKeyFile = BIO.File(open(self.__signingPriKeyFilePath))         
1039        pwdCallback = lambda *ar, **kw: self.__signingPriKeyPwd                                       
1040        priKey = RSA.load_key_bio(priKeyFile, callback=pwdCallback)
1041       
1042        sharedKey = priKey.private_decrypt(encryptedKey, RSA.pkcs1_padding)
1043       
1044
1045        # Check list of data elements that have been encrypted
1046        for dataRefNode in refListNode.childNodes:
1047
1048            # Get the URI for the reference
1049            dataRefURI = dataRefNode.getAttributeNode('URI').value                           
1050            if dataRefURI[0] != "#":
1051                raise VerifyError, \
1052                    "Expecting # identifier for DataReference URI \"%s\"" % \
1053                    dataRefURI
1054
1055            # XPath reference - need to check for wsu namespace qualified?
1056            #encrNodeXPath = '//*[@wsu:Id="%s"]' % dataRefURI[1:]
1057            encrNodeXPath = '//*[@Id="%s"]' % dataRefURI[1:]
1058            encrNode = xpath.Evaluate(encrNodeXPath, 
1059                                      contextNode=parsedSOAP.dom, 
1060                                      context=ctxt)[0]
1061               
1062            dataEncrMethodNode = getElements(encrNode, 'EncryptionMethod')[0]     
1063            dataAlgorithm = \
1064                        dataEncrMethodNode.getAttributeNode("Algorithm").value
1065            try:       
1066                # Match algorithm name to Crypto module
1067                CryptoAlg = self.cryptoAlg[dataAlgorithm]
1068               
1069            except KeyError:
1070                raise DecryptionError, \
1071'Encryption algorithm for data is "%s", supported algorithms are:\n "%s"' % \
1072                    (keyAlgorithm, "\n".join(self.cryptoAlg.keys()))
1073
1074            # Get Data
1075            dataCiphDataNode = getElements(encrNode, 'CipherData')[0]
1076            dataCiphValNode = getElements(dataCiphDataNode, 'CipherValue')[0]
1077       
1078            dataCiphVal = str(dataCiphValNode.childNodes[0].nodeValue)
1079            encryptedData = base64.decodestring(dataCiphVal)
1080           
1081            alg = CryptoAlg['module'].new(sharedKey, CryptoAlg['mode'])
1082            decryptedData = alg.decrypt(encryptedData)
1083           
1084            # Strip prefix - assume is block size
1085            decryptedData = decryptedData[CryptoAlg['blockSize']:]
1086           
1087            # Strip any padding suffix - Last byte should be number of padding
1088            # bytes
1089            # (http://www.w3.org/TR/xmlenc-core/#sec-Alg-Block)
1090            lastChar = decryptedData[-1]
1091            nPad = ord(lastChar)
1092           
1093            # Sanity check - there may be no padding at all - the last byte
1094            # being the end of the encrypted XML?
1095            #
1096            # TODO: are there better sanity checks than this?!
1097            if nPad < CryptoAlg['blockSize'] and nPad > 0 and \
1098               lastChar != '\n' and lastChar != '>':
1099               
1100                # Follow http://www.w3.org/TR/xmlenc-core/#sec-Alg-Block -
1101                # last byte gives number of padding bytes
1102                decryptedData = decryptedData[:-nPad]
1103
1104
1105            # Parse the encrypted data - inherit from Reader as a fudge to
1106            # enable relevant namespaces to be added prior to parse
1107            processorNss.update({'xsi': SCHEMA.XSI3, 'ns1': 'urn:ZSI:examples'})
1108            class _Reader(Reader):
1109                def initState(self, ownerDoc=None):
1110                    Reader.initState(self, ownerDoc=ownerDoc)
1111                    self._namespaces.update(processorNss)
1112                   
1113            rdr = _Reader()
1114            dataNode = rdr.fromString(decryptedData, ownerDoc=parsedSOAP.dom)
1115           
1116            # Add decrypted element to parent and remove encrypted one
1117            parentNode = encrNode._get_parentNode()
1118            parentNode.appendChild(dataNode)
1119            parentNode.removeChild(encrNode)
1120           
1121            from xml.dom.ext import ReleaseNode
1122            ReleaseNode(encrNode)
1123           
1124            # Ensure body_root attribute is up to date in case it was
1125            # previously encrypted
1126            parsedSOAP.body_root = parsedSOAP.body.childNodes[0]
1127            #print decryptedData
1128            #import pdb;pdb.set_trace()
1129
1130
1131#_____________________________________________________________________________
1132from zope.interface import classProvides, implements, Interface
1133import twisted.web.http
1134from twisted.python import log, failure
1135
1136from ZSI.twisted.WSresource import DefaultHandlerChain, \
1137    DefaultCallbackHandler, CallbackChainInterface, HandlerChainInterface, \
1138    DataHandler
1139   
1140from ZSI import _get_element_nsuri_name, EvaluateException, ParseException
1141   
1142   
1143class WSSecurityHandlerChainFactory:
1144    protocol = DefaultHandlerChain
1145   
1146    @classmethod
1147    def newInstance(cls):
1148        return cls.protocol(DefaultCallbackHandler, 
1149                            DataHandler,
1150                            WSSecurityHandler)
1151   
1152
1153class WSSecurityHandler:
1154    classProvides(HandlerChainInterface)
1155
1156    signatureHandler = None
1157   
1158    @classmethod
1159    def processRequest(cls, ps, **kw):
1160        """invokes callback that should return a (request,response) tuple.
1161        representing the SOAP request and response respectively.
1162        ps -- ParsedSoap instance representing HTTP Body.
1163        request -- twisted.web.server.Request
1164        """
1165        if cls.signatureHandler:
1166            cls.signatureHandler.verify(ps)
1167           
1168        return ps
1169   
1170    @classmethod
1171    def processResponse(cls, sw, **kw):
1172       
1173        if cls.signatureHandler:
1174            cls.signatureHandler.sign(sw)
1175           
1176        return sw
Note: See TracBrowser for help on using the repository browser.