Changeset 5357


Ignore:
Timestamp:
05/06/09 12:36:59 (10 years ago)
Author:
pjkersha
Message:
  • fix to WS-Security signature handler 4Suite implementation (ndg.security.common.wssecurity.signaturehandler.foursuite) to ensure timestamp is checked correctly
  • refactored ndg.security.common.wssecurity moving encryption handler development code into its own ndg.security.common.wssecurity.encryptionhandler package
  • Fixed copyright on some remaining files that still had NERC/CCLRC
  • further work on SSL CLient AuthN WSGI unit tests ndg.security.test.unit.wsgi.ssl
Location:
TI12-security/trunk/python
Files:
3 added
26 edited

Legend:

Unmodified
Added
Removed
  • TI12-security/trunk/python/Makefile

    r4750 r5357  
    66# Make all eggs 
    77# 
    8 # @copyright: (C) 2007 CCLRC & NERC 
     8# @copyright: (C) 2007 STFC 
    99# 
    1010# @license: This software may be distributed under the terms of the Q Public 
  • TI12-security/trunk/python/MyProxyClient/documentation/Makefile

    r4654 r5357  
    77# 
    88 
    9 # @copyright: (C) 2008 CCLRC & NERC 
     9# @copyright: (C) 2008 STFC 
    1010# 
    1111# @license: This software may be distributed under the terms of the Q Public 
  • TI12-security/trunk/python/Tests/etreewss/client/Makefile

    r4024 r5357  
    77# server side code 
    88# 
    9 # @copyright (C) 2007 CCLRC & NERC 
     9# @copyright (C) 2007 STFC 
    1010# 
    1111# @license This software may be distributed under the terms of the Q Public 
  • TI12-security/trunk/python/Tests/etreewss/server/Makefile

    r4024 r5357  
    77# server side code 
    88# 
    9 # @copyright (C) 2007 CCLRC & NERC 
     9# @copyright (C) 2007 STFC 
    1010# 
    1111# @license This software may be distributed under the terms of the Q Public  
  • TI12-security/trunk/python/ndg.security.common/ndg/security/common/authz/msi.py

    r5355 r5357  
    554554         
    555555        # Iterate through matching targets checking for user access 
    556         knownAttributeAuthorityURIs = [] 
    557556        request.subject[Subject.ROLES_NS] = [] 
    558557        permitForAllTargets = [Response.DECISION_PERMIT]*numMatchingTargets 
     
    565564            # Make call to the Policy Information Point to pull user 
    566565            # attributes applicable to this resource 
    567             if matchingTarget.attributeAuthorityURI not in \ 
    568                knownAttributeAuthorityURIs: 
     566            attributeQuery = PIPAttributeQuery() 
     567            attributeQuery[PIPAttributeQuery.SUBJECT_NS] = request.subject 
     568             
     569            attributeQuery[PIPAttributeQuery.ATTRIBUTEAUTHORITY_NS] = \ 
     570                                    matchingTarget.attributeAuthorityURI 
     571             
     572            # Exit from function returning indeterminate status if a  
     573            # problem occurs here 
     574            try: 
     575                attributeResponse=self.pip.attributeQuery(attributeQuery) 
    569576                 
    570                 attributeQuery = PIPAttributeQuery() 
    571                 attributeQuery[PIPAttributeQuery.SUBJECT_NS] = request.subject 
     577            except SubjectRetrievalError, e: 
     578                # i.e. a defined exception within the scope of this 
     579                # module 
     580                log.exception(e) 
     581                return Response(Response.DECISION_INDETERMINATE, 
     582                                message=str(e)) 
    572583                 
    573                 attributeQuery[PIPAttributeQuery.ATTRIBUTEAUTHORITY_NS] = \ 
    574                                         matchingTarget.attributeAuthorityURI 
    575                  
    576                 # Exit from function returning indeterminate status if a  
    577                 # problem occurs here 
    578                 try: 
    579                     attributeResponse=self.pip.attributeQuery(attributeQuery) 
    580                      
    581                 except SubjectRetrievalError, e: 
    582                     # i.e. a defined exception within the scope of this 
    583                     # module 
    584                     log.exception(e) 
    585                     return Response(Response.DECISION_INDETERMINATE, 
    586                                     message=str(e)) 
    587                      
    588                 except Exception, e: 
    589                     log.exception(e) 
    590                     return Response(Response.DECISION_INDETERMINATE, 
    591                                     message="An internal error occurred") 
    592                      
    593                 knownAttributeAuthorityURIs.append( 
    594                                         matchingTarget.attributeAuthorityURI) 
    595                  
    596                 # Accumulate attributes retrieved from multiple attribute 
    597                 # authorities 
    598                 request.subject[Subject.ROLES_NS] += attributeResponse[ 
    599                                                             Subject.ROLES_NS] 
     584            except Exception, e: 
     585                log.exception(e) 
     586                return Response(Response.DECISION_INDETERMINATE, 
     587                                message="An internal error occurred") 
     588                             
     589            # Accumulate attributes retrieved from multiple attribute 
     590            # authorities 
     591            request.subject[Subject.ROLES_NS] += attributeResponse[ 
     592                                                        Subject.ROLES_NS] 
    600593                
    601594            # Match the subject's attributes against the target 
  • TI12-security/trunk/python/ndg.security.common/ndg/security/common/wssecurity/__init__.py

    r5067 r5357  
    2323    CaseSensitiveConfigParser 
    2424 
    25 class WSSecurityConfigError(Exception): 
     25class WSSecurityError(Exception): 
     26    """For WS-Security generic exceptions not covered by other exception 
     27    classes in this module""" 
     28    def __init__(self, errorMessage): 
     29        log.warning(errorMessage) 
     30        super(WSSecurityError, self).__init__(errorMessage) 
     31         
     32class WSSecurityConfigError(WSSecurityError): 
    2633    """Configuration error with WS-Security setting or settings""" 
    2734     
     
    4653             caCertFilePathList=[], 
    4754             addTimestamp=True, 
     55             timestampMustBeSet=False, 
     56             createdElemMustBeSet=True, 
     57             expiresElemMustBeSet=True, 
    4858             applySignatureConfirmation=False, 
    4959             refC14nInclNS=[], 
  • TI12-security/trunk/python/ndg.security.common/ndg/security/common/wssecurity/signaturehandler/__init__.py

    r5280 r5357  
    2626 
    2727# Enable settings from a config file 
    28 from ndg.security.common.wssecurity import WSSecurityConfig 
     28from ndg.security.common.wssecurity import WSSecurityConfig, WSSecurityError 
    2929 
    3030from ndg.security.common.X509 import X509Cert, X509CertParse, X509CertRead, \ 
     
    3535log = logging.getLogger(__name__) 
    3636 
    37  
    38 class _ENCRYPTION(ENCRYPTION): 
    39     '''Derived from ENCRYPTION class to add in extra 'tripledes-cbc' - is this 
    40     any different to 'des-cbc'?  ENCRYPTION class implies that it is the same 
    41     because it's assigned to 'BLOCK_3DES' ??''' 
    42     BLOCK_TRIPLEDES = "http://www.w3.org/2001/04/xmlenc#tripledes-cbc" 
    4337 
    4438class _WSU(WSU): 
     
    5751 
    5852 
    59 class WSSecurityError(Exception): 
    60     """For WS-Security generic exceptions not covered by other exception 
    61     classes in this module""" 
    62     def __init__(self, errorMessage): 
    63         log.warning(errorMessage) 
    64         super(WSSecurityError, self).__init__(errorMessage) 
    65  
    6653class InvalidCertChain(WSSecurityError):     
    6754    """Raised from SignatureHandler.verify if the certificate submitted to 
     
    7562    """Raised from SignatureHandler._verifyTimestamp if there is a problem with 
    7663    the created or expiry times in an input message Timestamp""" 
     64 
     65class MessageExpired(TimestampError): 
     66    """Raised from SignatureHandler._verifyTimestamp if the timestamp of 
     67    the message being processed is before the current time.  Can be caught in 
     68    order to set a wsu:MessageExpired fault code""" 
    7769     
    7870class InvalidSignature(WSSecurityError): 
     
    271263            self.caCertFilePathList = self.cfg['caCertFilePathList'] 
    272264         
     265        # Configure signature generation t oadd/omit a timestamp 
    273266        self.addTimestamp = self.cfg['addTimestamp'] 
     267         
     268        # Configure timestamp checking in Signature verification handler 
     269        self.timestampMustBeSet = self.cfg['timestampMustBeSet'] 
     270        self.createdElemMustBeSet = self.cfg['createdElemMustBeSet'] 
     271        self.expiresElemMustBeSet = self.cfg['expiresElemMustBeSet'] 
    274272         
    275273        # set default value, if none specified in config file 
     
    691689                      doc="List of CA cert. files used for verification") 
    692690                 
     691 
     692    def _setBool(self, val): 
     693        """Convert input string, float or int to bool type 
     694         
     695        @type val: int, float or basestring 
     696        @param val: input value to be converted 
     697        @rtype: bool 
     698        @return: input value converted to bool type 
     699        """ 
     700         
     701        if isinstance(val, bool): 
     702            return val 
     703         
     704        elif isinstance(val, basestring): 
     705            val = val.lower() 
     706            if val not in ("true", "false"): 
     707                raise ValueError("String conversion failed for input: %r"%val) 
     708             
     709            return val == "true" 
     710             
     711        elif isinstance(val, (int, float)):  
     712            return bool(val) 
     713        else: 
     714            raise TypeError("Invalid type for bool conversion: %r" %  
     715                            val.__class__) 
     716         
     717    def _get_timestampMustBeSet(self): 
     718        return getattr(self, "_timestampMustBeSet", False) 
     719 
     720    def _set_timestampMustBeSet(self, val): 
     721        self._timestampMustBeSet = self._setBool(val) 
     722         
     723    timestampMustBeSet = property(fset=_set_timestampMustBeSet, 
     724                                  fget=_get_timestampMustBeSet, 
     725                                  doc="Set to True to raise an exception if a " 
     726                                      "message to be verified doesn't have a " 
     727                                      "timestamp element.  Set to False to " 
     728                                      "log a warning message and continue " 
     729                                      "processing") 
     730     
     731    def _get_createdElemMustBeSet(self): 
     732        return getattr(self, "_createdElemMustBeSet", False) 
     733 
     734    def _set_createdElemMustBeSet(self, val): 
     735        self._createdElemMustBeSet = self._setBool(val) 
     736         
     737    createdElemMustBeSet = property(fset=_set_createdElemMustBeSet, 
     738                                    fget=_get_createdElemMustBeSet, 
     739                                    doc="Set to True to raise an exception if " 
     740                                        "a message to be verified doesn't " 
     741                                        "have <wsu:Created/> element with its " 
     742                                        "timestamp element.  Set to False to " 
     743                                        "log a warning message and continue " 
     744                                        "processing") 
     745     
     746    def _get_expiresElemMustBeSet(self): 
     747        return getattr(self, "_expiresElemMustBeSet", False) 
     748 
     749    def _set_expiresElemMustBeSet(self, val): 
     750        self._expiresElemMustBeSet = self._setBool(val) 
     751         
     752    expiresElemMustBeSet = property(fset=_set_expiresElemMustBeSet, 
     753                                    fget=_get_expiresElemMustBeSet, 
     754                                    doc="Set to True to raise an exception if " 
     755                                        "a message to be verified doesn't " 
     756                                        "have <wsu:Expires/> element with its " 
     757                                        "timestamp element.  Set to False to " 
     758                                        "log a warning message and continue " 
     759                                        "processing")                                   
     760 
     761                               
  • TI12-security/trunk/python/ndg.security.common/ndg/security/common/wssecurity/signaturehandler/dom.py

    r5063 r5357  
    11""" DOM based WS-Security digital signature handler 
    22 
    3 NERC Data Grid Project 
     3NERC DataGrid Project 
    44""" 
    55__author__ = "C Byrom" 
     
    1818import base64 
    1919 
    20 # Conditional import as this is required for the encryption 
    21 # handler 
    22 try: 
    23     # For shared key encryption 
    24     from Crypto.Cipher import AES, DES3 
    25 except: 
    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  
    3620import os 
    3721 
     
    6852import logging 
    6953log = logging.getLogger(__name__) 
    70  
    71 from ndg.security.common.wssecurity.signaturehandler import _ENCRYPTION, \ 
    72     _WSU, OASIS, BaseSignatureHandler, WSSecurityError, NoSignatureFound, \ 
    73     InvalidSignature, TimestampError, VerifyError, SignatureError 
    74  
    75 # Enable settings from a config file 
    76 from ndg.security.common.wssecurity import WSSecurityConfig 
     54from ndg.security.common.wssecurity import WSSecurityError 
     55from ndg.security.common.wssecurity.signaturehandler import _WSU, OASIS, \ 
     56    BaseSignatureHandler, NoSignatureFound, InvalidSignature, TimestampError, \ 
     57    MessageExpired, VerifyError, SignatureError 
    7758 
    7859from ndg.security.common.X509 import X509Cert, X509CertParse, X509CertRead, \ 
    7960    X509Stack, X509StackParseFromDER 
     61 
     62 
     63# Helper functions      
     64def getElements(node, nameList): 
     65    '''DOM Helper function for getting child elements from a given node''' 
     66    # Avoid sub-string matches 
     67    nameList = isinstance(nameList, basestring) and [nameList] or nameList 
     68    return [n for n in node.childNodes if str(n.localName) in nameList] 
     69 
     70 
     71def getChildNodes(node, nodeList=None): 
     72    if nodeList is None: 
     73        nodeList = [node]  
     74    return _getChildNodes(node, nodeList) 
     75            
     76def _getChildNodes(node, nodeList): 
     77 
     78    if node.attributes is not None: 
     79        nodeList += node.attributes.values()  
     80    nodeList += node.childNodes 
     81    for childNode in node.childNodes: 
     82        _getChildNodes(childNode, nodeList) 
     83    return nodeList 
     84 
    8085 
    8186class SignatureHandler(BaseSignatureHandler): 
     
    141146         
    142147 
    143     def _verifyTimeStamp(self, parsedSOAP, ctxt, timestampMustBeSet=False): 
     148    def _verifyTimeStamp(self,  
     149                         parsedSOAP,  
     150                         ctxt,  
     151                         timestampMustBeSet=False, 
     152                         createdElemMustBeSet=True, 
     153                         expiresElemMustBeSet=True): 
    144154        """Call from verify to check timestamp if found.   
    145155         
     
    150160        sender 
    151161        @type ctxt: 
    152         @param ctxt: XPath context object""" 
     162        @param ctxt: XPath context object 
     163        @type timestampMustBeSet: bool 
     164        @param timestampMustBeSet: if set to True, raise an exception if no 
     165        timestamp element is found 
     166        @type createdElemMustBeSet: bool 
     167        @param createdElemMustBeSet: if True. raise an exception if no 
     168        <wsu:Created/> element is present 
     169        @param expiresElemMustBeSet: if True. raise an exception if no 
     170        <wsu:Expires/> element is present 
     171        """ 
    153172 
    154173        # TODO: do we need to be more rigorous in terms of handling the  
     
    159178                                           contextNode=parsedSOAP.dom, 
    160179                                           context=ctxt)[0] 
    161         except: 
     180        except IndexError: 
    162181            msg = "Verifying message - No timestamp element found" 
    163182            if timestampMustBeSet: 
     
    172191        createdNode = timestampNode.getElementsByTagName("wsu:Created") 
    173192        if createdNode is None: 
    174             raise TimestampError("Verifying message - No Created timestamp " 
    175                                  "sub-element found") 
    176              
    177         # Workaround for fractions of second 
    178         try: 
    179             createdDateTime, createdSecFraction = \ 
     193            msg = ("Verifying message: no <wsu:Created/> timestamp " 
     194                   "sub-element found") 
     195            if createdElemMustBeSet: 
     196                raise TimestampError(msg) 
     197            else: 
     198                log.warning(msg) 
     199        else:     
     200            # Workaround for fractions of second 
     201            try: 
     202                createdDateTime, createdSecFraction = \ 
    180203                            createdNode[0].childNodes[0].nodeValue.split('.') 
    181             dtCreated = _strptime(createdDateTime, '%Y-%m-%dT%H:%M:%S') 
    182             createdSeconds = float("0." + createdSecFraction.replace('Z', '')) 
    183             dtCreated += timedelta(seconds=createdSeconds) 
    184                                              
    185         except ValueError, e: 
    186             raise TimestampError("Failed to parse timestamp Created element: " 
    187                                  "%s" % e) 
    188          
    189         if dtCreated >= dtNow: 
    190             raise TimestampError("Timestamp created time %s is equal to or " 
    191                                  "after the current time %s" % \ 
    192                                  (dtCreated, dtNow)) 
     204                dtCreated = _strptime(createdDateTime, '%Y-%m-%dT%H:%M:%S') 
     205                createdSeconds = float("0."+createdSecFraction.replace('Z','')) 
     206                dtCreated += timedelta(seconds=createdSeconds) 
     207                                                 
     208            except ValueError, e: 
     209                raise TimestampError("Failed to parse timestamp Created " 
     210                                     "element: %s" % e) 
     211             
     212            if dtCreated >= dtNow: 
     213                raise TimestampError("Timestamp created time %s is equal to " 
     214                                     "or after the current time %s" % 
     215                                     (dtCreated, dtNow)) 
    193216         
    194217        expiresNode = timestampNode.getElementsByTagName("wsu:Expires") 
    195218        if expiresNode is None: 
    196             log.warning("Verifying message - No Expires element found in " 
    197                         "Timestamp") 
    198             return 
    199  
    200         try: 
    201             expiresDateTime, expiresSecFraction = \ 
     219            msg = ("Verifying message: no <wsu:Expires/> element found in " 
     220                   "Timestamp") 
     221            if expiresElemMustBeSet: 
     222                raise TimestampError(msg) 
     223            else: 
     224                log.warning(msg) 
     225        else: 
     226            try: 
     227                expiresDateTime, expiresSecFraction = \ 
    202228                            expiresNode[0].childNodes[0].nodeValue.split('.') 
    203             dtExpiry = _strptime(expiresDateTime, '%Y-%m-%dT%H:%M:%S') 
    204             expirySeconds = float("0." + expiresSecFraction.replace('Z', '')) 
    205             dtExpiry += timedelta(seconds=expirySeconds) 
    206  
    207         except ValueError, e: 
    208             raise TimestampError("Failed to parse timestamp Expires element: " 
    209                                  "%s" % e) 
    210  
    211         if dtExpiry < dtNow: 
    212             raise TimestampError("Timestamp expiry time %s is before the " 
    213                                  "current time %s - i.e. the message has " 
    214                                  "expired." % (dtExpiry, dtNow)) 
     229                dtExpiry = _strptime(expiresDateTime, '%Y-%m-%dT%H:%M:%S') 
     230                expirySeconds = float("0."+expiresSecFraction.replace('Z', '')) 
     231                dtExpiry += timedelta(seconds=expirySeconds) 
     232     
     233            except ValueError, e: 
     234                raise TimestampError("Failed to parse timestamp Expires " 
     235                                     "element: %s" % e) 
     236     
     237            if dtExpiry < dtNow: 
     238                raise MessageExpired("Message has expired: timestamp expiry " 
     239                                     "time %s is before the current time %s." % 
     240                                     (dtExpiry, dtNow)) 
    215241             
    216242                    
     
    723749                                  caX509Stack=self._caX509Stack) 
    724750         
    725         self._verifyTimeStamp(parsedSOAP, ctxt)  
     751        self._verifyTimeStamp(parsedSOAP,  
     752                              ctxt, 
     753                              timestampMustBeSet=self.timestampMustBeSet, 
     754                              createdElemMustBeSet=self.createdElemMustBeSet, 
     755                              expiresElemMustBeSet=self.expiresElemMustBeSet)  
     756 
    726757        log.info("Signature OK")         
    727          
    728  
    729 class EncryptionError(WSSecurityError): 
    730     """Flags an error in the encryption process""" 
    731  
    732 class DecryptionError(WSSecurityError): 
    733     """Raised from EncryptionHandler.decrypt if an error occurs with the 
    734     decryption process""" 
    735  
    736  
    737 class EncryptionHandler(object): 
    738     """Encrypt/Decrypt SOAP messages using WS-Security"""  
    739      
    740     # Map namespace URIs to Crypto algorithm module and mode  
    741     cryptoAlg = \ 
    742     { 
    743          _ENCRYPTION.WRAP_AES256:      {'module':       AES,  
    744                                         'mode':         AES.MODE_ECB, 
    745                                         'blockSize':    16}, 
    746           
    747          # CBC (Cipher Block Chaining) modes 
    748          _ENCRYPTION.BLOCK_AES256:     {'module':       AES,  
    749                                         'mode':         AES.MODE_CBC, 
    750                                         'blockSize':    16}, 
    751                                          
    752          _ENCRYPTION.BLOCK_TRIPLEDES:  {'module':       DES3,  
    753                                         'mode':         DES3.MODE_CBC, 
    754                                         'blockSize':    8}    
    755     } 
    756  
    757       
    758     def __init__(self, 
    759                  signingCertFilePath=None,  
    760                  signingPriKeyFilePath=None,  
    761                  signingPriKeyPwd=None, 
    762                  chkSecurityTokRef=False, 
    763                  encrNS=_ENCRYPTION.BLOCK_AES256): 
    764          
    765         self.__signingCertFilePath = signingCertFilePath 
    766         self.__signingPriKeyFilePath = signingPriKeyFilePath 
    767         self.__signingPriKeyPwd = signingPriKeyPwd 
    768          
    769         self.__chkSecurityTokRef = chkSecurityTokRef 
    770          
    771         # Algorithm for shared key encryption 
    772         try: 
    773             self.__encrAlg = self.cryptoAlg[encrNS] 
    774              
    775         except KeyError: 
    776             raise EncryptionError, \ 
    777         'Input encryption algorithm namespace "%s" is not supported' % encrNS 
    778  
    779         self.__encrNS = encrNS 
    780          
    781          
    782     def encrypt(self, soapWriter): 
    783         """Encrypt an outbound SOAP message 
    784          
    785         Use Key Wrapping - message is encrypted using a shared key which  
    786         itself is encrypted with the public key provided by the X.509 cert. 
    787         signingCertFilePath""" 
    788          
    789         # Use X.509 Cert to encrypt 
    790         x509Cert = X509.load_cert(self.__signingCertFilePath) 
    791          
    792         soapWriter.dom.setNamespaceAttribute('wsse', OASIS.WSSE) 
    793         soapWriter.dom.setNamespaceAttribute('xenc', _ENCRYPTION.BASE) 
    794         soapWriter.dom.setNamespaceAttribute('ds', DSIG.BASE) 
    795          
    796         # TODO: Put in a check to make sure <wsse:security> isn't already  
    797         # present in header 
    798         wsseElem = soapWriter._header.createAppendElement(OASIS.WSSE,  
    799                                                          'Security') 
    800         wsseElem.node.setAttribute('SOAP-ENV:mustUnderstand', "1") 
    801          
    802         encrKeyElem = wsseElem.createAppendElement(_ENCRYPTION.BASE,  
    803                                                    'EncryptedKey') 
    804          
    805         # Encryption method used to encrypt the shared key 
    806         keyEncrMethodElem = encrKeyElem.createAppendElement(_ENCRYPTION.BASE,  
    807                                                         'EncryptionMethod') 
    808          
    809         keyEncrMethodElem.node.setAttribute('Algorithm',  
    810                                             _ENCRYPTION.KT_RSA_1_5) 
    811  
    812  
    813         # Key Info 
    814         KeyInfoElem = encrKeyElem.createAppendElement(DSIG.BASE, 'KeyInfo') 
    815          
    816         secTokRefElem = KeyInfoElem.createAppendElement(OASIS.WSSE,  
    817                                                   'SecurityTokenReference') 
    818          
    819         x509IssSerialElem = secTokRefElem.createAppendElement(DSIG.BASE,  
    820                                                           'X509IssuerSerial') 
    821  
    822          
    823         x509IssNameElem = x509IssSerialElem.createAppendElement(DSIG.BASE,  
    824                                                           'X509IssuerName') 
    825         x509IssNameElem.createAppendTextNode(x509Cert.get_issuer().as_text()) 
    826  
    827          
    828         x509IssSerialNumElem = x509IssSerialElem.createAppendElement( 
    829                                                   DSIG.BASE,  
    830                                                   'X509IssuerSerialNumber') 
    831          
    832         x509IssSerialNumElem.createAppendTextNode( 
    833                                           str(x509Cert.get_serial_number())) 
    834  
    835         # References to what has been encrypted 
    836         encrKeyCiphDataElem = encrKeyElem.createAppendElement( 
    837                                                           _ENCRYPTION.BASE, 
    838                                                           'CipherData') 
    839          
    840         encrKeyCiphValElem = encrKeyCiphDataElem.createAppendElement( 
    841                                                           _ENCRYPTION.BASE, 
    842                                                           'CipherValue') 
    843  
    844         # References to what has been encrypted 
    845         refListElem = encrKeyElem.createAppendElement(_ENCRYPTION.BASE, 
    846                                                       'ReferenceList') 
    847          
    848         dataRefElem = refListElem.createAppendElement(_ENCRYPTION.BASE, 
    849                                                       'DataReference') 
    850         dataRefElem.node.setAttribute('URI', "#encrypted") 
    851  
    852                       
    853         # Add Encrypted data to SOAP body 
    854         encrDataElem = soapWriter.body.createAppendElement(_ENCRYPTION.BASE,  
    855                                                            'EncryptedData') 
    856         encrDataElem.node.setAttribute('Id', 'encrypted') 
    857         encrDataElem.node.setAttribute('Type', _ENCRYPTION.BASE)   
    858                
    859         # Encryption method used to encrypt the target data 
    860         dataEncrMethodElem = encrDataElem.createAppendElement( 
    861                                                       _ENCRYPTION.BASE,  
    862                                                       'EncryptionMethod') 
    863          
    864         dataEncrMethodElem.node.setAttribute('Algorithm', self.__encrNS) 
    865          
    866         # Cipher data 
    867         ciphDataElem = encrDataElem.createAppendElement(_ENCRYPTION.BASE, 
    868                                                         'CipherData') 
    869          
    870         ciphValueElem = ciphDataElem.createAppendElement(_ENCRYPTION.BASE, 
    871                                                          'CipherValue') 
    872  
    873  
    874         # Get elements from SOAP body for encryption 
    875         dataElem = soapWriter.body.node.childNodes[0] 
    876         data = dataElem.toxml() 
    877       
    878         # Pad data to nearest multiple of encryption algorithm's block size     
    879         modData = len(data) % self.__encrAlg['blockSize'] 
    880         nPad = modData and self.__encrAlg['blockSize'] - modData or 0 
    881          
    882         # PAd with random junk but ... 
    883         data += os.urandom(nPad-1) 
    884          
    885         # Last byte should be number of padding bytes 
    886         # (http://www.w3.org/TR/xmlenc-core/#sec-Alg-Block) 
    887         data += chr(nPad)        
    888          
    889         # Generate shared key and input vector - for testing use hard-coded  
    890         # values to allow later comparison               
    891         sharedKey = os.urandom(self.__encrAlg['blockSize']) 
    892         iv = os.urandom(self.__encrAlg['blockSize']) 
    893          
    894         alg = self.__encrAlg['module'].new(sharedKey, 
    895                                            self.__encrAlg['mode'], 
    896                                            iv) 
    897   
    898         # Encrypt required elements - prepend input vector 
    899         encryptedData = alg.encrypt(iv + data) 
    900         dataCiphValue = base64.encodestring(encryptedData).strip() 
    901  
    902         ciphValueElem.createAppendTextNode(dataCiphValue) 
    903          
    904          
    905         # ! Delete unencrypted message body elements ! 
    906         soapWriter.body.node.removeChild(dataElem) 
    907  
    908          
    909         # Use X.509 cert public key to encrypt the shared key - Extract key 
    910         # from the cert 
    911         rsaPubKey = x509Cert.get_pubkey().get_rsa() 
    912          
    913         # Encrypt the shared key 
    914         encryptedSharedKey = rsaPubKey.public_encrypt(sharedKey,  
    915                                                       RSA.pkcs1_padding) 
    916          
    917         encrKeyCiphVal = base64.encodestring(encryptedSharedKey).strip() 
    918          
    919         # Add the encrypted shared key to the EncryptedKey section in the SOAP 
    920         # header 
    921         encrKeyCiphValElem.createAppendTextNode(encrKeyCiphVal) 
    922  
    923 #        print soapWriter.dom.node.toprettyxml() 
    924 #        import pdb;pdb.set_trace() 
    925          
    926          
    927     def decrypt(self, parsedSOAP): 
    928         """Decrypt an inbound SOAP message""" 
    929          
    930         processorNss = \ 
    931         { 
    932             'xenc':   _ENCRYPTION.BASE, 
    933             'ds':     DSIG.BASE,  
    934             'wsu':    _WSU.UTILITY,  
    935             'wsse':   OASIS.WSSE,  
    936             'soapenv':"http://schemas.xmlsoap.org/soap/envelope/"  
    937         } 
    938         ctxt = Context(parsedSOAP.dom, processorNss=processorNss) 
    939          
    940         refListNodes = xpath.Evaluate('//xenc:ReferenceList',  
    941                                       contextNode=parsedSOAP.dom,  
    942                                       context=ctxt) 
    943         if len(refListNodes) > 1: 
    944             raise DecryptionError, 'Expecting a single ReferenceList element' 
    945          
    946         try: 
    947             refListNode = refListNodes[0] 
    948         except: 
    949             # Message wasn't encrypted - is this OK or is a check needed for 
    950             # encryption info in SOAP body - enveloped form? 
    951             return 
    952  
    953  
    954         # Check for wrapped key encryption 
    955         encrKeyNodes = xpath.Evaluate('//xenc:EncryptedKey',  
    956                                       contextNode=parsedSOAP.dom,  
    957                                       context=ctxt) 
    958         if len(encrKeyNodes) > 1: 
    959             raise DecryptionError, 'This implementation can only handle ' + \ 
    960                                    'single EncryptedKey element' 
    961          
    962         try: 
    963             encrKeyNode = encrKeyNodes[0] 
    964         except: 
    965             # Shared key encryption used - leave out for the moment 
    966             raise DecryptionError, 'This implementation can only handle ' + \ 
    967                                    'wrapped key encryption' 
    968  
    969          
    970         # Check encryption method 
    971         keyEncrMethodNode = getElements(encrKeyNode, 'EncryptionMethod')[0]      
    972         keyAlgorithm = keyEncrMethodNode.getAttributeNode("Algorithm").value 
    973         if keyAlgorithm != _ENCRYPTION.KT_RSA_1_5: 
    974             raise DecryptionError, \ 
    975             'Encryption algorithm for wrapped key is "%s", expecting "%s"' % \ 
    976                 (keyAlgorithm, _ENCRYPTION.KT_RSA_1_5) 
    977  
    978                                                              
    979         if self.__chkSecurityTokRef and self.__signingCertFilePath: 
    980               
    981             # Check input cert. against SecurityTokenReference 
    982             securityTokRefXPath = '/ds:KeyInfo/wsse:SecurityTokenReference' 
    983             securityTokRefNode = xpath.Evaluate(securityTokRefXPath,  
    984                                                 contextNode=encrKeyNode,  
    985                                                 context=ctxt) 
    986             # TODO: Look for ds:X509* elements to check against X.509 cert  
    987             # input 
    988  
    989  
    990         # Look for cipher data for wrapped key 
    991         keyCiphDataNode = getElements(encrKeyNode, 'CipherData')[0] 
    992         keyCiphValNode = getElements(keyCiphDataNode, 'CipherValue')[0] 
    993  
    994         keyCiphVal = str(keyCiphValNode.childNodes[0].nodeValue) 
    995         encryptedKey = base64.decodestring(keyCiphVal) 
    996  
    997         # Read RSA Private key in order to decrypt wrapped key   
    998         priKeyFile = BIO.File(open(self.__signingPriKeyFilePath))           
    999         pwdCallback = lambda *ar, **kw: self.__signingPriKeyPwd                                         
    1000         priKey = RSA.load_key_bio(priKeyFile, callback=pwdCallback) 
    1001          
    1002         sharedKey = priKey.private_decrypt(encryptedKey, RSA.pkcs1_padding) 
    1003          
    1004  
    1005         # Check list of data elements that have been encrypted 
    1006         for dataRefNode in refListNode.childNodes: 
    1007  
    1008             # Get the URI for the reference 
    1009             dataRefURI = dataRefNode.getAttributeNode('URI').value                             
    1010             if dataRefURI[0] != "#": 
    1011                 raise VerifyError, \ 
    1012                     "Expecting # identifier for DataReference URI \"%s\"" % \ 
    1013                     dataRefURI 
    1014  
    1015             # XPath reference - need to check for wsu namespace qualified? 
    1016             #encrNodeXPath = '//*[@wsu:Id="%s"]' % dataRefURI[1:] 
    1017             encrNodeXPath = '//*[@Id="%s"]' % dataRefURI[1:] 
    1018             encrNode = xpath.Evaluate(encrNodeXPath,  
    1019                                       contextNode=parsedSOAP.dom,  
    1020                                       context=ctxt)[0] 
    1021                  
    1022             dataEncrMethodNode = getElements(encrNode, 'EncryptionMethod')[0]      
    1023             dataAlgorithm = \ 
    1024                         dataEncrMethodNode.getAttributeNode("Algorithm").value 
    1025             try:         
    1026                 # Match algorithm name to Crypto module 
    1027                 CryptoAlg = self.cryptoAlg[dataAlgorithm] 
    1028                  
    1029             except KeyError: 
    1030                 raise DecryptionError, \ 
    1031 'Encryption algorithm for data is "%s", supported algorithms are:\n "%s"' % \ 
    1032                     (keyAlgorithm, "\n".join(self.cryptoAlg.keys())) 
    1033  
    1034             # Get Data 
    1035             dataCiphDataNode = getElements(encrNode, 'CipherData')[0] 
    1036             dataCiphValNode = getElements(dataCiphDataNode, 'CipherValue')[0] 
    1037          
    1038             dataCiphVal = str(dataCiphValNode.childNodes[0].nodeValue) 
    1039             encryptedData = base64.decodestring(dataCiphVal) 
    1040              
    1041             alg = CryptoAlg['module'].new(sharedKey, CryptoAlg['mode']) 
    1042             decryptedData = alg.decrypt(encryptedData) 
    1043              
    1044             # Strip prefix - assume is block size 
    1045             decryptedData = decryptedData[CryptoAlg['blockSize']:] 
    1046              
    1047             # Strip any padding suffix - Last byte should be number of padding 
    1048             # bytes 
    1049             # (http://www.w3.org/TR/xmlenc-core/#sec-Alg-Block) 
    1050             lastChar = decryptedData[-1] 
    1051             nPad = ord(lastChar) 
    1052              
    1053             # Sanity check - there may be no padding at all - the last byte  
    1054             # being the end of the encrypted XML? 
    1055             # 
    1056             # TODO: are there better sanity checks than this?! 
    1057             if nPad < CryptoAlg['blockSize'] and nPad > 0 and \ 
    1058                lastChar != '\n' and lastChar != '>': 
    1059                  
    1060                 # Follow http://www.w3.org/TR/xmlenc-core/#sec-Alg-Block - 
    1061                 # last byte gives number of padding bytes 
    1062                 decryptedData = decryptedData[:-nPad] 
    1063  
    1064  
    1065             # Parse the encrypted data - inherit from Reader as a fudge to  
    1066             # enable relevant namespaces to be added prior to parse 
    1067             processorNss.update({'xsi': SCHEMA.XSI3, 'ns1': 'urn:ZSI:examples'}) 
    1068             class _Reader(Reader): 
    1069                 def initState(self, ownerDoc=None): 
    1070                     Reader.initState(self, ownerDoc=ownerDoc) 
    1071                     self._namespaces.update(processorNss) 
    1072                      
    1073             rdr = _Reader() 
    1074             dataNode = rdr.fromString(decryptedData, ownerDoc=parsedSOAP.dom) 
    1075              
    1076             # Add decrypted element to parent and remove encrypted one 
    1077             parentNode = encrNode._get_parentNode() 
    1078             parentNode.appendChild(dataNode) 
    1079             parentNode.removeChild(encrNode) 
    1080              
    1081             from xml.dom.ext import ReleaseNode 
    1082             ReleaseNode(encrNode) 
    1083              
    1084             # Ensure body_root attribute is up to date in case it was 
    1085             # previously encrypted 
    1086             parsedSOAP.body_root = parsedSOAP.body.childNodes[0] 
    1087             #print decryptedData 
    1088             #import pdb;pdb.set_trace() 
    1089  
    1090  
    1091         
    1092 def getElements(node, nameList): 
    1093     '''DOM Helper function for getting child elements from a given node''' 
    1094     # Avoid sub-string matches 
    1095     nameList = isinstance(nameList, basestring) and [nameList] or nameList 
    1096     return [n for n in node.childNodes if str(n.localName) in nameList] 
    1097  
    1098  
    1099 def getChildNodes(node, nodeList=None): 
    1100     if nodeList is None: 
    1101         nodeList = [node]  
    1102     return _getChildNodes(node, nodeList) 
    1103             
    1104 def _getChildNodes(node, nodeList): 
    1105  
    1106     if node.attributes is not None: 
    1107         nodeList += node.attributes.values()  
    1108     nodeList += node.childNodes 
    1109     for childNode in node.childNodes: 
    1110         _getChildNodes(childNode, nodeList) 
    1111     return nodeList 
  • TI12-security/trunk/python/ndg.security.common/ndg/security/common/wssecurity/signaturehandler/etree.py

    r5063 r5357  
    11"""WS-Security digital signature handler for ElementTree XML package 
    22 
    3 NERC Data Grid Project 
     3NERC DataGrid Project 
    44""" 
    55__author__ = "P J Kershaw" 
     
    88__license__ = "BSD - see LICENSE file in top-level directory" 
    99__contact__ = "Philip.Kershaw@stfc.ac.uk" 
    10 __revision__ = '$Id:  $' 
    11  
     10__revision__ = '$Id$' 
     11import base64 
     12import os 
    1213import re 
    1314 
     
    1516from sha import sha 
    1617from M2Crypto import X509, BIO, RSA 
    17 import base64 
     18from StringIO import StringIO 
     19 
     20from ZSI.wstools.Namespaces import DSIG, SOAP 
    1821 
    1922from elementtree import ElementTree, ElementC14N 
    20 from StringIO import StringIO 
    21  
    22 # Conditional import as this is required for the encryption 
    23 # handler 
    24 try: 
    25     # For shared key encryption 
    26     from Crypto.Cipher import AES, DES3 
    27 except: 
    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  
    38 import os 
    39  
    40 import ZSI 
    41 from ZSI.wstools.Namespaces import DSIG, WSA200403, \ 
    42                                    SOAP, SCHEMA # last included for xsi 
    43  
    44 from ZSI.TC import ElementDeclaration,TypeDefinition 
    45 from ZSI.generate.pyclass import pyclass_type 
    46  
    47 from ZSI.wstools.Utility import DOMException 
    48 from ZSI.wstools.Utility import NamespaceError, MessageInterface, ElementProxy 
    49  
    50 # Canonicalization 
    51 from ZSI.wstools.c14n import Canonicalize 
    52  
    53 from xml.dom import Node 
    54 from xml.xpath.Context import Context 
    55 from xml import xpath 
    56  
    57 # Enable settings from a config file 
    58 from ndg.security.common.wssecurity import WSSecurityConfig 
    59  
    60 from ndg.security.common.wssecurity.signaturehandler import _WSU, OASIS, \ 
    61     BaseSignatureHandler 
    6223 
    6324# Check SoapWriter.dom attribute is ElementTreeProxy type 
    6425from ndg.security.common.zsi.elementtreeproxy import ElementTreeProxy 
     26 
     27from ndg.security.common.wssecurity import WSSecurityError 
     28from ndg.security.common.wssecurity.signaturehandler import _WSU, OASIS, \ 
     29    BaseSignatureHandler, NoSignatureFound, \ 
     30    InvalidSignature, TimestampError, MessageExpired, VerifyError, \ 
     31    SignatureError 
     32 
    6533 
    6634from ndg.security.common.X509 import X509Cert, X509CertParse, X509CertRead, \ 
     
    9361    } 
    9462 
    95                  
    9663 
    9764    def _applySignatureConfirmation(self, wsseElem): 
     
    138105        ''' 
    139106        # Nb. wsu ns declaration is in the SOAP header elem 
    140         timestampElem = ElementTree.Element("{%s}%s"%(_WSU.UTILITY,'Timestamp')) 
     107        timestampElem=ElementTree.Element("{%s}%s"%(_WSU.UTILITY,'Timestamp')) 
    141108        wsseElem.append(timestampElem) 
    142109         
     
    159126         
    160127 
    161     def _verifyTimeStamp(self, parsedSOAP, timestampMustBeSet=False): 
     128    def _verifyTimeStamp(self,  
     129                         parsedSOAP,  
     130                         timestampMustBeSet=False, 
     131                         createdElemMustBeSet=True, 
     132                         expiresElemMustBeSet=True): 
    162133        """Call from verify to check timestamp if found.   
    163134         
     
    168139        sender 
    169140        @type timestampMustBeSet: bool 
    170         @param timestampMustBeSet: set to True to raise an exception if no 
    171         timestamp element is present in the message to be checked""" 
    172  
     141        @type timestampMustBeSet: bool 
     142        @param timestampMustBeSet: if set to True, raise an exception if no 
     143        timestamp element is found 
     144        @type createdElemMustBeSet: bool 
     145        @param createdElemMustBeSet: if True. raise an exception if no 
     146        <wsu:Created/> element is present 
     147        @param expiresElemMustBeSet: if True. raise an exception if no 
     148        <wsu:Expires/> element is present 
     149        """ 
    173150        timestampElems = self._soapEnvElem.findall('.//wsu:Timestamp',  
    174151                                                namespaces=self._processorNSs) 
     
    193170                                         namespaces=self._processorNSs) 
    194171        if createdElem is None: 
    195             raise TimestampError("No 'Created' element found in timestamp") 
    196  
    197         # Workaround for fractions of second with datetime module 
    198         try: 
    199             createdDateTime,strCreatedSecFraction = createdElem.text.split('.') 
    200             strCreatedSecFraction = strCreatedSecFraction.split('Z')[0] 
    201             createdExp = -int(len(strCreatedSecFraction)) 
    202             createdSecFraction = int(strCreatedSecFraction) * 10 ** createdExp 
    203              
    204         except ValueError, e: 
    205             raise ValueError("Parsing timestamp Created element: %s" % e) 
    206          
    207  
    208         dtCreated = _strptime(createdDateTime, '%Y-%m-%dT%H:%M:%S') 
    209         dtCreated += timedelta(seconds=createdSecFraction) 
    210         if dtCreated >= dtNow: 
    211             raise TimestampError(\ 
    212         "Timestamp created time %s is equal to or after the current time %s" %\ 
    213                 (dtCreated, dtNow)) 
     172            "No <wsu:Created/> element found in timestamp" 
     173            if createdElemMustBeSet: 
     174                raise TimestampError(msg) 
     175            else: 
     176                log.warning(msg) 
     177        else: 
     178            # Workaround for fractions of second with datetime module 
     179            try: 
     180                createdDateTime, strCreatedSecFraction = \ 
     181                                                    createdElem.text.split('.') 
     182                strCreatedSecFraction = strCreatedSecFraction.split('Z')[0] 
     183                createdExp = -int(len(strCreatedSecFraction)) 
     184                createdSecFraction = int(strCreatedSecFraction) *10**createdExp 
     185                 
     186            except ValueError, e: 
     187                raise ValueError("Parsing timestamp Created element: %s" % e) 
     188             
     189     
     190            dtCreated = _strptime(createdDateTime, '%Y-%m-%dT%H:%M:%S') 
     191            dtCreated += timedelta(seconds=createdSecFraction) 
     192            if dtCreated >= dtNow: 
     193                raise TimestampError("Timestamp created time %s is equal to " 
     194                                     "or after the current time %s" % 
     195                                     (dtCreated, dtNow)) 
    214196         
    215197        expiresElem = timestampElem.find("wsu:Expires", 
    216198                                         namespaces=self._processorNSs) 
    217199        if expiresElem is None: 
    218             raise TimestampError("Verifying message - No Expires element " 
    219                                  "found in Timestamp") 
    220  
    221         try: 
    222             expiryDateTime, strExpirySecFraction = expiresElem.text.split('.') 
    223             strExpirySecFraction = strExpirySecFraction.split('Z')[0] 
    224             expiryExp = -int(len(strExpirySecFraction)) 
    225             expirySecFraction = int(strExpirySecFraction) * 10 ** expiryExp 
    226         except ValueError, e: 
    227             raise ValueError("Parsing timestamp Expires element: %s" % e) 
    228          
    229         dtExpiry = _strptime(expiryDateTime, '%Y-%m-%dT%H:%M:%S') 
    230         dtExpiry += timedelta(seconds=expirySecFraction) 
    231         if dtExpiry < dtNow: 
    232             raise TimestampError(\ 
    233                 "Timestamp expiry time %s is before the current time %s" % \ 
    234                 (dtCreated, dtNow)) 
    235              
    236         log.debug("Verified timestamp") 
    237              
    238                     
    239     #_________________________________________________________________________ 
     200            msg = "No <wsu:Expires/> element found in Timestamp" 
     201            if expiresElemMustBeSet: 
     202                raise TimestampError(msg) 
     203            else: 
     204                log.warning(msg) 
     205        else: 
     206            try: 
     207                expiryDateTime, strExpirySecFraction = \ 
     208                                                    expiresElem.text.split('.') 
     209                strExpirySecFraction = strExpirySecFraction.split('Z')[0] 
     210                expiryExp = -int(len(strExpirySecFraction)) 
     211                expirySecFraction = int(strExpirySecFraction) * 10 ** expiryExp 
     212                 
     213            except ValueError, e: 
     214                raise ValueError("Parsing timestamp Expires element: %s" % e) 
     215             
     216            dtExpiry = _strptime(expiryDateTime, '%Y-%m-%dT%H:%M:%S') 
     217            dtExpiry += timedelta(seconds=expirySecFraction) 
     218            if dtExpiry < dtNow: 
     219                raise MessageExpired("Message has expired: timestamp expiry " 
     220                                     "time %s is before the current time %s" %  
     221                                     (dtCreated, dtNow)) 
     222             
     223        log.debug("Completed timestamp verification") 
     224             
     225             
    240226    def sign(self, soapWriter): 
    241227        '''Sign the message body and binary security token of a SOAP message 
     
    697683        log.debug("Verified certificate chain of trust") 
    698684         
    699         self._verifyTimeStamp(parsedSOAP)  
     685        self._verifyTimeStamp(parsedSOAP, 
     686                              timestampMustBeSet=self.timestampMustBeSet, 
     687                              createdElemMustBeSet=self.createdElemMustBeSet, 
     688                              expiresElemMustBeSet=self.expiresElemMustBeSet)  
    700689 
    701690        log.info("Signature OK")         
  • TI12-security/trunk/python/ndg.security.common/ndg/security/common/wssecurity/signaturehandler/foursuite.py

    r5063 r5357  
    3535from Ft.Xml.Domlette import CanonicalPrint 
    3636 
    37 from ndg.security.common.wssecurity.signaturehandler import _ENCRYPTION, \ 
    38     _WSU, OASIS, BaseSignatureHandler, WSSecurityError, NoSignatureFound, \ 
    39     InvalidSignature, TimestampError, VerifyError, SignatureError 
    40  
    41 # Enable settings from a config file 
    42 from ndg.security.common.wssecurity import WSSecurityConfig 
     37from ndg.security.common.wssecurity import WSSecurityError 
     38from ndg.security.common.wssecurity.signaturehandler import _WSU, OASIS, \ 
     39    BaseSignatureHandler, NoSignatureFound, InvalidSignature, TimestampError, \ 
     40    MessageExpired, VerifyError, SignatureError 
    4341 
    4442from ndg.security.common.X509 import X509Cert, X509CertParse, X509CertRead, \ 
     
    116114         
    117115 
    118     def _verifyTimeStamp(self, parsedSOAP, timestampMustBeSet=False): 
    119         """Call from verify to check timestamp if found.   
    120          
    121         TODO: refactor input args - maybe these should by object attributes 
     116    def _verifyTimeStamp(self,  
     117                         parsedSOAP,  
     118                         processorNss, 
     119                         timestampMustBeSet=False, 
     120                         createdElemMustBeSet=True, 
     121                         expiresElemMustBeSet=True): 
     122        """Call from verify to check timestamp if found.  The WS-Security 1.1 
     123        specification allows elements to be optional.  Hence, the bool flags 
     124        enable their configuration.  The default behaviour is, send a warning 
     125        if a timestamp is not found but if a timestamp is present raise 
     126        an exception if the created or the expired elements are missing. 
    122127         
    123128        @type parsedSOAP: ZSI.parse.ParsedSoap 
    124129        @param parsedSOAP: object contain parsed SOAP message received from 
    125130        sender 
    126         @type ctxt: 
    127         @param ctxt: XPath context object""" 
    128  
    129         # TODO: do we need to be more rigorous in terms of handling the  
    130         # situation where no timestamp is found? 
    131          
     131        @type processorNss: dict 
     132        @param processorNss: namespaces to be used in XPath query to locate 
     133        timestamp. 
     134        @type timestampMustBeSet: bool 
     135        @param timestampMustBeSet: if set to True, raise an exception if no 
     136        timestamp element is found 
     137        @type createdElemMustBeSet: bool 
     138        @param createdElemMustBeSet: if True. raise an exception if no 
     139        <wsu:Created/> element is present 
     140        @param expiresElemMustBeSet: if True. raise an exception if no 
     141        <wsu:Expires/> element is present 
     142        """ 
     143 
    132144        try: 
    133             timestampNode = parsedSOAP.dom.xpath('//wsu:Timestamp',  
     145            timestampElem = parsedSOAP.dom.xpath('//wsu:Timestamp',  
    134146                                                 explicitNss=processorNss)[0] 
    135         except: 
     147        except IndexError: 
     148            # Catch TypeError raised from attempt to reference element 0 of 
     149            # None type 
    136150            msg = "Verifying message - No timestamp element found" 
    137151            if timestampMustBeSet: 
     
    144158        dtNow = datetime.utcnow() 
    145159 
    146         createdNode = timestampNode.getElementsByTagName("wsu:Created") 
    147         if createdNode is None: 
    148             raise TimestampError("Verifying message - No Created timestamp " 
    149                                  "sub-element found") 
    150              
    151         # Workaround for fractions of second 
    152         try: 
    153             createdDateTime, createdSecFraction = \ 
    154                             createdNode[0].childNodes[0].nodeValue.split('.') 
    155             dtCreated = _strptime(createdDateTime, '%Y-%m-%dT%H:%M:%S') 
    156             createdSeconds = float("0." + createdSecFraction.replace('Z', '')) 
    157             dtCreated += timedelta(seconds=createdSeconds) 
    158                                              
    159         except ValueError, e: 
    160             raise TimestampError("Failed to parse timestamp Created element: " 
    161                                  "%s" % e) 
    162          
    163         if dtCreated >= dtNow: 
    164             raise TimestampError("Timestamp created time %s is equal to or " 
    165                                  "after the current time %s" % \ 
    166                                  (dtCreated, dtNow)) 
    167          
    168         expiresNode = timestampNode.getElementsByTagName("wsu:Expires") 
    169         if expiresNode is None: 
    170             log.warning("Verifying message - No Expires element found in " 
    171                         "Timestamp") 
    172             return 
    173  
    174         try: 
    175             expiresDateTime, expiresSecFraction = \ 
    176                             expiresNode[0].childNodes[0].nodeValue.split('.') 
    177             dtExpiry = _strptime(expiresDateTime, '%Y-%m-%dT%H:%M:%S') 
    178             expirySeconds = float("0." + expiresSecFraction.replace('Z', '')) 
    179             dtExpiry += timedelta(seconds=expirySeconds) 
    180  
    181         except ValueError, e: 
    182             raise TimestampError("Failed to parse timestamp Expires element: " 
    183                                  "%s" % e) 
    184  
    185         if dtExpiry < dtNow: 
    186             raise TimestampError("Timestamp expiry time %s is before the " 
    187                                  "current time %s - i.e. the message has " 
    188                                  "expired." % (dtExpiry, dtNow)) 
     160        createdElem = getElements(timestampElem, "Created")            
     161        if len(createdElem) == 0: 
     162            msg = ("Verifying message: no <wsu:Created/> timestamp " 
     163                   "sub-element found") 
     164            if createdElemMustBeSet: 
     165                raise TimestampError(msg) 
     166            else: 
     167                log.warning(msg) 
     168        else:                
     169            # Workaround for fractions of second 
     170            try: 
     171                createdDateTime, createdSecFraction = \ 
     172                            createdElem[0].childNodes[0].nodeValue.split('.') 
     173                dtCreated = _strptime(createdDateTime, '%Y-%m-%dT%H:%M:%S') 
     174                createdSeconds = float("0."+createdSecFraction.replace('Z','')) 
     175                dtCreated += timedelta(seconds=createdSeconds) 
     176                                                 
     177            except ValueError, e: 
     178                raise TimestampError("Failed to parse timestamp Created " 
     179                                     "element: %s" % e) 
     180             
     181            if dtCreated >= dtNow: 
     182                raise TimestampError("Timestamp created time %s is equal to " 
     183                                     "or after the current time %s" % 
     184                                     (dtCreated, dtNow)) 
     185         
     186        expiresElem = getElements(timestampElem, "Expires") 
     187        if len(expiresElem) == 0: 
     188            msg = ("Verifying message: no <wsu:Expires/> element found in " 
     189                   "Timestamp") 
     190            if expiresElemMustBeSet: 
     191                raise TimeStampError(msg) 
     192            else: 
     193                log.warning(warning) 
     194        else: 
     195            try: 
     196                expiresDateTime, expiresSecFraction = \ 
     197                            expiresElem[0].childNodes[0].nodeValue.split('.') 
     198                dtExpiry = _strptime(expiresDateTime, '%Y-%m-%dT%H:%M:%S') 
     199                expirySeconds = float("0."+expiresSecFraction.replace('Z', '')) 
     200                dtExpiry += timedelta(seconds=expirySeconds) 
     201     
     202            except ValueError, e: 
     203                raise TimestampError("Failed to parse timestamp Expires " 
     204                                     "element: %s" % e) 
     205     
     206            if dtExpiry < dtNow: 
     207                raise MessageExpired("Message has expired: timestamp expiry " 
     208                                     "time %s is before the current time %s." % 
     209                                     (dtExpiry, dtNow)) 
    189210             
    190211                    
     
    324345        # Add Reference to body so that it can be included in the signature 
    325346        soapWriter.body.setAttributeNS(_WSU.UTILITY, 'Id', "body") 
    326 #        soapWriter.body.setAttributeNS('xmlns:wsu', _WSU.UTILITY) 
    327  
    328         # Serialize and re-parse prior to reference generation - calculating 
    329         # canonicalization based on soapWriter.dom.node seems to give an 
    330         # error: the order of wsu:Id attribute is not correct 
    331 #        try: 
    332 #            docNode = Reader().fromString(str(soapWriter)) 
    333 #        except Exception, e: 
    334 #            raise SignatureError("Error parsing SOAP message for signing: %s"% 
    335 #                                 e) 
    336  
    337347 
    338348        refElems = soapWriter.body.evaluate('//*[@wsu:Id]',  
     
    354364            # Canonicalize reference 
    355365            inclusiveNsKWs = self.createUnsupressedPrefixKW(self.refC14nKw) 
    356 #            refSubsetList = getChildNodes(refElem) 
    357 #            refC14n = Canonicalize(docNode,  
    358 #                                   None,  
    359 #                                   subset=refSubsetList, 
    360 #                                   **inclusiveNsKWs) 
    361366            refC14n = refElem.canonicalize(algorithm=refC14nAlg,  
    362367                                           **inclusiveNsKWs) 
     
    404409        #         
    405410        # Canonicalize the signedInfo node 
    406 #        docNode = Reader().fromString(str(soapWriter)) 
    407 #        ctxt = Context(docNode, processorNss=processorNss) 
    408 #        signedInfoElem = xpath.Evaluate('//ds:SignedInfo',  
    409 #                                        contextNode=docNode,  
    410 #                                        context=ctxt)[0] 
    411 # 
    412 #        signedInfoSubsetList = getChildNodes(signedInfoElem) 
    413          
    414411        signedInfoInclusiveNsKWs = self.createUnsupressedPrefixKW( 
    415412                                                        self.signedInfoC14nKw) 
    416 #        c14nSignedInfo = Canonicalize(docNode,  
    417 #                                      None,  
    418 #                                      subset=signedInfoSubsetList, 
    419 #                                      **inclusiveNSKWs) 
    420413        try: 
    421414            signedInfoElem = soapWriter.body.evaluate('//ds:SignedInfo', 
     
    691684                                  caX509Stack=self._caX509Stack) 
    692685         
    693         self._verifyTimeStamp(parsedSOAP)  
     686        self._verifyTimeStamp(parsedSOAP,  
     687                              processorNss, 
     688                              timestampMustBeSet=self.timestampMustBeSet, 
     689                              createdElemMustBeSet=self.createdElemMustBeSet, 
     690                              expiresElemMustBeSet=self.expiresElemMustBeSet)  
    694691        log.info("Signature OK")         
  • TI12-security/trunk/python/ndg.security.common/ndg/security/common/zsi/attributeauthority/Makefile

    r4361 r5357  
    77# server side code 
    88# 
    9 # @copyright (C) 2008 CCLRC & NERC 
     9# @copyright (C) 2008 STFC 
    1010# 
    1111# @license This software may be distributed under the terms of the Q Public  
  • TI12-security/trunk/python/ndg.security.common/ndg/security/common/zsi/sessionmanager/Makefile

    r4361 r5357  
    77# server side code 
    88# 
    9 # @copyright (C) 2007 CCLRC & NERC 
     9# @copyright (C) 2007 STFC 
    1010# 
    1111# @license This software may be distributed under the terms of the Q Public  
  • TI12-security/trunk/python/ndg.security.server/ndg/security/server/share/Makefile

    r4752 r5357  
    66# Generate SysV init scripts from ndg-aa template 
    77# 
    8 # @copyright (C) 2007 CCLRC & NERC 
     8# @copyright (C) 2007 STFC 
    99# 
    1010# @license This software may be distributed under the terms of the Q Public  
  • TI12-security/trunk/python/ndg.security.server/ndg/security/server/wsgi/authz.py

    r5355 r5357  
    137137        policyCfg = PEPFilter._filterKeywords(local_conf, 'policy.') 
    138138        self.policyFilePath = policyCfg['filePath'] 
    139         self.policy = Policy.Parse(policyCfg['filePath']) 
     139        policy = Policy.Parse(policyCfg['filePath']) 
    140140         
    141141        # Initialise the Policy Information Point to None.  This object is 
    142142        # created and set later.  See AuthorizationMiddleware. 
    143         self.pdp = PDP(self.policy, None) 
     143        self.pdp = PDP(policy, None) 
    144144         
    145145        self.sessionKey = local_conf.get('sessionKey',  
     
    250250        path  
    251251        """ 
    252         matchingTargets = [target for target in self.policy.targets  
     252        matchingTargets = [target for target in self.pdp.policy.targets  
    253253                           if target.regEx.match(resourceURI) is not None] 
    254254        return matchingTargets 
     
    302302                 
    303303        return filteredConf 
     304 
     305    def _getPDP(self): 
     306        if self._pdp is None: 
     307            raise TypeError("PDP object has not been initialised") 
     308        return self._pdp 
     309     
     310    def _setPDP(self, pdp): 
     311        if not isinstance(pdp, (PDP, None.__class__)): 
     312            raise TypeError("Expecting %s or None type for pdp; got %r" % 
     313                            (PDP.__class__.__name__, pdp)) 
     314        self._pdp = pdp 
     315 
     316    pdp = property(fget=_getPDP, 
     317                   fset=_setPDP, 
     318                   doc="Policy Decision Point object makes access control " 
     319                       "decisions on behalf of the PEP") 
    304320 
    305321    
     
    409425        attrCert = credential.get('attCert') 
    410426        if attrCert is not None: 
    411             log.debug("PIPMiddleware._getAttributeCertificate: existing " 
    412                       "Attribute Certificate cached in Credential Wallet for " 
    413                       "user session [%s]", 
     427            log.debug("PIPMiddleware._getAttributeCertificate: retrieved " 
     428                      "existing Attribute Certificate cached in Credential " 
     429                      "Wallet for user session [%s]", 
    414430                      self.session['username']) 
    415431 
     
    474490        pepInterceptFunc = pepFilter.multiHandlerInterceptFactory() 
    475491         
     492        # Slot in the Policy Information Point in the WSGI stack at this point 
     493        # so that it can take a copy of the beaker session object from environ 
     494        # ahead of the PDP make a request to it for an Attribute Certificate 
     495        # retrieval 
    476496        pipApp = PIPMiddleware(pepFilter, 
    477497                               global_conf, 
     
    480500        pepFilter.pdp.pip = pipApp 
    481501         
    482 #        app = MultiHandler(pepFilter) 
    483502        app = MultiHandler(pipApp) 
    484503 
  • TI12-security/trunk/python/ndg.security.server/ndg/security/server/wsgi/ssl.py

    r5343 r5357  
    263263        # Check certificate Distinguished Name via  
    264264        # ClientCertVerificationInterface object 
    265         return self._clientCertVerify(x509Cert) 
    266  
     265        return self._verifyClientCert(x509Cert) 
     266 
  • TI12-security/trunk/python/ndg.security.server/ndg/security/server/zsi/attributeauthority/Makefile

    r4386 r5357  
    77# server side code 
    88# 
    9 # @copyright (C) 2007 CCLRC & NERC 
     9# @copyright (C) 2007 STFC 
    1010# 
    1111# @license This software may be distributed under the terms of the Q Public  
  • TI12-security/trunk/python/ndg.security.server/ndg/security/server/zsi/sessionmanager/Makefile

    r4386 r5357  
    77# server side code 
    88# 
    9 # @copyright (C) 2007 CCLRC & NERC 
     9# @copyright (C) 2007 STFC 
    1010# 
    1111# @license This software may be distributed under the terms of the Q Public  
  • TI12-security/trunk/python/ndg.security.server/ndg/security/server/zsi/twisted/attributeauthority/Makefile

    r4390 r5357  
    77# server side code 
    88# 
    9 # @copyright (C) 2007 CCLRC & NERC 
     9# @copyright (C) 2007 STFC 
    1010# 
    1111# @license This software may be distributed under the terms of the Q Public  
  • TI12-security/trunk/python/ndg.security.server/ndg/security/server/zsi/twisted/sessionmanager/Makefile

    r4390 r5357  
    77# server side code 
    88# 
    9 # @copyright (C) 2007 CCLRC & NERC 
     9# @copyright (C) 2007 STFC 
    1010# 
    1111# @license This software may be distributed under the terms of the Q Public  
  • TI12-security/trunk/python/ndg.security.test/ndg/security/test/config/sessionmanager/userx509certauthn.py

    r5154 r5357  
    4545        ''' 
    4646         
    47         # Check password by executing a trial load of the private key 
     47        # Check password by executing a trial load of the private key - Nb. 
     48        # unicode is not supported 
    4849        try: 
    4950            RSA.load_key_string(self.userPriKey,  
    50                                 callback=lambda *ar, **kw: passphrase) 
     51                                callback=lambda *ar, **kw: str(passphrase)) 
    5152        except RSA.RSAError, e: 
    5253            raise AuthNServiceInvalidCredentials(e) 
  • TI12-security/trunk/python/ndg.security.test/ndg/security/test/unit/attCert/ac.xml

    r5290 r5357  
    99        <userId>/O=NDG/OU=BADC/CN=pjkershaw</userId> 
    1010        <validity> 
    11             <notBefore>2009 05 14 11 18 00</notBefore>  
    12             <notAfter>2009 05 14 19 18 00</notAfter>  
     11            <notBefore>2009 06 05 08 04 29</notBefore>  
     12            <notAfter>2009 06 05 16 04 29</notAfter>  
    1313        </validity> 
    1414        <attributes> 
     
    2727        <provenance>original</provenance>  
    2828    </acInfo> 
    29 <ds:Signature><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="ds"></ec:InclusiveNamespaces></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></ds:SignatureMethod><ds:Reference URI=""><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="xmlns"></ec:InclusiveNamespaces></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod><ds:DigestValue>sPx/VQ+W/6ImQpzpVyqNN4SRi94=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>lvYk1Pjb7lDkkkZBrF8PhlT5krZWReU1l6X1vjDUPPkthV2ASVwj3NHCLJIFwrp3PCD8TLUERbhN 
    30 7MBO/IhQ9AM2qHZDScc+QxJ1iCdXgIGiV5Bb98Gyex3ZxBE+Kj7HlD2NCwzLswKUTEHfdstMlWWI 
    31 FuDlalmEJ2pyGdyi+DaA7b1g2rUxapJneqvH94SNQCaS7RqiThy5JyI/u43cMeZXgxCeGzDUZmWo 
    32 UQb4jbLe+oQn7PhYV15jf3MwksLqfiLYO9iyjKZhfXQpn+mOW5dY3+00NTb5v6wup+f3l7uGNHkU 
    33 U+AfrdIAp52UPM15kWd9aYjQQgdwDegzAz/mBA==</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIICazCCAdSgAwIBAgICAQEwDQYJKoZIhvcNAQEEBQAwLzEMMAoGA1UEChMDTkRH 
     29<ds:Signature><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="ds"></ec:InclusiveNamespaces></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></ds:SignatureMethod><ds:Reference URI=""><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="xmlns"></ec:InclusiveNamespaces></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod><ds:DigestValue>7rpCVUcm4cDwkpIzPBthqpqj38Q=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>FPniax9JlVxqLYlj9b0aEYbPFMyTvz8ok0Ci3tU+s0GV5HIiA0c7eXj9sU1JVy5jxyYYMtzcU9V6 
     30eszrVX4TL7Twk9GL3xCWk+7VttUAAT3AAdtYisjD2O6wCHl6H/K/orV5EKERb+raB4v6ji++wOi1 
     31WJFjtV/QTz8Ld/kaQFdJV2cl3DFlO3XvL+TkBMayrjI4UXuwZk+AlhYtvDUKrcSL36JNpJBIhvgu 
     32RLgdwgDwdQKoLkg/tNhYNaqDiAs6N2F17jhYzisfLmwZfTs8vefT9nrvz0lTbpEB1/geiAl9nWdB 
     33zKGTsPpYlOG9wdEL6BBhAD2RsDSr4pb+ng1tjA==</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIICazCCAdSgAwIBAgICAQEwDQYJKoZIhvcNAQEEBQAwLzEMMAoGA1UEChMDTkRH 
    3434MQ0wCwYDVQQLEwRCQURDMRAwDgYDVQQDEwdUZXN0IENBMB4XDTA4MTIxNjE1MTE0 
    3535OFoXDTEzMTIxNTE1MTE0OFowLDEMMAoGA1UEChMDTkRHMQ0wCwYDVQQLEwRCQURD 
  • TI12-security/trunk/python/ndg.security.test/ndg/security/test/unit/wsgi/ssl/test_ssl.py

    r5343 r5357  
    1919from paste.deploy import loadapp 
    2020from ndg.security.test.unit import BaseTestCase 
     21from ndg.security.common.X509 import X509Cert 
    2122 
    2223class TestSSLClientAuthNMiddleware(BaseTestCase): 
     
    5152 
    5253 
    53 class SSLClientAuthNTestCase(unittest.TestCase): 
     54class SSLClientAuthNTestCase(BaseTestCase): 
    5455 
    5556    def __init__(self, *args, **kwargs): 
     
    5859        self.app = paste.fixture.TestApp(wsgiapp) 
    5960          
    60         unittest.TestCase.__init__(self, *args, **kwargs) 
     61        BaseTestCase.__init__(self, *args, **kwargs) 
    6162         
    6263 
     
    7374     
    7475    def test03ClientCertSet(self): 
    75         thisDir = os.dirname(__file__) 
    76         sslClientCertFilePath = os.path.join(BaseTestCase.configDirEnvVarName, 
    77                                              'pki', 
    78                                              'test.crt') 
    79         sslClientCert = str(X509.Read(sslClientCertFilePath)) 
     76        thisDir = os.path.dirname(__file__) 
     77        sslClientCertFilePath = os.path.join( 
     78                                os.environ[BaseTestCase.configDirEnvVarName], 
     79                                'pki', 
     80                                'test.crt') 
     81        sslClientCert = X509Cert.Read(sslClientCertFilePath).toString() 
    8082        extra_environ = {'HTTPS':'1', 'SSL_CLIENT_CERT': sslClientCert} 
    8183        response = self.app.get('/secured/uri', 
  • TI12-security/trunk/python/ndg.security.test/ndg/security/test/unit/wssecurity/dom/client/Makefile

    r3108 r5357  
    77# server side code 
    88# 
    9 # @copyright (C) 2007 CCLRC & NERC 
     9# @copyright (C) 2007 STFC 
    1010# 
    1111# @license This software may be distributed under the terms of the Q Public 
  • TI12-security/trunk/python/ndg.security.test/ndg/security/test/unit/wssecurity/dom/server/Makefile

    r3123 r5357  
    77# server side code 
    88# 
    9 # @copyright (C) 2007 CCLRC & NERC 
     9# @copyright (C) 2007 STFC 
    1010# 
    1111# @license This software may be distributed under the terms of the Q Public  
  • TI12-security/trunk/python/ndg.security.test/ndg/security/test/unit/wssecurity/foursuite/client/Makefile

    r5053 r5357  
    77# server side code 
    88# 
    9 # @copyright (C) 2007 CCLRC & NERC 
     9# @copyright (C) 2007 STFC 
    1010# 
    1111# @license This software may be distributed under the terms of the Q Public 
  • TI12-security/trunk/python/ndg.security.test/ndg/security/test/unit/wssecurity/foursuite/server/Makefile

    r5053 r5357  
    77# server side code 
    88# 
    9 # @copyright (C) 2007 CCLRC & NERC 
     9# @copyright (C) 2007 STFC 
    1010# 
    1111# @license This software may be distributed under the terms of the Q Public  
Note: See TracChangeset for help on using the changeset viewer.