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

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

Fix problem with search and replace licence not adding a new line.

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