source: TI12-security/trunk/python/ndg.security.common/ndg/security/common/wssecurity/BaseSignatureHandler.py @ 4131

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.common/ndg/security/common/wssecurity/BaseSignatureHandler.py@4131
Revision 4131, 28.8 KB checked in by cbyrom, 12 years ago (diff)

Create new utility package with class, ConfigFileParsers? - a utility
class with parsers for XML and INI style config files. This takes
a filename, together with an optional dictionary of valid keys (to
check for invalid config inputs) + optional section list (to restrict
parsing of INI files to particular sections) and returns a
dictionary of read in properties. NB, if valid keys are specified
and not featured in the prop file, default values are set up in the
returned property dict.
Implemented use of the ConfigFileParsers? in the AttAuthority? service.
Added new testsuite (together with noseTests class to drive tests) to
exercise the new parsers in the context of the AttAuthority? section +
added test config files.

Line 
1""" Base class for the WS-Security digital signature handlers - to allow sharing of common code
2
3NERC Data Grid Project
4"""
5__author__ = "C Byrom"
6__date__ = "18/08/08"
7__copyright__ = ""
8__license__ = \
9"""This software may be distributed under the terms of the Q Public
10License, version 1.0 or later."""
11__contact__ = "P.J.Kershaw@rl.ac.uk"
12__revision__ = '$Id: $'
13
14import re
15
16# Digest and signature/verify
17from sha import sha
18from M2Crypto import X509, BIO, RSA
19import base64
20
21# Conditional import as this is required for the encryption
22# handler
23try:
24    # For shared key encryption
25    from Crypto.Cipher import AES, DES3
26except:
27    from warnings import warn
28    warn('Crypto.Cipher not available: EncryptionHandler disabled!',
29         RuntimeWarning)
30    class AES:
31        MODE_ECB = None
32        MODE_CBC = None
33       
34    class DES3: 
35        MODE_CBC = None
36
37import os
38
39import ZSI
40from ZSI.wstools.Namespaces import DSIG, ENCRYPTION, WSU, WSA200403, \
41                                   SOAP, SCHEMA # last included for xsi
42
43from ZSI.wstools.Namespaces import OASIS as _OASIS
44                                 
45from ZSI.TC import ElementDeclaration,TypeDefinition
46from ZSI.generate.pyclass import pyclass_type
47
48from ZSI.wstools.Utility import DOMException
49from ZSI.wstools.Utility import NamespaceError, MessageInterface, ElementProxy
50
51# Canonicalization
52from ZSI.wstools.c14n import Canonicalize
53
54from xml.dom import Node
55from xml.xpath.Context import Context
56from xml import xpath
57
58# Include for re-parsing doc ready for canonicalization in sign method - see
59# associated note
60from xml.dom.ext.reader.PyExpat import Reader
61
62# Enable settings from a config file
63from ndg.security.common.wssecurity import WSSecurityConfig
64
65from ndg.security.common.X509 import X509Cert, X509CertParse, X509CertRead, \
66X509Stack, X509StackParseFromDER
67
68from datetime import datetime, timedelta
69import logging
70log = logging.getLogger(__name__)
71
72
73class _ENCRYPTION(ENCRYPTION):
74    '''Derived from ENCRYPTION class to add in extra 'tripledes-cbc' - is this
75    any different to 'des-cbc'?  ENCRYPTION class implies that it is the same
76    because it's assigned to 'BLOCK_3DES' ??'''
77    BLOCK_TRIPLEDES = "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"
78
79class _WSU(WSU):
80    '''Try different utility namespace for use with WebSphere'''
81    #UTILITY = "http://schemas.xmlsoap.org/ws/2003/06/utility"
82    UTILITY = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
83
84class OASIS(_OASIS):
85    # wss4j 1.5.3
86    WSSE11 = "http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd"
87    # wss4j 1.5.1
88    #WSSE11 = "http://docs.oasis-open.org/wss/2005/xx/oasis-2005xx-wss-wssecurity-secext-1.1.xsd"
89
90
91class WSSecurityError(Exception):
92    """For WS-Security generic exceptions not covered by other exception
93    classes in this module"""
94    def __init__(self, errorMessage):
95        log.warning(errorMessage)
96        super(WSSecurityError, self).__init__(errorMessage)
97
98class InvalidCertChain(WSSecurityError):   
99    """Raised from SignatureHandler.verify if the certificate submitted to
100    verify a signature is not from a known CA"""
101   
102class VerifyError(WSSecurityError):
103    """Raised from SignatureHandler.verify if an error occurs in the signature
104    verification"""
105 
106class TimestampError(WSSecurityError):
107    """Raised from SignatureHandler._verifyTimestamp if there is a problem with
108    the created or expiry times in an input message Timestamp"""
109   
110class InvalidSignature(WSSecurityError):
111    """Raised from verify method for an invalid signature"""
112
113class SignatureError(WSSecurityError):
114    """Flag if an error occurs during signature generation"""
115
116class NoSignatureFound(WSSecurityError):
117    """Raise from SignatureHandler.verify if inbound message is not signed"""
118
119
120class BaseSignatureHandler(object):
121    """Class to handle signature and verification of signature with
122    WS-Security
123   
124    @cvar binSecTokValType: supported ValueTypes for BinarySecurityToken
125    element in WSSE header
126    @type binSecTokValType: dict
127   
128    @ivar addTimestamp: set to true to add a timestamp to outbound messages
129    @type addTimestamp: bool
130
131    @ivar applySignatureConfirmation: for servers - set this flag to enable the
132    signature value of a request to be recorded and included with a
133    SignatureConfirmation element in the response.
134    @type applySignatureConfirmation: bool
135   
136    @param b64EncSignatureValue: base 64 encoded signature value for the last
137    message verified
138    @type b64EncSignatureValue: string/None"""
139
140   
141    binSecTokValType = {
142        "X509PKIPathv1": OASIS.X509TOKEN.X509PKIPathv1,
143        "X509":          OASIS.X509TOKEN.X509,
144        "X509v3":        OASIS.X509TOKEN.X509+"v3"
145    }
146
147
148    #_________________________________________________________________________
149    def __init__(self, cfg=None, cfgFileSection='DEFAULT',
150                 cfgClass=WSSecurityConfig, **kw):
151        '''
152        @keyword reqBinSecTokValType: set the ValueType for the BinarySecurityToken
153        added to the WSSE header for a signed message.  See
154        __setReqBinSecTokValType method and binSecTokValType class variable
155        for options.  binSecTokValType determines whether signingCert or
156        signingCertChain attributes will be used.       
157        @type binSecTokValType: string
158       
159        @keyword verifyingCert: X.509 certificate used by verify method to
160        verify a message.  This argument can be omitted if the message to
161        be verified contains the X.509 certificate in the
162        BinarySecurityToken element.  In this case, the cert read from the
163        message will be assigned to the verifyingCert attribute.
164        @type verifyingCert: M2Crypto.X509.X509 /
165        ndg.security.common.X509.X509Cert
166       
167        @keyword verifyingCertFilePath: alternative input to the above, pass
168        the file path to the certificate stored in a file
169        @type verifyingCertFilePath: string
170       
171        @keyword signingCert: certificate associated with private key used to
172        sign a message.  The sign method will add this to the
173        BinarySecurityToken element of the WSSE header.  binSecTokValType
174        attribute must be set to 'X509' or 'X509v3' ValueTyep.  As an
175        alternative, use signingCertChain - see below...
176        @type signingCert: M2Crypto.X509.X509 /
177        ndg.security.common.X509.X509Cert
178       
179        @keyword signingCertFilePath: alternative input to the above, pass
180        the file path to the certificate stored in a file
181        @type signingCertFilePath: string
182       
183        @keyword signingCertChain: pass a list of certificates constituting a
184        chain of trust from the certificate used to verifying the signature
185        backward to the CA cert.  The CA cert need not be included.  To use
186        this option, reqBinSecTokValType must be set to the 'X509PKIPathv1'
187        ValueType
188        @type signingCertChain: list or tuple
189       
190        @keyword signingPriKey: private key used to be sign method to sign
191        message
192        @type signingPriKey: M2Crypto.RSA.
193       
194        @keyword signingPriKeyFilePath: equivalent to the above but pass
195        private key from PEM file
196        @type signingPriKeyFilePath: string
197       
198        @keyword signingPriKeyPwd: password protecting private key.  Set /
199        default to None if there is no password.
200        @type signingPriKeyPwd: string or None
201       
202        @keyword caCertDirPath: establish trust for signature verification.
203        This is a directory containing CA certificates.  These are used to
204        verify the certificate used to verify the message signature.
205        @type caCertDirPath: string
206       
207        @keyword caCertFilePathList: same as above except pass in a list of
208        file paths instead of a single directory name.
209        @type caCertFilePathList: list or tuple
210       
211        @keyword addTimestamp: set to true to add a timestamp to outbound
212        messages
213        @type addTimestamp: bool
214       
215        @keyword applySignatureConfirmation: for servers - set this flag to enable the signature value of a
216        request to be recorded and included with a SignatureConfirmation
217        element in the response.
218        @type : bool
219       
220        @param refC14nInclNS: list of namespaces to include in reference
221        Canonicalization.
222        @type refC14nInclNS: list
223       
224        @param signedInfoC14nInclNS: list of namespaces to include in
225        Signed Info Canonicalization.
226        @type signedInfoC14nInclNS: list
227        '''
228        log.debug("BaseSignatureHandler.__init__ ...")
229
230        # WSSecurityConfig is the default class for reading config params but
231        # alternative derivative class may be passed in instead.
232        if not issubclass(cfgClass, WSSecurityConfig):
233            raise TypeError("%s is not a sub-class of WSSecurityConfig" % \
234                            cfgClass)
235       
236        # Read parameters from config file if set
237        if isinstance(cfg, basestring):
238            log.debug("BaseSignatureHandler.__init__: config file path input ...")
239            self.cfg = cfgClass()
240            self.cfg.read(cfg)
241        else:
242            log.debug("BaseSignatureHandler.__init__: config object input ...")
243            self.cfg = cfgClass(cfg=cfg)
244           
245        if cfg: # config object or config file path was set
246            log.debug("BaseSignatureHandler.__init__: Processing config file...")
247            self.cfg.parse(section=cfgFileSection)
248
249        # Also update config from keywords set
250        log.debug("BaseSignatureHandler.__init__: setting config from keywords...")
251        self.cfg.update(kw)
252       
253        # set default value type, if none specified in config file
254        if not self.cfg['reqBinSecTokValType']:
255            self.cfg['reqBinSecTokValType'] = "X509v3"
256           
257        self.reqBinSecTokValType = self.cfg['reqBinSecTokValType']
258
259        # Set keywords for canonicalization of SignedInfo and reference
260        # elements
261        self.refC14nKw = \
262            {'inclusive_namespaces': self.cfg['refC14nInclNS']}
263
264        self.signedInfoC14nKw = \
265            {'inclusive_namespaces': self.cfg['signedInfoC14nInclNS']}
266
267        self.verifyingCert = self.cfg['verifyingCert']
268        self.verifyingCertFilePath = self.cfg['verifyingCertFilePath']
269       
270        self.signingCert = self.cfg['signingCert']
271        self.signingCertFilePath = self.cfg['signingCertFilePath']
272
273        self.signingCertChain = self.cfg['signingCertChain']
274             
275        # MUST be set before _setSigningPriKeyFilePath / _setSigningPriKey
276        # are called
277        self.signingPriKeyPwd = self.cfg['signingPriKeyPwd']
278       
279        if self.cfg.get('signingPriKey'):
280            # Don't allow None for private key setting
281            self.signingPriKey = self.cfg['signingPriKey']
282           
283        self.signingPriKeyFilePath = self.cfg['signingPriKeyFilePath']
284       
285        # CA certificate(s) for verification of X.509 certificate used with
286        # signature.
287        if self.cfg.get('caCertDirPath'):
288            self.caCertDirPath = self.cfg['caCertDirPath']
289           
290        elif self.cfg.get('caCertFilePathList'):
291            self.caCertFilePathList = self.cfg['caCertFilePathList']
292           
293        self.addTimestamp = self.cfg['addTimestamp']
294       
295        # set default value, if none specified in config file
296        if not self.cfg['applySignatureConfirmation']:
297            self.cfg['applySignatureConfirmation'] = False
298
299        self.applySignatureConfirmation=self.cfg['applySignatureConfirmation']
300        self.b64EncSignatureValue = None
301       
302        log.debug("WSSE Config = %s" % self.cfg)
303
304               
305    #_________________________________________________________________________
306    def _setReqBinSecTokValType(self, value):
307        """Set ValueType attribute for BinarySecurityToken used in a request
308         
309        @type value: string
310        @param value: name space for BinarySecurityToken ValueType check
311        'binSecTokValType' class variable for supported types.  Input can be
312        shortened to binSecTokValType keyword if desired.
313        """
314        log.debug("Setting reqBinSecTokValType - to %s" %value)
315        if value in self.__class__.binSecTokValType:
316            self._reqBinSecTokValType = self.__class__.binSecTokValType[value]
317 
318        elif value in self.__class__.binSecTokValType.values():
319            self._reqBinSecTokValType = value
320        else:
321            raise WSSecurityError, \
322                'Request BinarySecurityToken ValueType "%s" not recognised' %\
323                value
324           
325    #_________________________________________________________________________
326    def _getReqBinSecTokValType(self):
327        """
328        Get ValueType attribute for BinarySecurityToken used in a request
329        """
330        log.debug("Getting reqBinSecTokValType value")
331        if hasattr(self, '_reqBinSecTokValType'):
332            return self._reqBinSecTokValType
333        else:
334            return ""
335       
336    reqBinSecTokValType = property(fset=_setReqBinSecTokValType,
337                                   fget=_getReqBinSecTokValType,
338         doc="ValueType attribute for BinarySecurityToken used in request")
339       
340
341    #_________________________________________________________________________
342    def __checkC14nKw(self, kw):
343        """Check keywords for canonicalization in signing process - generic
344        method for setting keywords for reference element and SignedInfo
345        element c14n
346       
347        @type kw: dict
348        @param kw: keyword used with ZSI.wstools.Utility.Canonicalization"""
349       
350        # Check for dict/None - Set to None in order to use inclusive
351        # canonicalization
352        if kw is not None and not isinstance(kw, dict):
353            # Otherwise keywords must be a dictionary
354            raise AttributeError, \
355                "Expecting dictionary type for reference c14n keywords"
356               
357        elif kw.get('inclusive_namespaces') and \
358             not isinstance(kw['inclusive_namespaces'], list) and \
359             not isinstance(kw['inclusive_namespaces'], tuple):
360            raise AttributeError, \
361                'Expecting list or tuple of prefix names for "%s" keyword' % \
362                'inclusive_namespaces'
363       
364               
365    #_________________________________________________________________________
366    def _setRefC14nKw(self, kw):
367        """Set keywords for canonicalization of reference elements in the
368        signing process"""
369        self.__checkC14nKw(kw)                   
370        self._refC14nKw = kw
371       
372    def _getRefC14nKw(self):
373        if hasattr(self, '_refC14nKw'):
374            return self._refC14nKw
375        else:
376            return {}
377       
378    refC14nKw = property(fset=_setRefC14nKw,
379                         fget=_getRefC14nKw,
380                         doc="Keywords for c14n of reference elements")
381       
382               
383    #_________________________________________________________________________
384    def _setSignedInfoC14nKw(self, kw):
385        """Set keywords for canonicalization of SignedInfo element in the
386        signing process"""
387        self.__checkC14nKw(kw)                   
388        self._signedInfoC14nKw = kw
389       
390    def _getSignedInfoC14nKw(self):
391        if hasattr(self, '_signedInfoC14nKw'):
392            return self._signedInfoC14nKw
393        else:
394            return {}
395       
396    signedInfoC14nKw = property(fset=_setSignedInfoC14nKw,
397                                fget=_getSignedInfoC14nKw,
398                                doc="Keywords for c14n of SignedInfo element")
399
400
401    #_________________________________________________________________________
402    def __refC14nIsExcl(self):
403        return isinstance(self._refC14nKw, dict) and \
404               isinstance(self._refC14nKw.get('inclusive_namespaces'), list)
405               
406    refC14nIsExcl = property(fget=__refC14nIsExcl,
407    doc="Return True/False c14n for reference elements set to exclusive type")
408     
409
410    #_________________________________________________________________________
411    def __signedInfoC14nIsExcl(self):
412        return isinstance(self._signedInfoC14nKw, dict) and \
413        isinstance(self._signedInfoC14nKw.get('inclusive_namespaces'), list)
414               
415    signedInfoC14nIsExcl = property(fget=__signedInfoC14nIsExcl,
416    doc="Return True/False c14n for SignedInfo element set to exclusive type")
417   
418   
419    #_________________________________________________________________________
420    def __setCert(self, cert):
421        """filter and convert input cert to signing verifying cert set
422        property methods.  For signingCert, set to None if it is not to be
423        included in the SOAP header.  For verifyingCert, set to None if this
424        cert can be expected to be retrieved from the SOAP header of the
425        message to be verified
426       
427        @type: ndg.security.common.X509.X509Cert / M2Crypto.X509.X509 /
428        string or None
429        @param cert: X.509 certificate. 
430       
431        @rtype ndg.security.common.X509.X509Cert
432        @return X.509 certificate object"""
433       
434        if not cert or isinstance(cert, X509Cert):
435            # ndg.security.common.X509.X509Cert type / None
436            return cert
437           
438        elif isinstance(cert, X509.X509):
439            # M2Crypto.X509.X509 type
440            return X509Cert(m2CryptoX509=cert)
441           
442        elif isinstance(cert, basestring):
443            return X509CertParse(cert)
444       
445        else:
446            raise AttributeError, "X.509 Cert. must be type: " + \
447                "ndg.security.common.X509.X509Cert, M2Crypto.X509.X509 or " +\
448                "a base64 encoded string"
449
450   
451    #_________________________________________________________________________
452    def _getVerifyingCert(self):
453        '''Return X.509 cert object corresponding to cert used to verify the
454        signature in the last call to verify
455       
456         * Cert will correspond to one used in the LATEST call to verify, on
457         the next call it will be replaced
458         * if verify hasn't been called, the cert will be None
459       
460        @rtype: M2Crypto.X509.X509
461        @return: certificate object
462        '''
463        log.debug("Getting verifying cert")
464        return self._verifyingCert
465
466
467    #_________________________________________________________________________
468    def _setVerifyingCert(self, verifyingCert):
469        "Set property method for X.509 cert. used to verify a signature"
470        log.debug("Setting verifying cert")
471        print "Cert:", verifyingCert
472        self._verifyingCert = self.__setCert(verifyingCert)
473        # Reset file path as it may no longer apply
474        self._verifyingCertFilePath = None
475       
476    verifyingCert = property(fset=_setVerifyingCert,
477                             fget=_getVerifyingCert,
478                             doc="Set X.509 Cert. for verifying signature")
479
480
481    #_________________________________________________________________________
482    def _setVerifyingCertFilePath(self, verifyingCertFilePath):
483        "Set method for Service X.509 cert. file path property"
484        if verifyingCertFilePath:
485            if isinstance(verifyingCertFilePath, basestring):
486                self._verifyingCert = X509CertRead(verifyingCertFilePath)
487            else:
488                raise AttributeError, "X.509 Cert file path is not a valid string"
489       
490        self._verifyingCertFilePath = verifyingCertFilePath
491       
492    verifyingCertFilePath = property(fset=_setVerifyingCertFilePath,
493                    doc="file path of X.509 Cert. for verifying signature")
494
495   
496    #_________________________________________________________________________
497    def _getSigningCert(self):
498        '''Return X.509 cert object corresponding to cert used with
499        signature
500       
501        @rtype: M2Crypto.X509.X509
502        @return: certificate object
503        '''
504        return self._signingCert
505
506
507    #_________________________________________________________________________
508    def _setSigningCert(self, signingCert):
509        "Set property method for X.509 cert. to be included with signature"
510        self._signingCert = self.__setCert(signingCert)
511   
512        # Reset file path as it may no longer apply
513        self._signingCertFilePath = None
514       
515    signingCert = property(fget=_getSigningCert,
516                           fset=_setSigningCert,
517                           doc="X.509 Cert. to include signature")
518
519 
520    #_________________________________________________________________________
521    def _setSigningCertFilePath(self, signingCertFilePath):
522        "Set signature X.509 cert property method"
523       
524        if isinstance(signingCertFilePath, basestring):
525            self._signingCert = X509CertRead(signingCertFilePath)
526           
527        elif signingCertFilePath is not None:
528            raise AttributeError, \
529                "Signature X.509 cert. file path must be a valid string"
530       
531        self._signingCertFilePath = signingCertFilePath
532       
533       
534    signingCertFilePath = property(fset=_setSigningCertFilePath,
535                   doc="File path X.509 cert. to include with signed message")
536
537   
538    #_________________________________________________________________________
539    def _setSigningCertChain(self, signingCertChain):
540        '''Signature set-up with "X509PKIPathv1" BinarySecurityToken
541        ValueType.  Use an X.509 Stack to store certificates that make up a
542        chain of trust to certificate used to verify a signature
543       
544        @type signingCertChain: list or tuple of M2Crypto.X509.X509Cert or
545        ndg.security.common.X509.X509Cert types.
546        @param signingCertChain: list of certificate objects making up the
547        chain of trust.  The last certificate is the one associated with the
548        private key used to sign the message.'''
549       
550        if not isinstance(signingCertChain, list) and \
551           not isinstance(signingCertChain, tuple):
552            log.warning('Expecting a list or tuple for "signingCertChain" - ignoring value set, "%s"' %signingCertChain)
553            self._signingCertChain = None
554            return
555       
556        self._signingCertChain = X509Stack()
557           
558        for cert in signingCertChain:
559            self._signingCertChain.push(cert)
560           
561    def _getSigningCertChain(self):
562        return self._signingCertChain
563   
564    signingCertChain = property(fset=_setSigningCertChain,
565                                fget=_getSigningCertChain,
566               doc="Cert.s in chain of trust to cert. used to verify msg.")
567
568 
569    #_________________________________________________________________________
570    def _setSigningPriKeyPwd(self, signingPriKeyPwd):
571        "Set method for private key file password used to sign message"
572        if signingPriKeyPwd is not None and \
573           not isinstance(signingPriKeyPwd, basestring):
574            raise AttributeError, \
575                "Signing private key password must be None or a valid string"
576       
577        self._signingPriKeyPwd = signingPriKeyPwd
578
579    def _getSigningPriKeyPwd(self):
580        if hasattr(self, '_signingPriKeyPwd'):
581            return self._signingPriKeyPwd
582        else:
583            return ""
584       
585    signingPriKeyPwd = property(fset=_setSigningPriKeyPwd,
586                                fget=_getSigningPriKeyPwd,
587             doc="Password protecting private key file used to sign message")
588
589 
590    #_________________________________________________________________________
591    def _setSigningPriKey(self, signingPriKey):
592        """Set method for client private key
593       
594        Nb. if input is a string, signingPriKeyPwd will need to be set if
595        the key is password protected.
596       
597        @type signingPriKey: M2Crypto.RSA.RSA / string
598        @param signingPriKey: private key used to sign message"""
599       
600        if isinstance(signingPriKey, basestring):
601            pwdCallback = lambda *ar, **kw: self._signingPriKeyPwd
602            self._signingPriKey = RSA.load_key_string(signingPriKey,
603                                                       callback=pwdCallback)
604
605        elif isinstance(signingPriKey, RSA.RSA):
606            self._signingPriKey = signingPriKey
607                   
608        else:
609            raise AttributeError, "Signing private key must be a valid " + \
610                                  "M2Crypto.RSA.RSA type or a string"
611               
612    def _getSigningPriKey(self):
613        return self._signingPriKey
614
615    signingPriKey = property(fset=_setSigningPriKey,
616                             fget=_getSigningPriKey,
617                             doc="Private key used to sign outbound message")
618
619 
620    #_________________________________________________________________________
621    def _setSigningPriKeyFilePath(self, signingPriKeyFilePath):
622        """Set method for client private key file path
623       
624        signingPriKeyPwd MUST be set prior to a call to this method"""
625        if isinstance(signingPriKeyFilePath, basestring):                           
626            try:
627                # Read Private key to sign with   
628                priKeyFile = BIO.File(open(signingPriKeyFilePath)) 
629                pwdCallback = lambda *ar, **kw: self._signingPriKeyPwd                                           
630                self._signingPriKey = RSA.load_key_bio(priKeyFile, 
631                                                        callback=pwdCallback)           
632            except Exception, e:
633                raise AttributeError, \
634                                "Setting private key for signature: %s" % e
635       
636        elif signingPriKeyFilePath is not None:
637            raise AttributeError, \
638                        "Private key file path must be a valid string or None"
639       
640        self.__signingPriKeyFilePath = signingPriKeyFilePath
641       
642    signingPriKeyFilePath = property(fset=_setSigningPriKeyFilePath,
643                      doc="File path for private key used to sign message")
644
645    def __caCertIsSet(self):
646        '''Check for CA certificate set (X.509 Stack has been created)'''
647        return hasattr(self, '_caX509Stack')
648   
649    caCertIsSet = property(fget=__caCertIsSet,
650           doc='Check for CA certificate set (X.509 Stack has been created)')
651   
652    #_________________________________________________________________________
653    def __appendCAX509Stack(self, caCertList):
654        '''Store CA certificates in an X.509 Stack
655       
656        @param caCertList: list or tuple
657        @type caCertList: M2Crypto.X509.X509 certificate objects'''
658       
659        if not self.caCertIsSet:
660            self._caX509Stack = X509Stack()
661           
662        for cert in caCertList:
663            self._caX509Stack.push(cert)
664
665
666    #_________________________________________________________________________
667    def __setCAX509StackFromDir(self, caCertDir):
668        '''Read CA certificates from directory and add them to the X.509
669        stack
670       
671        @param caCertDir: string
672        @type caCertDir: directory from which to read CA certificate files'''
673       
674        # Mimic OpenSSL -CApath option which expects directory of CA files
675        # of form <Hash cert subject name>.0
676        reg = re.compile('\d+\.0')
677        try:
678            caCertList = [X509CertRead(caFile) \
679                          for caFile in os.listdir(caCertDir) \
680                          if reg.match(caFile)]
681        except Exception, e:
682            raise WSSecurityError, \
683                'Loading CA certificate "%s" from CA directory: %s' % \
684                                                        (caFile, str(e))
685                   
686        # Add to stack
687        self.__appendCAX509Stack(caCertList)
688       
689    caCertDirPath = property(fset=__setCAX509StackFromDir,
690                      doc="Dir. containing CA cert.s used for verification")
691
692
693    #_________________________________________________________________________
694    def __setCAX509StackFromCertFileList(self, caCertFilePathList):
695        '''Read CA certificates from file and add them to the X.509
696        stack
697       
698        @type caCertFilePathList: list or tuple
699        @param caCertFilePathList: list of file paths for CA certificates to
700        be used to verify certificate used to sign message'''
701       
702        if not isinstance(caCertFilePathList, list) and \
703           not isinstance(caCertFilePathList, tuple):
704            raise WSSecurityError, \
705                        'Expecting a list or tuple for "caCertFilePathList"'
706
707        # Mimic OpenSSL -CApath option which expects directory of CA files
708        # of form <Hash cert subject name>.0
709        try:
710            caCertList = [X509CertRead(caFile) \
711                          for caFile in caCertFilePathList]
712        except Exception, e:
713            raise WSSecurityError, \
714                    'Loading CA certificate "%s" from file list: %s' % \
715                                                        (caFile, str(e))
716                   
717        # Add to stack
718        self.__appendCAX509Stack(caCertList)
719       
720    caCertFilePathList = property(fset=__setCAX509StackFromCertFileList,
721                      doc="List of CA cert. files used for verification")
722               
Note: See TracBrowser for help on using the repository browser.