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

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

#884: added addtional validity checks to AttributeAuthority?.getAttCert and wssecurity.BaseSignatureHandler?

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__ = "Philip.Kershaw@stfc.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', cfgFilePrefix='',
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                      "...")
244            self.cfg = cfgClass()
245            self.cfg.read(cfg)
246        else:
247            log.debug("BaseSignatureHandler.__init__: config object input ...")
248            self.cfg = cfgClass(cfg=cfg)
249           
250        if cfg: # config object or config file path was set
251            log.debug("BaseSignatureHandler.__init__: Processing config "
252                      "file...")
253            self.cfg.parse(section=cfgFileSection, prefix=cfgFilePrefix)
254
255        # Also update config from keywords set
256        log.debug("BaseSignatureHandler.__init__: setting config from "
257                  "keywords...")
258       
259        # Filter keywords if a prefix is set removing any that don't start with
260        # the prefix given
261#        if cfgFilePrefix:
262#            pfxWithDot = cfgFilePrefix+'.'
263#            kw = dict([(k.replace(pfxWithDot, ''), v) for k, v in kw.items()
264#                       if k.startswith(pfxWithDot)])
265#                   
266        self.cfg.update(kw, prefix=cfgFilePrefix)
267       
268        # set default value type, if none specified in config file
269        if not self.cfg['reqBinSecTokValType']:
270            self.cfg['reqBinSecTokValType'] = "X509v3"
271           
272        self.reqBinSecTokValType = self.cfg['reqBinSecTokValType']
273
274        # Set keywords for canonicalization of SignedInfo and reference
275        # elements
276        self.refC14nKw = {'inclusive_namespaces': self.cfg['refC14nInclNS']}
277
278        self.signedInfoC14nKw = \
279            {'inclusive_namespaces': self.cfg['signedInfoC14nInclNS']}
280
281        self.verifyingCert = self.cfg['verifyingCert']
282        self.verifyingCertFilePath = self.cfg['verifyingCertFilePath']
283       
284        self.signingCert = self.cfg['signingCert']
285        self.signingCertFilePath = self.cfg['signingCertFilePath']
286
287        self.signingCertChain = self.cfg['signingCertChain']
288             
289        # MUST be set before _setSigningPriKeyFilePath / _setSigningPriKey
290        # are called
291        self.signingPriKeyPwd = self.cfg['signingPriKeyPwd']
292       
293        if self.cfg.get('signingPriKey'):
294            # Don't allow None for private key setting
295            self.signingPriKey = self.cfg['signingPriKey']
296           
297        self.signingPriKeyFilePath = self.cfg['signingPriKeyFilePath']
298       
299        # CA certificate(s) for verification of X.509 certificate used with
300        # signature.
301        if self.cfg.get('caCertDirPath'):
302            self.caCertDirPath = self.cfg['caCertDirPath']
303           
304        elif self.cfg.get('caCertFilePathList'):
305            self.caCertFilePathList = self.cfg['caCertFilePathList']
306       
307        self.addTimestamp = self.cfg['addTimestamp']
308       
309        # set default value, if none specified in config file
310        if not self.cfg['applySignatureConfirmation']:
311            self.cfg['applySignatureConfirmation'] = False
312
313        self.applySignatureConfirmation=self.cfg['applySignatureConfirmation']
314        self.b64EncSignatureValue = None
315       
316        log.debug("WSSE Config = %s" % self.cfg)
317
318               
319    def _setReqBinSecTokValType(self, value):
320        """Set ValueType attribute for BinarySecurityToken used in a request
321         
322        @type value: string
323        @param value: name space for BinarySecurityToken ValueType check
324        'binSecTokValType' class variable for supported types.  Input can be
325        shortened to binSecTokValType keyword if desired.
326        """
327        log.debug("Setting reqBinSecTokValType - to %s" %value)
328        if value in self.__class__.binSecTokValType:
329            self._reqBinSecTokValType = self.__class__.binSecTokValType[value]
330 
331        elif value in self.__class__.binSecTokValType.values():
332            self._reqBinSecTokValType = value
333        else:
334            raise WSSecurityError('Request BinarySecurityToken ValueType '
335                                  '"%s" not recognised' % value)
336           
337    def _getReqBinSecTokValType(self):
338        """
339        Get ValueType attribute for BinarySecurityToken used in a request
340        """
341        log.debug("Getting reqBinSecTokValType value")
342        if hasattr(self, '_reqBinSecTokValType'):
343            return self._reqBinSecTokValType
344        else:
345            return ""
346       
347    reqBinSecTokValType = property(fset=_setReqBinSecTokValType,
348                                   fget=_getReqBinSecTokValType,
349         doc="ValueType attribute for BinarySecurityToken used in request")
350       
351
352    def __checkC14nKw(self, kw):
353        """Check keywords for canonicalization in signing process - generic
354        method for setting keywords for reference element and SignedInfo
355        element c14n
356       
357        @type kw: dict
358        @param kw: keyword used with ZSI.wstools.Utility.Canonicalization"""
359       
360        # Check for dict/None - Set to None in order to use inclusive
361        # canonicalization
362        if kw is not None and not isinstance(kw, dict):
363            # Otherwise keywords must be a dictionary
364            raise AttributeError, \
365                "Expecting dictionary type for reference c14n keywords"
366               
367        elif kw.get('inclusive_namespaces') and \
368             not isinstance(kw['inclusive_namespaces'], list) and \
369             not isinstance(kw['inclusive_namespaces'], tuple):
370            raise AttributeError('Expecting list or tuple of prefix names for '
371                                 '"%s" keyword' % 'inclusive_namespaces')
372       
373               
374    def _setRefC14nKw(self, kw):
375        """Set keywords for canonicalization of reference elements in the
376        signing process"""
377        self.__checkC14nKw(kw)                   
378        self._refC14nKw = kw
379       
380    def _getRefC14nKw(self):
381        if hasattr(self, '_refC14nKw'):
382            return self._refC14nKw
383        else:
384            return {}
385       
386    refC14nKw = property(fset=_setRefC14nKw,
387                         fget=_getRefC14nKw,
388                         doc="Keywords for c14n of reference elements")
389       
390               
391    def _setSignedInfoC14nKw(self, kw):
392        """Set keywords for canonicalization of SignedInfo element in the
393        signing process"""
394        self.__checkC14nKw(kw)                   
395        self._signedInfoC14nKw = kw
396       
397    def _getSignedInfoC14nKw(self):
398        if hasattr(self, '_signedInfoC14nKw'):
399            return self._signedInfoC14nKw
400        else:
401            return {}
402       
403    signedInfoC14nKw = property(fset=_setSignedInfoC14nKw,
404                                fget=_getSignedInfoC14nKw,
405                                doc="Keywords for c14n of SignedInfo element")
406
407
408    def __refC14nIsExcl(self):
409        return isinstance(self._refC14nKw, dict) and \
410               isinstance(self._refC14nKw.get('inclusive_namespaces'), list) and \
411               len(self._refC14nKw['inclusive_namespaces']) > 0
412               
413    refC14nIsExcl = property(fget=__refC14nIsExcl,
414    doc="Return True/False c14n for reference elements set to exclusive type")
415
416     
417    def __signedInfoC14nIsExcl(self):
418        return isinstance(self._signedInfoC14nKw, dict) and \
419        isinstance(self._signedInfoC14nKw.get('inclusive_namespaces'), list) and \
420        len(self._signedInfoC14nKw['inclusive_namespaces']) > 0
421       
422    signedInfoC14nIsExcl = property(fget=__signedInfoC14nIsExcl,
423                                    doc="Return True/False c14n for "
424                                    "SignedInfo element set to exclusive type")
425   
426   
427    def __setCert(self, cert):
428        """filter and convert input cert to signing verifying cert set
429        property methods.  For signingCert, set to None if it is not to be
430        included in the SOAP header.  For verifyingCert, set to None if this
431        cert can be expected to be retrieved from the SOAP header of the
432        message to be verified
433       
434        @type: ndg.security.common.X509.X509Cert / M2Crypto.X509.X509 /
435        string or None
436        @param cert: X.509 certificate. 
437       
438        @rtype ndg.security.common.X509.X509Cert
439        @return X.509 certificate object"""
440       
441        if not cert or isinstance(cert, X509Cert):
442            # ndg.security.common.X509.X509Cert type / None
443            x509Cert = cert
444           
445        elif isinstance(cert, X509.X509):
446            # M2Crypto.X509.X509 type
447            x509Cert = X509Cert(m2CryptoX509=cert)
448           
449        elif isinstance(cert, basestring):
450            x509Cert = X509CertParse(cert)
451       
452        else:
453            raise AttributeError("X.509 Cert. must be type: ndg.security."
454                                 "common.X509.X509Cert, M2Crypto.X509.X509 or "
455                                 "a base64 encoded string")
456       
457        # Check for expired certificate
458        if x509Cert:   
459            x509Cert.isValidTime(raiseExcep=True)
460
461   
462    def _getVerifyingCert(self):
463        '''Return X.509 cert object corresponding to cert used to verify the
464        signature in the last call to verify
465       
466         * Cert will correspond to one used in the LATEST call to verify, on
467         the next call it will be replaced
468         * if verify hasn't been called, the cert will be None
469       
470        @rtype: M2Crypto.X509.X509
471        @return: certificate object
472        '''
473        log.debug("Getting verifying cert")
474        return self._verifyingCert
475
476
477    def _setVerifyingCert(self, verifyingCert):
478        "Set property method for X.509 cert. used to verify a signature"
479        log.debug("Setting verifying cert")
480        self._verifyingCert = self.__setCert(verifyingCert)
481        # Reset file path as it may no longer apply
482        self._verifyingCertFilePath = None
483       
484    verifyingCert = property(fset=_setVerifyingCert,
485                             fget=_getVerifyingCert,
486                             doc="Set X.509 Cert. for verifying signature")
487
488
489    def _setVerifyingCertFilePath(self, verifyingCertFilePath):
490        "Set method for Service X.509 cert. file path property"
491        if verifyingCertFilePath:
492            if isinstance(verifyingCertFilePath, basestring):
493                self._verifyingCert = X509CertRead(verifyingCertFilePath)
494            else:
495                raise AttributeError, "X.509 Cert file path is not a valid string"
496       
497        self._verifyingCertFilePath = verifyingCertFilePath
498       
499    verifyingCertFilePath = property(fset=_setVerifyingCertFilePath,
500                    doc="file path of X.509 Cert. for verifying signature")
501
502   
503    def _getSigningCert(self):
504        '''Return X.509 cert object corresponding to cert used with
505        signature
506       
507        @rtype: M2Crypto.X509.X509
508        @return: certificate object
509        '''
510        return self._signingCert
511
512
513    def _setSigningCert(self, signingCert):
514        "Set property method for X.509 cert. to be included with signature"
515        self._signingCert = self.__setCert(signingCert)
516   
517        # Reset file path as it may no longer apply
518        self._signingCertFilePath = None
519       
520    signingCert = property(fget=_getSigningCert,
521                           fset=_setSigningCert,
522                           doc="X.509 Cert. to include signature")
523
524 
525    def _setSigningCertFilePath(self, signingCertFilePath):
526        "Set signature X.509 cert property method"
527       
528        if isinstance(signingCertFilePath, basestring):
529            self._signingCert = X509CertRead(signingCertFilePath)
530           
531        elif signingCertFilePath is not None:
532            raise AttributeError(
533                "Signature X.509 cert. file path must be a valid string")
534       
535        self._signingCertFilePath = signingCertFilePath
536       
537       
538    signingCertFilePath = property(fset=_setSigningCertFilePath,
539                   doc="File path X.509 cert. to include with signed message")
540
541   
542    def _setSigningCertChain(self, signingCertChain):
543        '''Signature set-up with "X509PKIPathv1" BinarySecurityToken
544        ValueType.  Use an X.509 Stack to store certificates that make up a
545        chain of trust to certificate used to verify a signature
546       
547        @type signingCertChain: list or tuple of M2Crypto.X509.X509Cert or
548        ndg.security.common.X509.X509Cert types.
549        @param signingCertChain: list of certificate objects making up the
550        chain of trust.  The last certificate is the one associated with the
551        private key used to sign the message.'''
552       
553        if not isinstance(signingCertChain, (list, tuple)):
554            log.warning('Expecting a list or tuple for "signingCertChain" - '
555                        'ignoring value set, "%s"' % signingCertChain)
556            self._signingCertChain = None
557            return
558       
559        self._signingCertChain = X509Stack()
560           
561        for cert in signingCertChain:
562            self._signingCertChain.push(cert)
563           
564    def _getSigningCertChain(self):
565        return self._signingCertChain
566   
567    signingCertChain = property(fset=_setSigningCertChain,
568                                fget=_getSigningCertChain,
569                                doc="Cert.s in chain of trust to cert. used "
570                                    "to verify msg.")
571
572 
573    def _setSigningPriKeyPwd(self, signingPriKeyPwd):
574        "Set method for private key file password used to sign message"
575        if signingPriKeyPwd is not None and \
576           not isinstance(signingPriKeyPwd, basestring):
577            raise AttributeError("Signing private key password must be None "
578                                 "or a valid string")
579       
580        self._signingPriKeyPwd = signingPriKeyPwd
581
582    def _getSigningPriKeyPwd(self):
583        if hasattr(self, '_signingPriKeyPwd'):
584            return self._signingPriKeyPwd
585        else:
586            return ""
587       
588    signingPriKeyPwd = property(fset=_setSigningPriKeyPwd,
589                                fget=_getSigningPriKeyPwd,
590                                doc="Password protecting private key file "
591                                    "used to sign message")
592
593 
594    def _setSigningPriKey(self, signingPriKey):
595        """Set method for client private key
596       
597        Nb. if input is a string, signingPriKeyPwd will need to be set if
598        the key is password protected.
599       
600        @type signingPriKey: M2Crypto.RSA.RSA / string
601        @param signingPriKey: private key used to sign message"""
602       
603        if isinstance(signingPriKey, basestring):
604            pwdCallback = lambda *ar, **kw: self._signingPriKeyPwd
605            self._signingPriKey = RSA.load_key_string(signingPriKey,
606                                                       callback=pwdCallback)
607
608        elif isinstance(signingPriKey, RSA.RSA):
609            self._signingPriKey = signingPriKey
610                   
611        else:
612            raise AttributeError("Signing private key must be a valid "
613                                  "M2Crypto.RSA.RSA type or a string")
614               
615    def _getSigningPriKey(self):
616        return self._signingPriKey
617
618    signingPriKey = property(fset=_setSigningPriKey,
619                             fget=_getSigningPriKey,
620                             doc="Private key used to sign outbound message")
621
622 
623    def _setSigningPriKeyFilePath(self, signingPriKeyFilePath):
624        """Set method for client private key file path
625       
626        signingPriKeyPwd MUST be set prior to a call to this method"""
627        if isinstance(signingPriKeyFilePath, basestring):                           
628            try:
629                # Read Private key to sign with   
630                priKeyFile = BIO.File(open(signingPriKeyFilePath)) 
631                pwdCallback = lambda *ar, **kw: self._signingPriKeyPwd                                           
632                self._signingPriKey = RSA.load_key_bio(priKeyFile, 
633                                                        callback=pwdCallback)           
634            except Exception, e:
635                raise AttributeError("Setting private key for signature: %s"%e)
636       
637        elif signingPriKeyFilePath is not None:
638            raise AttributeError("Private key file path must be a valid "
639                                 "string or None")
640       
641        self.__signingPriKeyFilePath = signingPriKeyFilePath
642       
643    signingPriKeyFilePath = property(fset=_setSigningPriKeyFilePath,
644                      doc="File path for private key used to sign message")
645
646    def __caCertIsSet(self):
647        '''Check for CA certificate set (X.509 Stack has been created)'''
648        return hasattr(self, '_caX509Stack')
649   
650    caCertIsSet = property(fget=__caCertIsSet,
651           doc='Check for CA certificate set (X.509 Stack has been created)')
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 hasattr(self, '_caX509Stack'):
660            self._caX509Stack = X509Stack()
661           
662        for cert in caCertList:
663            self._caX509Stack.push(cert)
664
665
666    def __setCAX509StackFromDir(self, caCertDir):
667        '''Read CA certificates from directory and add them to the X.509
668        stack
669       
670        @param caCertDir: string
671        @type caCertDir: directory from which to read CA certificate files'''
672       
673        # Mimic OpenSSL -CApath option which expects directory of CA files
674        # of form <Hash cert subject name>.0
675        reg = re.compile('\d+\.0')
676        try:
677            caCertList = [X509CertRead(caFile) \
678                          for caFile in os.listdir(caCertDir) \
679                          if reg.match(caFile)]
680        except Exception, e:
681            raise WSSecurityError('Loading CA certificate "%s" from CA '
682                                  'directory: %s' % (caFile, str(e)))
683                   
684        # Add to stack
685        self.__appendCAX509Stack(caCertList)
686       
687    caCertDirPath = property(fset=__setCAX509StackFromDir,
688                             doc="Dir. containing CA cert.s used for "
689                                "verification")
690
691
692    def __setCAX509StackFromCertFileList(self, caCertFilePathList):
693        '''Read CA certificates from file and add them to the X.509
694        stack
695       
696        @type caCertFilePathList: list or tuple
697        @param caCertFilePathList: list of file paths for CA certificates to
698        be used to verify certificate used to sign message'''
699       
700        if not isinstance(caCertFilePathList, (list, tuple)):
701            raise WSSecurityError('Expecting a list or tuple for '
702                                  '"caCertFilePathList"')
703
704        # Mimic OpenSSL -CApath option which expects directory of CA files
705        # of form <Hash cert subject name>.0
706        try:
707            caCertList=[X509CertRead(caFile) for caFile in caCertFilePathList]
708        except Exception, e:
709            raise WSSecurityError('Loading CA certificate "%s" from file '
710                                  'list: %s' % (caFile, str(e)))
711                   
712        # Add to stack
713        self.__appendCAX509Stack(caCertList)
714       
715    caCertFilePathList = property(fset=__setCAX509StackFromCertFileList,
716                      doc="List of CA cert. files used for verification")
717               
Note: See TracBrowser for help on using the repository browser.