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

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

Refactoring of CredWallet?

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