Changeset 6399
- Timestamp:
- 25/01/10 16:19:17 (11 years ago)
- Location:
- TI12-security/trunk/WSSecurity/ndg/wssecurity
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
TI12-security/trunk/WSSecurity/ndg/wssecurity/common/signaturehandler/__init__.py
r6396 r6399 111 111 className=('',), 112 112 reqBinarySecurityTokValType=(OASIS.X509TOKEN.X509,), 113 verifyingCert=(None, ),113 verifyingCert=(None, ''), 114 114 verifyingCertFilePath=(None, ''), 115 signingCert=(None, ),115 signingCert=(None, ''), 116 116 signingCertFilePath=(None, ''), 117 117 signingCertChain=([],), 118 signingPriKey=(None, ),118 signingPriKey=(None, ''), 119 119 signingPriKeyFilePath=(None, ''), 120 120 signingPriKeyPwd=(None, ''), … … 471 471 doc="X.509 Certificate to include signature") 472 472 473 473 def _getSigningCertFilePath(self): 474 "Get signature X.509 certificate property method" 475 return self.__signingCertFilePath 476 474 477 def _setSigningCertFilePath(self, signingCertFilePath): 475 478 "Set signature X.509 certificate property method" … … 484 487 self.__signingCertFilePath = signingCertFilePath 485 488 486 signingCertFilePath = property(fset=_setSigningCertFilePath, 489 signingCertFilePath = property(fget=_getSigningCertFilePath, 490 fset=_setSigningCertFilePath, 487 491 doc="File path X.509 cert. to include with " 488 492 "signed message") … … 498 502 chain of trust. The last certificate is the one associated with the 499 503 private key used to sign the message.''' 504 self.__signingCertChain = X509Stack() 505 500 506 for cert in signingCertChain: 501 self.__signingCertChain.push(cert) 507 if cert: 508 self.__signingCertChain.push(cert) 502 509 503 510 def _getSigningCertChain(self): … … 506 513 signingCertChain = property(fset=_setSigningCertChain, 507 514 fget=_getSigningCertChain, 508 doc="Cert.s in chain of trust to cert. used " 509 "to verify msg.") 515 doc="Certificates in the chain of trust to " 516 "verify the certificate provided in an " 517 "incoming message.") 510 518 511 519 def _setSigningPriKeyPwd(self, signingPriKeyPwd): 512 520 "Set method for private key file password used to sign message" 513 if signingPriKeyPwd is not None and \514 not isinstance(signingPriKeyPwd, basestring):521 if (signingPriKeyPwd is not None and 522 not isinstance(signingPriKeyPwd, basestring)): 515 523 raise AttributeError("Signing private key password must be None " 516 524 "or a valid string") … … 534 542 @type signingPriKey: M2Crypto.RSA.RSA / string / None 535 543 @param signingPriKey: private key used to sign message""" 536 537 if isinstance(signingPriKey, basestring): 544 if not signingPriKey: 545 self.__signingPriKey = None 546 547 elif isinstance(signingPriKey, basestring): 538 548 pwdCallback = lambda *ar, **kw: self.__signingPriKeyPwd 539 549 self.__signingPriKey = RSA.load_key_string(signingPriKey, … … 542 552 self.__signingPriKey = signingPriKey 543 553 544 elif signingPriKey is None:545 self.__signingPriKey = None546 554 else: 547 555 raise TypeError("Signing private key must be a valid " … … 602 610 @type caCertDir: directory from which to read CA certificate files''' 603 611 604 if caCertDir is None:612 if not caCertDir: 605 613 return 606 614 -
TI12-security/trunk/WSSecurity/ndg/wssecurity/common/signaturehandler/foursuite.py
r6396 r6399 14 14 import os 15 15 import re 16 import traceback 16 17 from cStringIO import StringIO 17 18 … … 57 58 WS-Security 58 59 """ 60 # Namespaces for XPath searches 61 PROCESSOR_NSS = { 62 'ds': DSIG.BASE, 63 'wsu': _WSU.UTILITY, 64 'wsse': OASIS.WSSE, 65 'soapenv':SOAP.ENV 66 } 67 59 68 _refC14nPfxSet = lambda self: len(self.refC14nKw.get( 60 69 BaseSignatureHandler.ZSI_C14N_KEYWORD_NAME, [])) > 0 … … 71 80 "for signature of signed info " 72 81 "elements") 82 83 def sign(self, soapWriter): 84 '''Sign the message body and binary security token of a SOAP message 85 86 @type soapWriter: ZSI.writer.SoapWriter 87 @param soapWriter: ZSI object to write SOAP message 88 ''' 89 90 # Add X.509 cert as binary security token 91 if self.reqBinarySecurityTokValType == \ 92 self.BINARY_SECURITY_TOK_VAL_TYPE['X509PKIPathv1']: 93 if self.signingCertChain is None: 94 msg = 'SignatureHandler signingCertChain attribute is not set' 95 log.error(msg) 96 raise AttributeError(msg) 97 98 binSecTokVal = base64.encodestring(self.signingCertChain.asDER()) 99 else: 100 # Assume X.509 / X.509 vers 3 101 if self.signingCert is None: 102 msg = 'SignatureHandler signingCert attribute is not set' 103 log.error(msg) 104 raise AttributeError(msg) 105 106 binSecTokVal = base64.encodestring(self.signingCert.asDER()) 107 108 soapWriter._header.setNamespaceAttribute('wsse', OASIS.WSSE) 109 soapWriter._header.setNamespaceAttribute('wsse11', OASIS.WSSE11) 110 soapWriter._header.setNamespaceAttribute('wsu', _WSU.UTILITY) 111 soapWriter._header.setNamespaceAttribute('ds', DSIG.BASE) 112 113 # Flag if inclusive namespace prefixes are set for the signature or 114 # reference elements 115 if self.refC14nPfxSet or self.signedInfoC14nPfxSet: 116 soapWriter._header.setNamespaceAttribute('ec', DSIG.C14N_EXCL) 117 118 # Check <wsse:security> isn't already present in header 119 wsseElems = soapWriter._header.evaluate('//wsse:security', 120 processorNss=SignatureHandler.PROCESSOR_NSS) 121 if len(wsseElems) > 1: 122 raise SignatureError('wsse:Security element is already present') 123 124 # Add WSSE element 125 wsseElem = soapWriter._header.createAppendElement(OASIS.WSSE, 126 'Security') 127 wsseElem.setNamespaceAttribute('wsse', OASIS.WSSE) 128 129 # Flag to recipient - they MUST parse and check this signature 130 wsseElem.setAttributeNS(SOAP.ENV, 'mustUnderstand', "1") 131 132 # Binary Security Token element will contain the X.509 cert 133 # corresponding to the private key used to sing the message 134 binSecTokElem = wsseElem.createAppendElement(OASIS.WSSE, 135 'BinarySecurityToken') 136 137 # Value type can be any be any one of those supported via 138 # BINARY_SECURITY_TOK_VAL_TYPE 139 binSecTokElem.setAttributeNS(None, 140 'ValueType', 141 self.reqBinarySecurityTokValType) 142 143 binSecTokElem.setAttributeNS(None, 144 'EncodingType', 145 self.BINARY_SECURITY_TOK_ENCODING_TYPE) 146 147 # Add ID so that the binary token can be included in the signature 148 binSecTokElem.setAttributeNS(_WSU.UTILITY, 'Id', "binaryToken") 149 150 binSecTokElem.createAppendTextNode(binSecTokVal) 151 152 # Timestamp 153 if self.addTimestamp: 154 self._addTimeStamp(wsseElem) 155 156 # Signature Confirmation 157 if self.applySignatureConfirmation: 158 self._applySignatureConfirmation(wsseElem) 159 160 # Signature 161 signatureElem = wsseElem.createAppendElement(DSIG.BASE, 'Signature') 162 signatureElem.setNamespaceAttribute('ds', DSIG.BASE) 163 164 # Signature - Signed Info 165 signedInfoElem = signatureElem.createAppendElement(DSIG.BASE, 166 'SignedInfo') 167 168 # Signed Info - Canonicalization method 169 c14nMethodElem = signedInfoElem.createAppendElement(DSIG.BASE, 170 'CanonicalizationMethod') 171 172 # Set based on 'signedInfoIsExcl' property 173 c14nAlgOpt = (DSIG.C14N, DSIG.C14N_EXCL) 174 signedInfoC14nAlg = c14nAlgOpt[int(self.signedInfoC14nIsExcl)] 175 176 c14nMethodElem.setAttributeNS(None, 'Algorithm', signedInfoC14nAlg) 177 178 if self.signedInfoC14nPfxSet: 179 c14nInclNamespacesElem = c14nMethodElem.createAppendElement( 180 signedInfoC14nAlg, 181 'InclusiveNamespaces') 182 inclNsPfx = ' '.join(self.signedInfoC14nKw[ 183 SignatureHandler.ZSI_C14N_KEYWORD_NAME]) 184 c14nInclNamespacesElem.setAttributeNS(None, 'PrefixList', inclNsPfx) 185 186 # Signed Info - Signature method 187 sigMethodElem = signedInfoElem.createAppendElement(DSIG.BASE, 188 'SignatureMethod') 189 sigMethodElem.setAttributeNS(None, 'Algorithm', DSIG.SIG_RSA_SHA1) 190 191 # Signature - Signature value 192 signatureValueElem = signatureElem.createAppendElement(DSIG.BASE, 193 'SignatureValue') 194 195 # Key Info 196 KeyInfoElem = signatureElem.createAppendElement(DSIG.BASE, 'KeyInfo') 197 secTokRefElem = KeyInfoElem.createAppendElement(OASIS.WSSE, 198 'SecurityTokenReference') 199 200 # Reference back to the binary token included earlier 201 wsseRefElem = secTokRefElem.createAppendElement(OASIS.WSSE, 202 'Reference') 203 wsseRefElem.setAttributeNS(None, 'URI', "#binaryToken") 204 205 # Add Reference to body so that it can be included in the signature 206 soapWriter.body.setAttributeNS(_WSU.UTILITY, 'Id', "body") 207 208 refElems = soapWriter.body.evaluate('//*[@wsu:Id]', 209 processorNss=SignatureHandler.PROCESSOR_NSS) 210 211 # Set based on 'signedInfoIsExcl' property 212 refC14nAlg = c14nAlgOpt[int(self.refC14nIsExcl)] 213 214 # 1) Reference Generation 215 # 216 # Find references 217 for refElem in refElems: 218 219 refID = refElem.getAttributeValue(_WSU.UTILITY, 'Id') 220 221 # Set URI attribute to point to reference to be signed 222 uri = u"#" + refID 223 224 # Canonicalize reference 225 # 226 # ndg.wssecurity.common.utils.zsi.DomletteElementProxy's C14N method 227 # wraps 4Suite-XML CanonicalPrint 228 unsuppressedPrefixes = self.refC14nKw.get( 229 SignatureHandler.ZSI_C14N_KEYWORD_NAME, []) 230 231 refC14n = refElem.canonicalize(algorithm=refC14nAlg, 232 unsuppressedPrefixes=unsuppressedPrefixes) 233 234 # Calculate digest for reference and base 64 encode 235 # 236 # Nb. encodestring adds a trailing newline char 237 digestValue = base64.encodestring(sha(refC14n).digest()).strip() 238 239 240 # Add a new reference element to SignedInfo 241 refElem = signedInfoElem.createAppendElement(DSIG.BASE, 242 'Reference') 243 refElem.setAttributeNS(None, 'URI', uri) 244 245 # Use ds:Transforms or wsse:TransformationParameters? 246 transformsElem = refElem.createAppendElement(DSIG.BASE, 247 'Transforms') 248 transformElem = transformsElem.createAppendElement(DSIG.BASE, 249 'Transform') 250 251 # Set Canonicalization algorithm type 252 transformElem.setAttributeNS(None, 'Algorithm', refC14nAlg) 253 if self.refC14nPfxSet: 254 # Exclusive C14N requires inclusive namespace elements 255 inclNamespacesElem = transformElem.createAppendElement( 256 refC14nAlg, 257 'InclusiveNamespaces') 258 refInclNsPfx = ' '.join(self.refC14nKw[ 259 SignatureHandler.ZSI_C14N_KEYWORD_NAME]) 260 inclNamespacesElem.setAttributeNS(None, 'PrefixList', 261 refInclNsPfx) 262 263 # Digest Method 264 digestMethodElem = refElem.createAppendElement(DSIG.BASE, 265 'DigestMethod') 266 digestMethodElem.setAttributeNS(None, 'Algorithm', DSIG.DIGEST_SHA1) 267 268 # Digest Value 269 digestValueElem = refElem.createAppendElement(DSIG.BASE, 270 'DigestValue') 271 digestValueElem.createAppendTextNode(digestValue) 272 273 # 2) Signature Generation 274 # 275 # Canonicalize the signedInfo node 276 try: 277 signedInfoElem = soapWriter.body.evaluate('//ds:SignedInfo', 278 processorNss=SignatureHandler.PROCESSOR_NSS)[0] 279 except TypeError, e: 280 log.error("Error locating SignedInfo element for signature") 281 raise 282 283 unsuppressedPrefixes = self.refC14nKw.get( 284 SignatureHandler.ZSI_C14N_KEYWORD_NAME, []) 285 286 # ndg.wssecurity.common.utils.zsi.DomletteElementProxy's C14N method 287 # wraps 4Suite-XML CanonicalPrint 288 c14nSignedInfo = signedInfoElem.canonicalize( 289 algorithm=signedInfoC14nAlg, 290 unsuppressedPrefixes=unsuppressedPrefixes) 291 # Calculate digest of SignedInfo 292 signedInfoDigestValue = sha(c14nSignedInfo).digest() 293 294 # Sign using the private key and base 64 encode the result 295 signatureValue = self.signingPriKey.sign(signedInfoDigestValue) 296 b64EncSignatureValue = base64.encodestring(signatureValue).strip() 297 298 # Add to <SignatureValue> 299 signatureValueElem.createAppendTextNode(b64EncSignatureValue) 300 301 log.info("Signature generation complete") 302 303 def verify(self, parsedSOAP, raiseNoSignatureFound=True): 304 """Verify signature 305 306 @type parsedSOAP: ZSI.parse.ParsedSoap 307 @param parsedSOAP: object contain parsed SOAP message received from 308 sender""" 309 310 signatureElem = parsedSOAP.dom.xpath('//ds:Signature', 311 explicitNss=SignatureHandler.PROCESSOR_NSS) 312 nSignatureElem = len(signatureElem) 313 if nSignatureElem > 1: 314 raise VerifyError('Multiple <ds:Signature/> elements found') 315 316 elif nSignatureElem < 1: 317 # Message wasn't signed 318 msg = "Input message wasn't signed!" 319 if raiseNoSignatureFound: 320 raise NoSignatureFound(msg) 321 else: 322 log.warning(msg) 323 return 324 325 signatureElem = signatureElem[0] 326 327 # Two stage process: reference validation followed by signature 328 # validation 329 330 # 1) Reference Validation 331 332 # Check for canonicalization set via ds:CanonicalizationMethod - 333 # Use this later as a back up in case no Canonicalization was set in 334 # the transforms elements 335 try: 336 c14nMethodElem = parsedSOAP.dom.xpath('//ds:CanonicalizationMethod', 337 explicitNss=SignatureHandler.PROCESSOR_NSS)[0] 338 except TypeError: 339 log.error("XPath query error locating " 340 "<ds:CanonicalizationMethod/>: %s" % 341 traceback.format_exc()) 342 raise 343 344 refElems = parsedSOAP.dom.xpath('//ds:Reference', 345 explicitNss=SignatureHandler.PROCESSOR_NSS) 346 347 for refElem in refElems: 348 # Get the URI for the reference 349 refURI = refElem.getAttributeNS(None, 'URI') 350 351 try: 352 transformsElem = getElements(refElem, "Transforms")[0] 353 transformElems = getElements(transformsElem, "Transform") 354 355 refAlgorithm = transformElems[0].getAttributeNS(None, 356 'Algorithm') 357 except Exception, e: 358 raise VerifyError('failed to get transform algorithm for ' 359 '<ds:Reference URI="%s">' % 360 (refURI, e)) 73 361 74 362 # Add extra keyword for Exclusive canonicalization method 363 refC14nKw = {} 364 if self.refC14nIsExcl: 365 try: 366 # Check for no inclusive namespaces set 367 inclusiveNS = getElements(transformElems[0], 368 "InclusiveNamespaces") 369 if len(inclusiveNS) > 0: 370 pfxListAttElem = inclusiveNS[0].getAttributeNodeNS(None, 371 'PrefixList') 372 373 refC14nKw['inclusivePrefixes'] = \ 374 pfxListAttElem.value.split() 375 else: 376 refC14nKw['inclusivePrefixes'] = None 377 except Exception, e: 378 raise VerifyError('failed to handle transform (%s) in ' 379 '<ds:Reference URI="%s">: %s' % \ 380 (transformElems[0], refURI, e)) 381 382 # Canonicalize the reference data and calculate the digest 383 if refURI[0] != "#": 384 raise VerifyError("Expecting # identifier for Reference URI " 385 "\"%s\"" % refURI) 386 387 # XPath reference 388 uriXPath = '//*[@wsu:Id="%s"]' % refURI[1:] 389 uriElem = parsedSOAP.dom.xpath(uriXPath, 390 explicitNss=SignatureHandler.PROCESSOR_NSS)[0] 391 392 f = StringIO() 393 CanonicalPrint(uriElem, stream=f, exclusive=self.refC14nIsExcl, 394 **refC14nKw) 395 refC14n = f.getvalue() 396 digestValue = base64.encodestring(sha(refC14n).digest()).strip() 397 398 # Extract the digest value that was stored 399 digestNode = getElements(refElem, "DigestValue")[0] 400 nodeDigestValue = str(digestNode.childNodes[0].nodeValue).strip() 401 402 # Reference validates if the two digest values are the same 403 if digestValue != nodeDigestValue: 404 raise InvalidSignature('Digest Values do not match for URI: ' 405 '"%s"' % refURI) 406 407 log.debug("Verified canonicalization for element %s" % refURI[1:]) 408 409 # 2) Signature Validation 410 signedInfoElem = parsedSOAP.dom.xpath('//ds:SignedInfo', 411 explicitNss=SignatureHandler.PROCESSOR_NSS)[0] 412 413 # Get algorithm used for canonicalization of the SignedInfo 414 # element. Nb. This is NOT necessarily the same as that used to 415 # canonicalize the reference elements checked above! 416 signedInfoC14nAlg = c14nMethodElem.getAttributeNS(None, "Algorithm") 417 signedInfoC14nKw = {} 418 if self.signedInfoC14nIsExcl: 419 try: 420 # Check for inclusive namespaces 421 inclusiveNsElem = getElements(c14nMethodElem, 422 "InclusiveNamespaces") 423 if len(inclusiveNsElem) > 0: 424 pfxListAttElem = inclusiveNsElem[0].getAttributeNodeNS(None, 425 'PrefixList') 426 signedInfoC14nKw['inclusivePrefixes' 427 ] = pfxListAttElem.value.split() 428 else: 429 signedInfoC14nKw['inclusivePrefixes'] = None 430 except Exception, e: 431 raise VerifyError('failed to handle exclusive ' 432 'canonicalisation for SignedInfo: %s' % e) 433 434 # Canonicalize the SignedInfo node and take digest 435 f = StringIO() 436 CanonicalPrint(signedInfoElem, 437 stream=f, 438 exclusive=self.signedInfoC14nIsExcl, 439 **signedInfoC14nKw) 440 c14nSignedInfo = f.getvalue() 441 442 signedInfoDigestValue = sha(c14nSignedInfo).digest() 443 444 # Get the signature value in order to check against the digest just 445 # calculated 446 try: 447 signatureValueElem = parsedSOAP.dom.xpath('//ds:SignatureValue', 448 explicitNss=SignatureHandler.PROCESSOR_NSS)[0] 449 except: 450 raise WSSecurityError("No signature value found: %s" % 451 traceback.format_exc()) 452 453 # Remove base 64 encoding 454 b64EncSignatureValue = signatureValueElem.childNodes[0].nodeValue 455 signatureValue = base64.decodestring(b64EncSignatureValue) 456 457 # Cache Signature Value here so that a response can include it. 458 # 459 # Nb. If the sign method is called from a separate SignatureHandler 460 # object then the signature value must be passed from THIS object to 461 # the other SignatureHandler otherwise signature confirmation will 462 # fail 463 if self.applySignatureConfirmation: 464 # re-encode string to avoid possible problems with interpretation 465 # of line breaks 466 self.b64EncSignatureValue = b64EncSignatureValue 467 else: 468 self.b64EncSignatureValue = None 469 470 # Look for X.509 Cert in wsse:BinarySecurityToken node 471 try: 472 binSecTokElem = parsedSOAP.dom.xpath('//wsse:BinarySecurityToken', 473 explicitNss=SignatureHandler.PROCESSOR_NSS)[0] 474 except: 475 # Signature may not have included the Binary Security Token in 476 # which case the verifying cert will need to have been set 477 # elsewhere 478 log.info("No Binary Security Token found in WS-Security header") 479 binSecTokElem = None 480 481 if binSecTokElem: 482 try: 483 x509CertTxt=str(binSecTokElem.childNodes[0].nodeValue) 484 485 valueType = binSecTokElem.getAttributeNS(None, "ValueType") 486 tokValTypes = ( 487 self.__class__.BINARY_SECURITY_TOK_VAL_TYPE['X509v3'], 488 self.__class__.BINARY_SECURITY_TOK_VAL_TYPE['X509']) 489 490 if valueType in tokValTypes: 491 # Remove base 64 encoding 492 derString = base64.decodestring(x509CertTxt) 493 self.verifyingCert = X509Cert.Parse(derString, 494 format=X509Cert.formatDER) 495 x509Stack = X509Stack() 496 497 elif valueType == self.__class__.BINARY_SECURITY_TOK_VAL_TYPE[ 498 'X509PKIPathv1']: 499 500 derString = base64.decodestring(x509CertTxt) 501 x509Stack = X509StackParseFromDER(derString) 502 503 # TODO: Check ordering - is the last off the stack the 504 # one to use to verify the message? 505 self.verifyingCert = x509Stack[-1] 506 else: 507 raise WSSecurityError("BinarySecurityToken ValueType " 508 'attribute is not recognised: "%s"' % 509 valueType) 510 511 except Exception: 512 raise VerifyError("Error extracting BinarySecurityToken " 513 "from WSSE header: %s" % 514 traceback.format_exc()) 515 516 if self.verifyingCert is None: 517 raise VerifyError("No certificate set for verification of the " 518 "signature") 519 520 # Extract RSA public key from the cert 521 rsaPubKey = self.verifyingCert.pubKey.get_rsa() 522 523 # Apply the signature verification 524 try: 525 verify = rsaPubKey.verify(signedInfoDigestValue, signatureValue) 526 except RSA.RSAError, e: 527 raise VerifyError("Error in Signature: " % e) 528 529 if not verify: 530 raise InvalidSignature("Invalid signature") 531 532 # Verify chain of trust 533 x509Stack.verifyCertChain(x509Cert2Verify=self.verifyingCert, 534 caX509Stack=self._caX509Stack) 535 536 self._verifyTimeStamp(parsedSOAP, 537 SignatureHandler.PROCESSOR_NSS, 538 timestampClockSkew=self.timestampClockSkew, 539 timestampMustBeSet=self.timestampMustBeSet, 540 createdElemMustBeSet=self.createdElemMustBeSet, 541 expiresElemMustBeSet=self.expiresElemMustBeSet) 542 log.info("Signature OK") 543 75 544 def _applySignatureConfirmation(self, wsseElem): 76 545 '''Add SignatureConfirmation element - as specified in WS-Security 1.1 … … 230 699 "time %s is before the current time %s." % 231 700 (dtExpiry, dtNow)) 232 233 def sign(self, soapWriter):234 '''Sign the message body and binary security token of a SOAP message235 236 @type soapWriter: ZSI.writer.SoapWriter237 @param soapWriter: ZSI object to write SOAP message238 '''239 240 # Namespaces for XPath searches241 processorNss = \242 {243 'ds': DSIG.BASE,244 'wsu': _WSU.UTILITY,245 'wsse': OASIS.WSSE,246 'soapenv':"http://schemas.xmlsoap.org/soap/envelope/"247 }248 249 # Add X.509 cert as binary security token250 if self.reqBinarySecurityTokValType == \251 self.BINARY_SECURITY_TOK_VAL_TYPE['X509PKIPathv1']:252 if self.signingCertChain is None:253 msg = 'SignatureHandler signingCertChain attribute is not set'254 log.error(msg)255 raise AttributeError(msg)256 257 binSecTokVal = base64.encodestring(self.signingCertChain.asDER())258 else:259 # Assume X.509 / X.509 vers 3260 if self.signingCert is None:261 msg = 'SignatureHandler signingCert attribute is not set'262 log.error(msg)263 raise AttributeError(msg)264 265 binSecTokVal = base64.encodestring(self.signingCert.asDER())266 267 soapWriter._header.setNamespaceAttribute('wsse', OASIS.WSSE)268 soapWriter._header.setNamespaceAttribute('wsse11', OASIS.WSSE11)269 soapWriter._header.setNamespaceAttribute('wsu', _WSU.UTILITY)270 soapWriter._header.setNamespaceAttribute('ds', DSIG.BASE)271 272 # Flag if inclusive namespace prefixes are set for the signature or273 # reference elements274 if self.refC14nPfxSet or self.signedInfoC14nPfxSet:275 soapWriter._header.setNamespaceAttribute('ec', DSIG.C14N_EXCL)276 277 # Check <wsse:security> isn't already present in header278 wsseElems = soapWriter._header.evaluate('//wsse:security',279 processorNss=processorNss)280 if len(wsseElems) > 1:281 raise SignatureError('wsse:Security element is already present')282 283 # Add WSSE element284 wsseElem = soapWriter._header.createAppendElement(OASIS.WSSE,285 'Security')286 wsseElem.setNamespaceAttribute('wsse', OASIS.WSSE)287 288 # Flag to recipient - they MUST parse and check this signature289 wsseElem.setAttributeNS(SOAP.ENV, 'mustUnderstand', "1")290 291 # Binary Security Token element will contain the X.509 cert292 # corresponding to the private key used to sing the message293 binSecTokElem = wsseElem.createAppendElement(OASIS.WSSE,294 'BinarySecurityToken')295 296 # Value type can be any be any one of those supported via297 # BINARY_SECURITY_TOK_VAL_TYPE298 binSecTokElem.setAttributeNS(None,299 'ValueType',300 self.reqBinarySecurityTokValType)301 302 binSecTokElem.setAttributeNS(None,303 'EncodingType',304 self.BINARY_SECURITY_TOK_ENCODING_TYPE)305 306 # Add ID so that the binary token can be included in the signature307 binSecTokElem.setAttributeNS(_WSU.UTILITY, 'Id', "binaryToken")308 309 binSecTokElem.createAppendTextNode(binSecTokVal)310 311 # Timestamp312 if self.addTimestamp:313 self._addTimeStamp(wsseElem)314 315 # Signature Confirmation316 if self.applySignatureConfirmation:317 self._applySignatureConfirmation(wsseElem)318 319 # Signature320 signatureElem = wsseElem.createAppendElement(DSIG.BASE, 'Signature')321 signatureElem.setNamespaceAttribute('ds', DSIG.BASE)322 323 # Signature - Signed Info324 signedInfoElem = signatureElem.createAppendElement(DSIG.BASE,325 'SignedInfo')326 327 # Signed Info - Canonicalization method328 c14nMethodElem = signedInfoElem.createAppendElement(DSIG.BASE,329 'CanonicalizationMethod')330 331 # Set based on 'signedInfoIsExcl' property332 c14nAlgOpt = (DSIG.C14N, DSIG.C14N_EXCL)333 signedInfoC14nAlg = c14nAlgOpt[int(self.signedInfoC14nIsExcl)]334 335 c14nMethodElem.setAttributeNS(None, 'Algorithm', signedInfoC14nAlg)336 337 if self.signedInfoC14nPfxSet:338 c14nInclNamespacesElem = c14nMethodElem.createAppendElement(339 signedInfoC14nAlg,340 'InclusiveNamespaces')341 inclNsPfx = ' '.join(self.signedInfoC14nKw['inclusive_namespaces'])342 c14nInclNamespacesElem.setAttributeNS(None,'PrefixList',inclNsPfx)343 344 # Signed Info - Signature method345 sigMethodElem = signedInfoElem.createAppendElement(DSIG.BASE,346 'SignatureMethod')347 sigMethodElem.setAttributeNS(None, 'Algorithm', DSIG.SIG_RSA_SHA1)348 349 # Signature - Signature value350 signatureValueElem = signatureElem.createAppendElement(DSIG.BASE,351 'SignatureValue')352 353 # Key Info354 KeyInfoElem = signatureElem.createAppendElement(DSIG.BASE, 'KeyInfo')355 secTokRefElem = KeyInfoElem.createAppendElement(OASIS.WSSE,356 'SecurityTokenReference')357 358 # Reference back to the binary token included earlier359 wsseRefElem = secTokRefElem.createAppendElement(OASIS.WSSE,360 'Reference')361 wsseRefElem.setAttributeNS(None, 'URI', "#binaryToken")362 363 # Add Reference to body so that it can be included in the signature364 soapWriter.body.setAttributeNS(_WSU.UTILITY, 'Id', "body")365 366 refElems = soapWriter.body.evaluate('//*[@wsu:Id]',367 processorNss=processorNss)368 369 # Set based on 'signedInfoIsExcl' property370 refC14nAlg = c14nAlgOpt[int(self.refC14nIsExcl)]371 372 # 1) Reference Generation373 #374 # Find references375 for refElem in refElems:376 377 refID = refElem.getAttributeValue(_WSU.UTILITY, 'Id')378 379 # Set URI attribute to point to reference to be signed380 uri = u"#" + refID381 382 # Canonicalize reference383 inclusiveNsKWs = self.createUnsupressedPrefixKW(self.refC14nKw)384 refC14n = refElem.canonicalize(algorithm=refC14nAlg,385 **inclusiveNsKWs)386 387 # Calculate digest for reference and base 64 encode388 #389 # Nb. encodestring adds a trailing newline char390 digestValue = base64.encodestring(sha(refC14n).digest()).strip()391 392 393 # Add a new reference element to SignedInfo394 refElem = signedInfoElem.createAppendElement(DSIG.BASE,395 'Reference')396 refElem.setAttributeNS(None, 'URI', uri)397 398 # Use ds:Transforms or wsse:TransformationParameters?399 transformsElem = refElem.createAppendElement(DSIG.BASE,400 'Transforms')401 transformElem = transformsElem.createAppendElement(DSIG.BASE,402 'Transform')403 404 # Set Canonicalization algorithm type405 transformElem.setAttributeNS(None, 'Algorithm', refC14nAlg)406 if self.refC14nPfxSet:407 # Exclusive C14N requires inclusive namespace elements408 inclNamespacesElem = transformElem.createAppendElement(409 refC14nAlg,410 'InclusiveNamespaces')411 refInclNsPfx = ' '.join(self.refC14nKw['inclusive_namespaces'])412 inclNamespacesElem.setAttributeNS(None, 'PrefixList',413 refInclNsPfx)414 415 # Digest Method416 digestMethodElem = refElem.createAppendElement(DSIG.BASE,417 'DigestMethod')418 digestMethodElem.setAttributeNS(None, 'Algorithm',DSIG.DIGEST_SHA1)419 420 # Digest Value421 digestValueElem = refElem.createAppendElement(DSIG.BASE,422 'DigestValue')423 digestValueElem.createAppendTextNode(digestValue)424 425 426 # 2) Signature Generation427 #428 # Canonicalize the signedInfo node429 signedInfoInclusiveNsKWs = self.createUnsupressedPrefixKW(430 self.signedInfoC14nKw)431 try:432 signedInfoElem = soapWriter.body.evaluate('//ds:SignedInfo',433 processorNss=processorNss)[0]434 except TypeError, e:435 log.error("Error locating SignedInfo element for signature")436 raise437 438 c14nSignedInfo=signedInfoElem.canonicalize(algorithm=signedInfoC14nAlg,439 **signedInfoInclusiveNsKWs)440 # Calculate digest of SignedInfo441 signedInfoDigestValue = sha(c14nSignedInfo).digest()442 443 # Sign using the private key and base 64 encode the result444 signatureValue = self.signingPriKey.sign(signedInfoDigestValue)445 b64EncSignatureValue = base64.encodestring(signatureValue).strip()446 447 # Add to <SignatureValue>448 signatureValueElem.createAppendTextNode(b64EncSignatureValue)449 450 log.info("Signature generation complete")451 452 453 def createUnsupressedPrefixKW(self, dictToConvert):454 """455 Convert a dictionary to use keys with names, 'inclusive_namespaces' in456 place of keys with names 'unsupressedPrefixes'457 NB, this is required for the ZSI canonicalize method458 @type dictToConvert: dict459 @param dictToConvert: dictionary to convert460 @rtype: dict461 @return: dictionary with corrected keys462 """463 nsList = []464 newDict = dictToConvert.copy()465 if isinstance(newDict, dict) and \466 isinstance(newDict.get('inclusive_namespaces'), list):467 nsList = newDict.get('inclusive_namespaces')468 newDict.pop('inclusive_namespaces')469 470 newDict['unsuppressedPrefixes'] = nsList471 return newDict472 473 def verify(self, parsedSOAP, raiseNoSignatureFound=True):474 """Verify signature475 476 @type parsedSOAP: ZSI.parse.ParsedSoap477 @param parsedSOAP: object contain parsed SOAP message received from478 sender"""479 480 processorNss = {481 'ds': DSIG.BASE,482 'wsu': _WSU.UTILITY,483 'wsse': OASIS.WSSE,484 'soapenv':SOAP.ENV485 }486 signatureElem = parsedSOAP.dom.xpath('//ds:Signature',487 explicitNss=processorNss)488 if len(signatureElem) > 1:489 raise VerifyError('Multiple <ds:Signature/> elements found')490 491 try:492 signatureElem = signatureElem[0]493 except IndexError:494 # Message wasn't signed495 msg = "Input message wasn't signed!"496 if raiseNoSignatureFound:497 raise NoSignatureFound(msg)498 else:499 log.warning(msg)500 return501 502 # Two stage process: reference validation followed by signature503 # validation504 505 # 1) Reference Validation506 507 # Check for canonicalization set via ds:CanonicalizationMethod -508 # Use this later as a back up in case no Canonicalization was set in509 # the transforms elements510 try:511 c14nMethodElem=parsedSOAP.dom.xpath('//ds:CanonicalizationMethod',512 explicitNss=processorNss)[0]513 except TypeError, e:514 log.error("XPath query error locating "515 "<ds:CanonicalizationMethod/>: %s" % e)516 raise517 518 refElems = parsedSOAP.dom.xpath('//ds:Reference',519 explicitNss=processorNss)520 521 for refElem in refElems:522 # Get the URI for the reference523 refURI = refElem.getAttributeNS(None, 'URI')524 525 try:526 transformsElem = getElements(refElem, "Transforms")[0]527 transformElems = getElements(transformsElem, "Transform")528 529 refAlgorithm = transformElems[0].getAttributeNS(None,530 'Algorithm')531 except Exception, e:532 raise VerifyError('failed to get transform algorithm for '533 '<ds:Reference URI="%s">' %534 (refURI, e))535 536 # Add extra keyword for Exclusive canonicalization method537 refC14nKw = {}538 refC14nIsExcl = refAlgorithm == DSIG.C14N_EXCL539 if refC14nIsExcl:540 try:541 # Check for no inclusive namespaces set542 inclusiveNS = getElements(transformElems[0],543 "InclusiveNamespaces")544 if len(inclusiveNS) > 0:545 pfxListAttElem=inclusiveNS[0].getAttributeNodeNS(None,546 'PrefixList')547 548 refC14nKw['inclusivePrefixes'] = \549 pfxListAttElem.value.split()550 else:551 refC14nKw['inclusivePrefixes'] = None552 except Exception, e:553 raise VerifyError('failed to handle transform (%s) in '554 '<ds:Reference URI="%s">: %s' % \555 (transformElems[0], refURI, e))556 557 # Canonicalize the reference data and calculate the digest558 if refURI[0] != "#":559 raise VerifyError("Expecting # identifier for Reference URI "560 "\"%s\"" % refURI)561 562 # XPath reference563 uriXPath = '//*[@wsu:Id="%s"]' % refURI[1:]564 uriElem = parsedSOAP.dom.xpath(uriXPath,565 explicitNss=processorNss)[0]566 567 f = StringIO()568 CanonicalPrint(uriElem, stream=f, exclusive=refC14nIsExcl,569 **refC14nKw)570 refC14n = f.getvalue()571 digestValue = base64.encodestring(sha(refC14n).digest()).strip()572 573 # Extract the digest value that was stored574 digestNode = getElements(refElem, "DigestValue")[0]575 nodeDigestValue = str(digestNode.childNodes[0].nodeValue).strip()576 577 # Reference validates if the two digest values are the same578 if digestValue != nodeDigestValue:579 raise InvalidSignature('Digest Values do not match for URI: '580 '"%s"' % refURI)581 582 log.debug("Verified canonicalization for element %s" % refURI[1:])583 584 # 2) Signature Validation585 signedInfoElem = parsedSOAP.dom.xpath('//ds:SignedInfo',586 explicitNss=processorNss)[0]587 588 # Get algorithm used for canonicalization of the SignedInfo589 # element. Nb. This is NOT necessarily the same as that used to590 # canonicalize the reference elements checked above!591 signedInfoC14nAlg = c14nMethodElem.getAttributeNS(None, "Algorithm")592 signedInfoC14nKw = {}593 signedInfoC14nIsExcl = signedInfoC14nAlg == DSIG.C14N_EXCL594 if signedInfoC14nIsExcl:595 try:596 # Check for inclusive namespaces597 inclusiveNsElem = getElements(c14nMethodElem,598 "InclusiveNamespaces")599 if len(inclusiveNsElem) > 0:600 pfxListAttElem=inclusiveNsElem[0].getAttributeNodeNS(None,601 'PrefixList')602 signedInfoC14nKw['inclusivePrefixes'] = \603 pfxListAttElem.value.split()604 else:605 signedInfoC14nKw['inclusivePrefixes'] = None606 except Exception, e:607 raise VerifyError('failed to handle exclusive '608 'canonicalisation for SignedInfo: %s' % e)609 610 # Canonicalize the SignedInfo node and take digest611 f = StringIO()612 CanonicalPrint(signedInfoElem,613 stream=f,614 exclusive=signedInfoC14nIsExcl,615 **signedInfoC14nKw)616 c14nSignedInfo = f.getvalue()617 618 signedInfoDigestValue = sha(c14nSignedInfo).digest()619 620 # Get the signature value in order to check against the digest just621 # calculated622 signatureValueElem = parsedSOAP.dom.xpath('//ds:SignatureValue',623 explicitNss=processorNss)[0]624 625 # Remove base 64 encoding626 b64EncSignatureValue = signatureValueElem.childNodes[0].nodeValue627 signatureValue = base64.decodestring(b64EncSignatureValue)628 629 # Cache Signature Value here so that a response can include it.630 #631 # Nb. If the sign method is called from a separate SignatureHandler632 # object then the signature value must be passed from THIS object to633 # the other SignatureHandler otherwise signature confirmation will634 # fail635 if self.applySignatureConfirmation:636 # re-encode string to avoid possible problems with interpretation637 # of line breaks638 self.b64EncSignatureValue = b64EncSignatureValue639 else:640 self.b64EncSignatureValue = None641 642 # Look for X.509 Cert in wsse:BinarySecurityToken node643 try:644 binSecTokElem = parsedSOAP.dom.xpath('//wsse:BinarySecurityToken',645 explicitNss=processorNss)[0]646 except:647 # Signature may not have included the Binary Security Token in648 # which case the verifying cert will need to have been set649 # elsewhere650 log.info("No Binary Security Token found in WS-Security header")651 binSecTokElem = None652 653 if binSecTokElem:654 try:655 x509CertTxt=str(binSecTokElem.childNodes[0].nodeValue)656 657 valueType = binSecTokElem.getAttributeNS(None, "ValueType")658 if valueType in (self.__class__.BINARY_SECURITY_TOK_VAL_TYPE['X509v3'],659 self.__class__.BINARY_SECURITY_TOK_VAL_TYPE['X509']):660 # Remove base 64 encoding661 derString = base64.decodestring(x509CertTxt)662 self.verifyingCert = X509Cert.Parse(derString,663 format=X509Cert.formatDER)664 x509Stack = X509Stack()665 666 elif valueType == \667 self.__class__.BINARY_SECURITY_TOK_VAL_TYPE['X509PKIPathv1']:668 669 derString = base64.decodestring(x509CertTxt)670 x509Stack = X509StackParseFromDER(derString)671 672 # TODO: Check ordering - is the last off the stack the673 # one to use to verify the message?674 self.verifyingCert = x509Stack[-1]675 else:676 raise WSSecurityError("BinarySecurityToken ValueType "677 'attribute is not recognised: "%s"' %678 valueType)679 680 except Exception, e:681 raise VerifyError("Error extracting BinarySecurityToken "682 "from WSSE header: %s" % e)683 684 if self.verifyingCert is None:685 raise VerifyError("No certificate set for verification of the "686 "signature")687 688 # Extract RSA public key from the cert689 rsaPubKey = self.verifyingCert.pubKey.get_rsa()690 691 # Apply the signature verification692 try:693 verify = rsaPubKey.verify(signedInfoDigestValue, signatureValue)694 except RSA.RSAError, e:695 raise VerifyError("Error in Signature: " % e)696 697 if not verify:698 raise InvalidSignature("Invalid signature")699 700 # Verify chain of trust701 x509Stack.verifyCertChain(x509Cert2Verify=self.verifyingCert,702 caX509Stack=self._caX509Stack)703 704 self._verifyTimeStamp(parsedSOAP,705 processorNss,706 timestampClockSkew=self.timestampClockSkew,707 timestampMustBeSet=self.timestampMustBeSet,708 createdElemMustBeSet=self.createdElemMustBeSet,709 expiresElemMustBeSet=self.expiresElemMustBeSet)710 log.info("Signature OK") -
TI12-security/trunk/WSSecurity/ndg/wssecurity/test/unit/signaturehandler/foursuite/server/echoServer.py
r5291 r6399 15 15 from EchoService_services_server import EchoService as _EchoService 16 16 17 from ndg. security.common.wssecurity.signaturehandler.dom import SignatureHandler17 from ndg.wssecurity.common.signaturehandler import SignatureHandlerFactory 18 18 19 19 from os.path import expandvars as xpdVars … … 118 118 # Create the Inherited version of the server 119 119 echo = EchoService() 120 echo.signatureHandler = SignatureHandler(cfg=wsseCfgFilePath) 120 echo.signatureHandler = SignatureHandlerFactory.fromConfigFile( 121 wsseCfgFilePath) 121 122 122 123 serviceContainer.setNode(echo, url=path) -
TI12-security/trunk/WSSecurity/ndg/wssecurity/test/unit/signaturehandler/foursuite/server/wssecurity.cfg
r5560 r6399 9 9 # BSD - See LICENCE file for details 10 10 # 11 # TODO: Refactor option names - put into inbound and outbound sections / apply12 # namespace prefixes to better categorise13 11 [DEFAULT] 12 className = ndg.wssecurity.common.signaturehandler.foursuite.SignatureHandler 14 13 15 14 #
Note: See TracChangeset
for help on using the changeset viewer.