Index: trunk/rp/common/python/infocard/infocardlib.py
===================================================================
--- trunk/rp/common/python/infocard/infocardlib.py (revision 1104)
+++ trunk/rp/common/python/infocard/infocardlib.py (revision 1104)
@@ -0,0 +1,459 @@
+#  Copyright (c) 2007 Novell, Inc.
+#  All Rights Reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public License as
+#  published by the Free Software Foundation; version 2.1 of the license.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with this library; if not, contact Novell, Inc.
+#
+#  To contact Novell about this file by physical or electronic mail,
+#  you may find current contact information at www.novell.com
+#
+#  This file incorporates work covered by the following copyright and  
+#   permission notice:  
+#
+#    This file originated from Robert Richards CDATA blog,
+#    downloaded from  http://www.cdatazone.org/index.php?/pages/source.html
+#    http://www.cdatazone.org/files/pubdomain.tar.gz
+#    According to blog entry 
+#	 http://www.cdatazone.org/index.php?/archives/26-Catching-Up.html
+#	 posted Thursday, July 5. 2007 the libraries have been released under a 
+#	 bsd style license.
+#
+#	 Copyright (c) 2007, Robert Richards <rrichards@ctindustries.net>.
+#     All rights reserved.
+#
+#     Redistribution and use in source and binary forms, with or without
+#     modification, are permitted provided that the following conditions
+#     are met:
+#
+#     Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#
+#     * Neither the name of Robert Richards nor the names of his
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+#     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+#     COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+#     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+#     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+#     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+#     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+#     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+#     ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#     POSSIBILITY OF SUCH DAMAGE.
+   
+import sys, traceback
+import xmlseclibs, cookielib, datetime
+import hashlib
+import urlparse
+from xml.dom import minidom
+from xml import xpath
+
+import xml.utils.iso8601
+
+import event
+from event import Event, BasicEventLog
+
+""" public namespace identifiers for different token types
+"""
+SAML_1_0_ASSERT_NS = 'urn:oasis:names:tc:SAML:1.0:assertion'
+SAML_1_1_ASSERT_NS = 'http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1'
+SAML_2_0_ASSERT_NS = 'urn:oasis:names:tc:SAML:2.0:assertion'
+
+""" public identifiers for meta data about the token
+"""
+META_AssertionID = 'AssertionID'
+META_Audience = 'Audience'
+META_NotBefore = 'NotBefore'
+META_NotOnOrAfter = 'NotOnOrAfter'
+META_Issuer = 'Issuer' 
+META_IssueInstant = 'IssueInstant'
+META_MajorVersion = 'MajorVersion'
+META_MinorVersion = 'MinorVersion'
+META_CardKeyHash = 'CardKeyHash'
+
+""" public identifiers for options which can or must be set on the 
+infocard processor
+"""
+OPTION_CryptoKey = 'cryptoKey'
+OPTION_CryptoKeyPass = 'cryptoKeyPass'
+OPTION_CryptoKeyIsFile = 'cryptoKeyIsFile'
+OPTION_CryptoKeyIsCert = 'cryptoKeyIsCert'
+
+""" space delimited lists of claims
+"""
+OPTION_required_claims = 'required_claims'
+OPTION_optional_claims = 'optional_claims'
+OPTION_multivalued_claims = 'multivalued_claims'
+
+""" options which may reduce security by ignoring or downgrading some checks.
+    some options may need to be set for interoperablity
+"""
+OPTION_check_required_claims = 'check_required_claims'
+
+
+class InfoCardProcessor:
+	
+	def __init__(self):
+		self.options = {}
+		
+	def set_decode(self, privateKey, passPhrase = None, isFile=False, isCert = True):
+		if isFile:
+			fp = open(privateKey, 'rb')
+			privateKey = fp.read()
+			fp.close()
+			isFile = False
+		self.options[OPTION_CryptoKey] = privateKey
+		self.options[OPTION_CryptoKeyPass] = passPhrase
+		self.options[OPTION_CryptoKeyIsFile] = isFile
+		self.options[OPTION_CryptoKeyIsCert] = isCert
+		
+			
+	def set_claims(self, required, optional = None, multivalued = None):
+		self.options[OPTION_required_claims] = required
+		self.options[OPTION_optional_claims] = optional
+		self.options[OPTION_multivalued_claims] = multivalued
+		
+	def set_options(self, options):
+		for name in options.keys():
+			self.options[name] = options[name]
+	
+	def _fix_options(self):
+		if not self._option(OPTION_check_required_claims):
+			self.options[OPTION_check_required_claims] = True
+		
+		
+	def _option(self, name):
+		try:
+			return self.options[name]
+		except Exception:
+			return None
+			
+	def process_token(self, xmlToken = None):
+		self._fix_options()
+		
+		#parse the token
+		secToken = SecToken()
+		if secToken:
+			secToken.process_token(xmlToken, self.options)
+					
+		if secToken and secToken.isValid:
+			#todo check for required claims, or extra claims as feature is enabled
+			if self._option(OPTION_check_required_claims) and self._option(OPTION_required_claims):
+				rClaims = self._option(OPTION_required_claims).rsplit(' ')
+				for claim in rClaims:
+					pass
+					
+		return secToken
+		
+		
+class SecToken:
+
+
+	def __init__(self):
+		self.crypted = None			#Raw security token as delivered from the client
+		self.cryptedDom = None		#Raw security token as delivered from the client
+									# and parsed into dom
+		self.decrypted = None		#decrypted token
+		self.isValid = False		#boolean, is security token valid
+		self.objKeyInfo = None		#internal object
+		self.assertions = {}		#array of assertions/claims
+		self.metadata = {}			#array of misc data about the token, such as 
+									#token type
+		self.eventLog = BasicEventLog()
+
+	def _option(self, options, option):
+		try:
+			return options[option]
+		except Exception:
+			return None
+			
+	def process_token(self, xmlToken, options):
+		self.crypted = xmlToken
+		try:
+			objenc = xmlseclibs.XMLSecEnc(self.eventLog)
+			self.cryptedDom = minidom.parseString(self.crypted)
+			encData = objenc.locateEncryptedData(self.cryptedDom)
+			if not encData:
+				self.eventLog.add_event("Cannot locate encrypted"\
+					"data in security token", event.FATAL, 'parse')
+			else:
+				objenc.setNode(encData)
+				objenc.type = encData.getAttribute("Type")
+		
+				key = None
+				objKey = objenc.locateKey()
+				if (objKey):
+					self.objKeyInfo = objenc.locateKeyInfo(objKey)
+					if (self.objKeyInfo):
+						if (self.objKeyInfo.isEncrypted):
+							objencKey = self.objKeyInfo.encryptedCtx
+							self.objKeyInfo.loadKey(
+								self._option(options, OPTION_CryptoKey), 
+								self._option(options, OPTION_CryptoKeyPass),
+								self._option(options, OPTION_CryptoKeyIsFile), 
+								self._option(options, OPTION_CryptoKeyIsCert))
+							key = objencKey.decryptKey(self.objKeyInfo)
+		
+				if (not objKey) or (not key):
+					self.eventLog.add_event("Error loading key to handle"\
+					"Decryption", event.FATAL, 'parse')
+				else:
+					objKey.loadKey(key)
+					
+					decrypt = objenc.decryptNode(objKey, False)
+					if decrypt:
+						# we have the saml token so load er up
+						self.decrypted = minidom.parseString(decrypt)
+						if not self.decrypted:
+							self.eventLog.add_event('Unable to parse decrypted token',
+								event.FATAL, 'parse')
+						else:
+							#do validity checking
+							self.isValid = self._isValid(options)
+							#grab assertions/claims
+							self._setupAssertions(options)
+							#set up metadata about the token for later use
+							self._setupMiscData(options)
+					else:
+						self.eventLog.add_event('Unable to decrypt token',
+								event.FATAL, 'decrypt')
+			if not self.isValid:
+				self.eventLog.add_event("SecToken did not pass all valididation requirements", 
+					event.ERROR, 'info')
+		except Exception, why:
+			trace = traceback.format_exception(*sys.exc_info())
+			traceString = '\n '.join(trace)
+			errorMsg = "ERROR:\n%s\n" % (traceString)
+			self.eventLog.add_event("Exception encountered while processing Security Token, exception: %s " % (str(errorMsg)), 
+					event.FATAL, 'exception')
+
+	"""Setup all of the data about the token we can 
+	"""
+	def _setupMiscData(self, options):
+		rootNode = self.decrypted.documentElement
+		if rootNode:
+			self.metadata[META_Issuer] = rootNode.getAttribute('Issuer')
+			self.metadata[META_IssueInstant] = rootNode.getAttribute('IssueInstant')
+			self.metadata[META_AssertionID] = rootNode.getAttribute('AssertionID')
+			self.metadata[META_MajorVersion] = rootNode.getAttribute('MajorVersion')
+			self.metadata[META_MinorVersion] = rootNode.getAttribute('MinorVersion')
+		self.metadata[META_CardKeyHash] = self._getCardKeyHash()
+
+	def _isValid(self, options):
+		""" Validate the SAML token
+		"""
+		
+		objXMLSecDSig = xmlseclibs.XMLSecurityDSig(self.eventLog)
+		objDSig = objXMLSecDSig.locateSignature(self.decrypted)
+		if not objDSig:
+			self.eventLog.add_event("SAML Signature Location Failed",
+				event.ERROR, 'locate-saml-signature')
+
+		# Canonicalize the signed info
+		objXMLSecDSig.canonicalizeSignedInfo()
+
+		retVal = objXMLSecDSig.validateReference()
+		if not retVal:
+			self.eventLog.add_event("SAML Reference Validation Failed",
+				event.ERROR, 'validate-saml-reference')
+			return False
+
+		key = None
+		objKey = objXMLSecDSig.locateKey()
+		if objKey:
+			self.objKeyInfo = xmlseclibs.XMLSecEnc.staticLocateKeyInfo(objKey, 
+									objDSig, self.eventLog)
+			if (self.objKeyInfo):
+				# Handle any additional key processing such as encrypted keys here
+#				self.eventLog.add_event("Potential additional key processing required: %s, %u" \
+#					% (self.objKeyInfo.type, objKey.isEncrypted),
+#					event.INFO)
+				pass
+		else:
+			self.eventLog.add_event("Error locating/loading key to handle"\
+				"Signature", event.ERROR, 'decrypt-loadkey')
+			return False
+
+		retVal = objXMLSecDSig.verify(objKey)
+		if not retVal:
+			self.eventLog.add_event("Unable to verify Signature",
+				event.ERROR, 'verify-signature')
+			return False
+
+		#validate Conditions, both Audience and Time
+		xPath = xpath.CreateContext(self.decrypted)
+		xPath.setNamespaces({'mysaml' : SAML_1_0_ASSERT_NS})
+
+		query = '/mysaml:Assertion/mysaml:Conditions/mysaml:AudienceRestrictionCondition/mysaml:Audience'
+		nodelist = xpath.Evaluate(query, context=xPath)
+		for node in nodelist:
+			self.metadata[META_Audience] = node.firstChild.data
+
+		query = '/mysaml:Assertion/mysaml:Conditions'
+		nodelist = xpath.Evaluate(query, context=xPath)
+		if nodelist and nodelist[0]:
+			node = nodelist[0]
+			self.metadata[META_NotBefore] = node.getAttribute('NotBefore')
+			self.metadata[META_NotOnOrAfter] = node.getAttribute('NotOnOrAfter')
+
+			if ((not self.metadata[META_NotBefore]) or 
+				(not self.metadata[META_NotOnOrAfter])):
+				self.eventLog.add_event("Security Token does not contain "\
+					"both NotBefore and NotOnOrAfter conditions", event.ERROR, 
+					'validate-time-conditions-present')
+				return False
+			if (not checkDateConditions(self.metadata[META_NotBefore], self.metadata[META_NotOnOrAfter])):
+				currentTime = datetime.datetime.utcnow()
+				self.eventLog.add_event(
+					'Token range [%s - %s] does not include current time %s' % (
+					self.metadata[META_NotBefore], self.metadata[META_NotOnOrAfter],
+					currentTime.isoformat()),
+				event.ERROR, 'validate-time-conditions-inrange')
+				return False
+		if self.eventLog.has_severity(event.ERROR):
+			return False
+
+		return True
+
+
+	#TODO handle namespaces, claim name mapping, multiple values!
+	def _setupAssertions(self, options):
+		if not self.assertions or len(self.assertions) == 0:
+			mvClaims = []
+			temp = self._option(options, OPTION_multivalued_claims)
+			if temp:
+				mvClaims = temp.rsplit(' ')
+			xPath = xpath.CreateContext(self.decrypted)
+			xPath.setNamespaces({'mysaml' : SAML_1_0_ASSERT_NS})
+			query = '/mysaml:Assertion/mysaml:AttributeStatement/mysaml:Attribute'
+			nodelist = xpath.Evaluate(query, context=xPath)
+
+			for node in nodelist:
+				ns = node.getAttribute('AttributeNamespace')
+				name = node.getAttribute('AttributeName')
+				if (name):
+					value = ''
+					for child in node.childNodes:
+						if (child.localName == 'AttributeValue'):
+							value = child.firstChild.data
+							try:
+								nsDict = self.assertions[ns]
+								try:
+									if nsDict[name]:
+#										self.eventLog.add_event("Ignoring multi-valued %s/%s : %s" \
+#										% (ns, name, value), 
+#										event.INFO, 'parse-assertions')
+										#TODO multivalued check
+										pass
+								except Exception:
+#									self.eventLog.add_event("Adding %s/%s : %s" \
+#									% (ns, name, value), 
+#									event.INFO, 'parse-assertions')
+									nsDict[name] = value
+							except Exception:
+#								self.eventLog.add_event("First NS, adding %s/%s : %s" \
+#								% (ns, name, value), 
+#								event.INFO, 'parse-assertions')
+								self.assertions[ns] = {name:value}
+
+	def getAssertion(self, identifier=None):
+		"""Allows retrivial of any claim or assertion associated with the security token
+		Returns either the data or None
+		visit and finish this function!
+		"""
+		#is the identifier a URI or just a short name?
+		ns = None
+		claim = None
+		try:
+			if identifier:
+				s = identifier.rsplit('/', 1)
+				if len(s) > 1:
+					ns = s[0]
+				claim = s[-1]
+		except Exception:
+			claim = identifier
+
+		claims = dict()
+		for nsElem in self.assertions.keys():
+			if not ns or (ns and ns == nsElem):
+				nsDict = self.assertions[nsElem]
+				for key in nsDict.keys():
+					if claim and claim == key:
+						return nsDict[key]
+					elif not claim:
+						if ns:
+							claims[ns+'//'+key] = nsDict[key]
+						else:
+							claims[key] = nsDict[key]
+		if claims.items():
+			return claims
+		else:
+			return None
+
+
+	def getMetaData(self, identifier=None):
+		"""Allows retrivial of any meta data associated with the security token
+		Pass a specific string for the identifier and receive either a 
+		string or None
+		If the identifier is None then a dictionary of all meta data is returned
+		Currently all meta data is single valued!
+		"""
+		if identifier:
+			try:
+				return self.metadata[identifier]
+			except Exception:
+				return None
+		else:
+			return self.metadata
+		
+	def _getCardKeyHash(self):
+		try:
+			return self.metadata[META_CardKeyHash]
+		except Exception:
+			m = hashlib.md5()
+			signer = None
+#			ppid = self.getAssertion('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier')
+			ppid = self.getAssertion('privatepersonalidentifier')
+			if ppid:
+				m.update(ppid)
+			if self.objKeyInfo and self.objKeyInfo.isEncrypted:
+				if self.objKeyInfo.modulus:
+					m.update(self.objKeyInfo.modulus)
+				if self.objKeyInfo.exponent:
+					m.update(self.objKeyInfo.exponent)
+			self.metadata[META_CardKeyHash] = m.hexdigest()
+			return self.metadata[META_CardKeyHash]
+		
+# TODO: need work on ISO date checking - skip for now
+def checkDateConditions(start=None, end=None, difference=300):
+	currentTime = datetime.datetime.now()
+	if (start):
+		startTime = datetime.datetime.fromtimestamp(xml.utils.iso8601.parse(start))
+		# Allow for a 5 minute difference in Time
+		d = datetime.timedelta(minutes=difference)
+		if ((not startTime) or ((startTime - d) > currentTime)):
+			return False
+
+	if (end):
+		endTime = datetime.datetime.fromtimestamp(xml.utils.iso8601.parse(end))
+		if ((not endTime) or (endTime <= currentTime)):
+			return False
+	return True
Index: trunk/rp/common/python/infocard/__init__.py
===================================================================
--- trunk/rp/common/python/infocard/__init__.py (revision 1104)
+++ trunk/rp/common/python/infocard/__init__.py (revision 1104)
@@ -0,0 +1,1 @@
+
Index: trunk/rp/common/python/infocard/event.py
===================================================================
--- trunk/rp/common/python/infocard/event.py (revision 1104)
+++ trunk/rp/common/python/infocard/event.py (revision 1104)
@@ -0,0 +1,66 @@
+#  Copyright (c) 2007 Novell, Inc.
+#  All Rights Reserved.
+ 
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public License as
+#  published by the Free Software Foundation; version 2.1 of the license.
+ 
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+#  GNU Lesser General Public License for more details.
+ 
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with this library; if not, contact Novell, Inc.
+ 
+#  To contact Novell about this file by physical or electronic mail,
+#  you may find current contact information at www.novell.com
+
+import logging
+
+CRITICAL = logging.CRITICAL                                                                    
+DEBUG = logging.DEBUG                                                                  
+ERROR = logging.ERROR                                                                    
+FATAL = logging.FATAL                                                                    
+INFO = logging.INFO                                                       
+NOTSET = logging.NOTSET
+
+class IEventLog:
+	"""An interface for managing related events, including inline filtering
+	"""
+	
+	
+	def add_event( eid, text, level):
+		"""Add an event to the queue, filtering as needed
+		"""
+		
+
+class BasicEventLog:
+#	implements(IEventLog)
+	
+	def __init__(self):
+		self.events = [] #array of errors, warnings, and other significant events
+		
+	def add_event(self, text, severity=NOTSET, tag=None):
+		event = Event(text, severity, tag)
+		self.events.append(event)
+		
+	def has_severity(self, severity):
+		count = 0
+		for event in self.events:
+			if event.severity >= severity:
+				count += count
+		return count
+			
+class Event:
+	text = None
+	tag = None
+	severity = NOTSET
+	#todo add a datetime
+	
+	def __init__(self, text, severity=NOTSET, tag=None):
+		self.text = text
+		self.severity = severity
+		self.tag = tag
+		
+	
Index: trunk/rp/common/python/infocard/xmlseclibs.py
===================================================================
--- trunk/rp/common/python/infocard/xmlseclibs.py (revision 1104)
+++ trunk/rp/common/python/infocard/xmlseclibs.py (revision 1104)
@@ -0,0 +1,1093 @@
+#  Copyright (c) 2007 Novell, Inc.
+#  All Rights Reserved.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public License as
+#  published by the Free Software Foundation; version 2.1 of the license.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with this library; if not, contact Novell, Inc.
+#
+#  To contact Novell about this file by physical or electronic mail,
+#  you may find current contact information at www.novell.com
+#
+#  This file incorporates work covered by the following copyright and  
+#   permission notice:  
+#
+#    This file originated from Robert Richards CDATA blog,
+#    downloaded from  http://www.cdatazone.org/index.php?/pages/source.html
+#    http://www.cdatazone.org/files/pubdomain.tar.gz
+#    According to blog entry 
+#	 http://www.cdatazone.org/index.php?/archives/26-Catching-Up.html
+#	 posted Thursday, July 5. 2007 the libraries have been released under a 
+#	 bsd style license.
+#
+#	 Copyright (c) 2007, Robert Richards <rrichards@ctindustries.net>.
+#     All rights reserved.
+#
+#     Redistribution and use in source and binary forms, with or without
+#     modification, are permitted provided that the following conditions
+#     are met:
+#
+#     Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#
+#     * Neither the name of Robert Richards nor the names of his
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+#     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+#     COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+#     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+#     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+#     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+#     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+#     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+#     ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#     POSSIBILITY OF SUCH DAMAGE.
+#   
+   
+import base64
+from Crypto.Hash import SHA, SHA256, RIPEMD
+from Crypto.Cipher import AES, DES3
+from M2Crypto import EVP, RSA, util, BIO, X509, m2
+from xml.dom import minidom
+import xml.dom.ext.c14n
+from xml import xpath
+from urlparse import urlparse
+
+import event
+from event import Event, BasicEventLog
+
+def report_event(eventLog, text, severity = event.NOTSET, tag = None):
+	if eventLog:
+		eventLog.add_event(text, severity, tag)
+	elif severity >= event.ERROR:
+		raise Exception(text)
+	
+
+class XMLSecurityKey:
+	TRIPLEDES_CBC = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc'
+	AES128_CBC = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc'
+	AES192_CBC = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc'
+	AES256_CBC = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc'
+	RSA_1_5 = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'
+	RSA_OAEP_MGF1P = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'
+	RSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
+	DSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#dsa-sha1'
+
+	def __init__(self, type, eventLog = None, params=None):
+		self.type = 0
+		self.key = None
+		self.passphrase = ""
+		self.eventLog = eventLog
+		self.iv = None
+		self.name = None
+		self.keyChain = None
+		self.isEncrypted = False
+		self.encryptedCtx = None
+		self.cryptParams = {'mode':None, 'cipher':None, 'padding':None, 'type':None, 'hash':None}
+		if (type == XMLSecurityKey.TRIPLEDES_CBC):
+				self.cryptParams['library'] = 'mcrypt'
+				self.cryptParams['cipher'] = DES3
+				self.cryptParams['mode'] = DES3.MODE_CBC
+				self.cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc'
+		elif (type == XMLSecurityKey.AES128_CBC):
+				self.cryptParams['library'] = 'mcrypt'
+				self.cryptParams['cipher'] = AES
+				self.cryptParams['mode'] = AES.MODE_CBC
+				self.cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc'
+				self.cryptParams['type'] = 'AES'
+		elif (type == XMLSecurityKey.AES192_CBC):
+				self.cryptParams['library'] = 'mcrypt'
+				self.cryptParams['cipher'] = AES
+				self.cryptParams['mode'] = AES.MODE_CBC
+				self.cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc'
+				self.cryptParams['type'] = 'AES'
+		elif (type == XMLSecurityKey.AES256_CBC):
+				self.cryptParams['library'] = 'mcrypt'
+				self.cryptParams['cipher'] = AES
+				self.cryptParams['mode'] = AES.MODE_CBC
+				self.cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc'
+				self.cryptParams['type'] = 'AES'
+		elif (type == XMLSecurityKey.RSA_1_5):
+				self.cryptParams['library'] = 'openssl'
+				self.cryptParams['padding'] = RSA.pkcs1_padding
+				self.cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'
+				if isinstance(params, dict):
+					if (params['type'] == 'public' or params['type'] == 'private'):
+						self.cryptParams['type'] = params['type']
+		elif (type == XMLSecurityKey.RSA_OAEP_MGF1P):
+				self.cryptParams['library'] = 'openssl';
+				self.cryptParams['padding'] = RSA.pkcs1_oaep_padding;
+				self.cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p';
+				self.cryptParams['hash'] = None;
+				if isinstance(params, dict):
+					if (params['type'] == 'public' or params['type'] == 'private'):
+						self.cryptParams['type'] = params['type']
+		elif (type == XMLSecurityKey.RSA_SHA1):
+				self.cryptParams['library'] = 'openssl'
+				self.cryptParams['method'] = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
+				if isinstance(params, dict):
+					if (params['type'] == 'public' or params['type'] == 'private'):
+						self.cryptParams['type'] = params['type']
+		else:
+				return None
+		self.type = type
+		self.modulus =None
+		self.exponent = None
+		return None
+
+#	def generateSessionKey(self):
+#		key = ''
+#		if (len(self.cryptParams['cipher']) > 0 and len(self.cryptParams['mode']) > 0):
+#			keysize = mcrypt_module_get_algo_key_size(self.cryptParams['cipher'])
+#			# Generating random key using iv generation routines
+#			if ((keysize > 0) and (td = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '',self.cryptParams['mode'], ''))):
+#				if (self.cryptParams['type'] == 'AES'):
+#					keysize = 16
+#					if self.type == XMLSecurityKey.AES256_CBC:
+#						keysize = 32
+#					elif self.type == XMLSecurityKey.AES192_CBC:
+#						keysize = 24
+#				while (len(key) < keysize):
+#					key += mcrypt_create_iv(mcrypt_enc_get_iv_size (td),MCRYPT_RAND);
+#				mcrypt_module_close(td)
+#				key = substr(key, 0, keysize)
+#				self.key = key
+#		return key
+
+
+ 	def loadKey(self, key, passPhrase = None, isFile=False, isCert = True):
+		if isFile:
+			fp = open(key, 'rb')
+			key = fp.read()
+			fp.close()
+		self.passphrase = passPhrase
+
+		if (self.cryptParams['library'] == 'openssl'):
+			if (self.cryptParams['type'] == 'public'):
+				if isCert:
+					self.key = X509.load_cert_string(key).get_pubkey()
+				else:
+					bio = BIO.MemoryBuffer(key)
+					self.key = EVP.PKey()
+					self.key.assign_rsa(RSA.load_pub_key_bio(bio))
+			else:
+				if isCert:
+					self.key = RSA.load_key_string(key)
+				else:
+					if self.passphrase == None:
+						self.key = RSA.load_key_string(key)
+					else:
+						self.key = RSA.load_key_string(key, self.passphrase_callback)
+		elif (self.cryptParams['type'] == 'AES'):
+			# Check key length
+			if (self.type == XMLSecurityKey.AES256_CBC):
+				if (len(key) < 25):
+					report_event(self.eventLog, "XMLSecurityKey.loadkey for  "\
+						"%s::%s cipher too short %u, should be at least 25 characters" \
+						% (self.cryptParams['library'], self.cryptParams['type'], len(key)),
+						event.ERROR, 'cipher-size-invalid')
+			elif (self.type == XMLSecurityKey.AES192_CBC):
+				if (len(key) < 17):
+					report_event(self.eventLog, "XMLSecurityKey.loadkey for  "\
+						"%s::%s cipher too short %u, should be at least 25 characters" \
+						% (self.cryptParams['library'], self.cryptParams['type'], len(key)),
+						event.ERROR, 'cipher-size-invalid')
+			self.key = key
+			
+		if not self.key:
+			report_event(self.eventLog, "XMLSecurityKey.loadkey for  %s::%s no key loaded" \
+				% (self.cryptParams['library'], self.cryptParams['type']),
+				event.ERROR, 'unsupported-key-load')
+		
+		return None
+
+	#TODO: Convert encryptMcrypt
+	def encryptMcrypt(self, data):
+		td = mcrypt_module_open(self.cryptParams['cipher'], '', self.cryptParams['mode'], '')
+		self.iv = mcrypt_create_iv (mcrypt_enc_get_iv_size(td), MCRYPT_RAND)
+		mcrypt_generic_init(td, self.key, self.iv)
+		encrypted_data = self.iv.mcrypt_generic(td, data)
+		mcrypt_generic_deinit(td)
+		mcrypt_module_close(td)
+		return encrypted_data
+
+	def decryptMcrypt(self, data):
+		dCipher = self.cryptParams['cipher']
+		iv_length = dCipher.block_size
+		self.iv = data[0:iv_length]
+		data = data[iv_length:len(data)]
+		
+		#if (self.cryptParams['mode'] == MCRYPT_MODE_CBC):
+		# For now only CBC supported so remove test
+		pad = len(data) % iv_length
+		data += '0'*(iv_length - pad)
+		#End CBC only code
+
+		td = dCipher.new(self.key, self.cryptParams['mode'], self.iv)
+		decrypted_data = td.decrypt(data)
+
+		if (not decrypted_data):
+			return None
+
+		#if (self.cryptParams['mode'] == MCRYPT_MODE_CBC):
+		# For now only CBC supported so remove test
+		#first remove any padding we have added
+		dataLen = len(decrypted_data)
+		newdataLen = dataLen-iv_length+pad
+		decrypted_data = decrypted_data[0:newdataLen]
+		#remove any CBC padding
+		paddingLength = decrypted_data[newdataLen-1:newdataLen]
+		decrypted_data = decrypted_data[0:newdataLen - ord(paddingLength)]
+		#End CBC only code
+
+		return decrypted_data
+
+	#TODO: Convert encryptOpenSSL
+	def encryptOpenSSL(self, data):
+		if (self.cryptParams['type'] == 'public'):
+			if (not openssl_public_encrypt(data, encrypted_data, self.key, self.cryptParams['padding'])):
+				# throw new Exception('Failure encrypting Data')
+				return None
+		else:
+			if (not openssl_private_encrypt(data, encrypted_data, self.key, self.cryptParams['padding'])):
+				# throw new Exception('Failure encrypting Data');
+				return None
+		return encrypted_data
+
+	def decryptOpenSSL(self, data):
+		if (self.cryptParams['type'] == 'public'):
+			decrypted = self.key.public_decrypt(data, self.cryptParams['padding'])
+			if (not decrypted):
+				# throw new Exception('Failure decrypting Data')
+				print 'Failure decrypting Data'
+				return None
+		else:
+			decrypted = self.key.private_decrypt(data, self.cryptParams['padding'])
+			if (not decrypted):
+				# throw new Exception('Failure decrypting Data')
+				print 'Failure decrypting Data'
+				return None
+		return decrypted
+
+	def signOpenSSL(self, data):
+		pKey = self.key
+		pKey.sign_init()
+		pKey.sign_update(data)
+		return pKey.sign_final()
+
+	def verifyOpenSSL(self, data, signature):
+		pKey = self.key
+		pKey.verify_init()
+		pKey.verify_update(data)
+		return m2.verify_final(pKey.ctx, signature, pKey.pkey)
+		
+
+	def encryptData(self, data):
+		if (self.cryptParams['library'] == 'mcrypt'):
+			return self.encryptMcrypt(data)
+		elif (self.cryptParams['library'] == 'openssl'):
+			return self.encryptOpenSSL(data)
+		else:
+			return None
+
+	def decryptData(self, data):
+		if (self.cryptParams['library'] == 'mcrypt'):
+			return self.decryptMcrypt(data)
+		elif (self.cryptParams['library'] == 'openssl'):
+			return self.decryptOpenSSL(data)
+		else:
+			return None
+
+	def signData(self, data):
+		if (self.cryptParams['library'] == 'openssl'):
+			return self.signOpenSSL(data)
+		return None
+
+	def verifySignature(self, data, signature):
+		if (self.cryptParams['library'] == 'openssl'):
+			return self.verifyOpenSSL(data, signature)
+
+	def getAlgorith(self):
+		return self.cryptParams['method']
+
+	def makeAsnSegment(type, instring):
+		if (type == 0x02):
+			if (ord(instring[0]) > 0x7f):
+				instring = chr(0)+instring
+		elif (type == 0x03):
+			instring = chr(0)+instring
+	
+		length = len(instring)
+
+		if (length < 128):
+		   output = "%c%c%s" % (type, length, instring)
+		elif (length < 0x0100):
+		   output = "%c%c%c%s" % (type, 0x81, length, instring)
+		elif (length < 0x010000):
+		   output = "%c%c%c%c%s" % (type, 0x82, length/0x0100, length%0x0100, instring);
+		else:
+			output = None
+
+		return output
+
+	# Modulus and Exponent must already be base64 decoded
+	def convertRSA(modulus, exponent):
+		# make an ASN publicKeyInfo
+		exponentEncoding = XMLSecurityKey.makeAsnSegment(0x02, exponent)
+		modulusEncoding = XMLSecurityKey.makeAsnSegment(0x02, modulus)
+		sequenceEncoding = XMLSecurityKey. makeAsnSegment(0x30, modulusEncoding+exponentEncoding)
+		bitstringEncoding = XMLSecurityKey.makeAsnSegment(0x03, sequenceEncoding)
+		rsaAlgorithmIdentifier = util.h2b("300D06092A864886F70D0101010500")
+		publicKeyInfo = XMLSecurityKey.makeAsnSegment(0x30, rsaAlgorithmIdentifier + bitstringEncoding)
+
+		# encode the publicKeyInfo in base64 and add PEM brackets
+		publicKeyInfoBase64 = base64.b64encode(publicKeyInfo);    
+		encoding = "-----BEGIN PUBLIC KEY-----\n"
+		offset = 0
+		segment= publicKeyInfoBase64[offset:offset+64]
+		while(segment):
+		   encoding = encoding + segment + "\n"
+		   offset += 64
+		   segment=publicKeyInfoBase64[offset: offset+64]
+
+		return encoding + "-----END PUBLIC KEY-----\n"
+	
+	def serializeKey(self, parent):
+		return None
+
+	makeAsnSegment = staticmethod(makeAsnSegment)
+	convertRSA = staticmethod(convertRSA)
+
+	def passphrase_callback(*args):
+		return self.passphrase
+	
+class XMLSecurityDSig:
+	XMLDSIGNS = 'http://www.w3.org/2000/09/xmldsig#'
+	SHA1 = 'http://www.w3.org/2000/09/xmldsig#sha1'
+	SHA256 = 'http://www.w3.org/2001/04/xmlenc#sha256'
+	SHA512 = 'http://www.w3.org/2001/04/xmlenc#sha512'
+	RIPEMD160 = 'http://www.w3.org/2001/04/xmlenc#ripemd160'
+
+	C14N = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315'
+	C14N_COMMENTS = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments'
+	EXC_C14N = 'http://www.w3.org/2001/10/xml-exc-c14n#'
+	EXC_C14N_COMMENTS = 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments'
+
+	template = '<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">\
+  <ds:SignedInfo>\
+    <ds:SignatureMethod />\
+  </ds:SignedInfo>\
+</ds:Signature>'
+	
+	def __init__(self, eventLog = None):
+		
+		self.eventLog = eventLog
+		self.idKeys = {}
+		self.idNS = {}
+		self.signedInfo = None
+		self.xPathCtx = None
+		self.canonicalMethod = None
+		self.prefix = 'ds'
+		self.searchpfx = 'secdsig'
+
+		sigdoc = minidom.parseString(XMLSecurityDSig.template)
+		self.sigNode = sigdoc.documentElement
+
+	def getXPathObj(self):
+		if (not self.xPathCtx and self.sigNode):
+			xPath = xpath.CreateContext(self.sigNode)
+			xPath.setNamespaces({'secdsig' : XMLSecurityDSig.XMLDSIGNS})
+			self.xPathCtx = xPath
+		return self.xPathCtx
+
+	# TODO: Convert generate_GUID
+	def generate_GUID(prefix=None):
+		uuid = md5(uniqid(rand(), True))
+		guid =  prefix.substr(uuid,0,8)+"-" + \
+				substr(uuid,8,4)+"-" + \
+				substr(uuid,12,4)+"-" + \
+				substr(uuid,16,4)+"-" + \
+				substr(uuid,20,12)
+		return guid
+
+	def locateSignature(self, objDoc):
+		if (objDoc.nodeType == objDoc.DOCUMENT_NODE):
+			doc = objDoc
+		else:
+			doc = objDoc.ownerDocument
+		if doc:
+			xPath = xpath.CreateContext(doc)
+			xPath.setNamespaces({'secdsig' : XMLSecurityDSig.XMLDSIGNS})
+			query = ".//secdsig:Signature"
+			nodeset = xpath.Evaluate(query, contextNode=objDoc, context=xPath)
+			if nodeset:
+				self.sigNode = nodeset[0]
+				return self.sigNode;
+		return None
+
+	def createNewSignNode(self, name, value=None):
+		doc = self.sigNode.ownerDocument
+		if (not is_null(value)):
+			node = doc.newChild(XMLSecurityDSig.XMLDSIGNS, self.prefix+':'+name, value)
+		else:
+			node = doc.newChild(XMLSecurityDSig.XMLDSIGNS, self.prefix+':'+name)
+
+		return node
+
+	def setCanonicalMethod(self, method):
+		if (method == 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315' or \
+			method == 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments' or \
+			method == 'http://www.w3.org/2001/10/xml-exc-c14n#' or \
+			method == 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments'):
+				self.canonicalMethod = method
+				return True
+		else:
+				return False
+
+		xpath = self.getXPathObj()
+		if (xpath):
+			query = './' + self.searchpfx + ':SignedInfo'
+			nodeset = xpath.xpathEval(query)
+			sinfo = nodeset[0]
+			if (sinfo):
+				query = './' + self.searchpfx + 'CanonicalizationMethod'
+				xpath.setContextNode(sinfo)
+				nodeset = xpath.xpathEval(query)
+				canonNode = nodeset-[0]
+				if (not canonNode):
+					canonNode = self.createNewSignNode('CanonicalizationMethod')
+					sinfo.insertBefore(canonNode, sinfo.firstChild)
+				canonNode.setAttribute('Algorithm', self.canonicalMethod)
+
+	def canonicalizeData(self, node, canonicalmethod, 
+			inclusiveNameSpacePrefixes=None):
+		exclusive = 0
+		withComments = 0
+		if (canonicalmethod == 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315'):
+			exclusive = 0;
+			withComments = 0;
+		elif (canonicalmethod == 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments'):
+			withComments = 1;
+		elif (canonicalmethod == 'http://www.w3.org/2001/10/xml-exc-c14n#'):
+			exclusive = 1;
+		elif (canonicalmethod == 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments'):
+			exclusive = 1;
+			withComments = 1;
+		else:
+			report_event(self.eventLog, "canonicalizeData requested for " \
+				"unsupported algorithm : %s" % (canonicalmethod),
+				event.ERROR, 'calculate-canonical')
+
+		if (exclusive == 1):
+			return xml.dom.ext.c14n.Canonicalize(node, None, comments=withComments, 
+				unsuppressedPrefixes=inclusiveNameSpacePrefixes)
+		else:
+			return xml.dom.ext.c14n.Canonicalize(node, None, comments=withComments)
+
+	def canonicalizeSignedInfo(self):
+		doc = self.sigNode.ownerDocument
+		canonicalmethod = None
+		if (doc):
+			xPath = self.getXPathObj()
+			query = "./secdsig:SignedInfo"
+			nodeset = xpath.Evaluate(query, contextNode=self.sigNode, context=xPath)
+			signInfoNode = nodeset[0]
+			if (signInfoNode):
+				canonNode = signInfoNode.firstChild
+				while (canonNode and \
+				(canonNode.localName != 'CanonicalizationMethod') and \
+				(canonNode.namespaceURI != XMLSecurityDSig.XMLDSIGNS)):
+					canonNode = canonNode.nextSibling
+				if (canonNode):
+					canonicalmethod = canonNode.getAttribute('Algorithm')
+				self.signedInfo = self.canonicalizeData(signInfoNode, canonicalmethod)
+				return self.signedInfo
+		return None
+
+	def calculateDigest (self, digestAlgorithm, data):
+		if (digestAlgorithm == XMLSecurityDSig.SHA1):
+			#alg = 'sha1'
+			alg = SHA.new()
+		elif (digestAlgorithm == XMLSecurityDSig.SHA256):
+			#alg = 'sha256'
+			alg = SHA256.new()
+#		elif (digestAlgorithm == XMLSecurityDSig.SHA512):
+			#not implemented yet
+			#alg = 'sha512'
+#			return None
+		elif (digestAlgorithm == XMLSecurityDSig.RIPEMD160):
+			#alg = 'ripemd160'
+			alg = RIPEMD.new()
+		else:
+			report_event(self.eventLog, "Digest Calcluation requested for " \
+				"unsupported algorithm : %s" % (digestAlgorithm),
+				event.ERROR, 'calculate-digest')
+			return None
+		alg.update(data)
+		hashed = alg.digest()
+		return base64.b64encode(hashed)
+
+	def validateDigest(self, refNode, data, dataObject):
+		xPath = xpath.CreateContext(refNode)
+		xPath.setNamespaces({'secdsig' : XMLSecurityDSig.XMLDSIGNS})
+		query = 'string(./secdsig:DigestMethod/@Algorithm)'
+		digestAlgorithm = xpath.Evaluate(query, contextNode=refNode, context=xPath)
+		digValue = self.calculateDigest(digestAlgorithm, data)
+		query = 'string(./secdsig:DigestValue)'
+		digestValue = xpath.Evaluate(query, contextNode=refNode, context=xPath)
+		if digValue != digestValue:
+			report_event(self.eventLog, "%s Digest Validation calculated \""\
+				"%s\" != \"%s\"\nCalculated: %s\nSent: %s" % (digestAlgorithm, 
+				digValue, digestValue, data, dataObject.toxml()), 
+				event.ERROR, 'validate-digest')
+			report_event(self.eventLog, "calculated on untransformed data %s" % (
+				self.calculateDigest(digestAlgorithm, dataObject.toxml())),
+				event.INFO, 'validate-digest')
+		return (digValue == digestValue)
+
+	def processTransforms(self, signatureNode, signedDataNode):
+		transformedData = signedDataNode
+		xPath = xpath.CreateContext(signatureNode)
+		xPath.setNamespaces({'secdsig' : XMLSecurityDSig.XMLDSIGNS})
+		query = './secdsig:Transforms/secdsig:Transform'
+		nodelist = xpath.Evaluate(query, contextNode=signatureNode, context=xPath)
+		canonicalMethod = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315'
+		for i in range(len(nodelist)):
+			transform = nodelist[i]
+			algorithm = transform.getAttribute("Algorithm")
+			if (algorithm == 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315' or \
+			algorithm == 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments' or \
+			algorithm == 'http://www.w3.org/2001/10/xml-exc-c14n#' or \
+			algorithm == 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments'):
+				canonicalMethod = algorithm
+				break
+				
+		inclusiveNameSpacePrefixes = None
+		nodelist = signatureNode.getElementsByTagNameNS( '*', 'InclusiveNamespaces')
+		for node in nodelist:
+			inclusiveNameSpacePrefixes = node.getAttribute("PrefixList").split(' ')
+		if inclusiveNameSpacePrefixes == None or inclusiveNameSpacePrefixes == []:
+				report_event(self.eventLog, "Signature doesn't have the" \
+					" InclusiveNamespaces element,"\
+					" by not including this element your chosen IdP puts you"\
+					" at risk of a brown bag attack!",
+				event.INFO, 'signature-missing-InclusiveNameSpaces')
+				
+		if (isinstance(signedDataNode, minidom.Node)):
+			transformedData = self.canonicalizeData(signedDataNode, \
+				canonicalMethod, inclusiveNameSpacePrefixes)
+				
+		return transformedData
+
+	def processRefNode(self, refNode):
+		signedObject = None
+		uri = refNode.getAttribute("URI")
+		if (uri):
+			arUrl = urlparse(uri)
+			if (not arUrl[2]):
+				identifier = arUrl[5]
+				if (identifier):
+					if (self.idNS and isinstance(seld.idNS, dict)):
+						for nspf in self.idNS.keys():
+							ns = self.idNS[nspf]
+							xPath.xpathRegisterNs(nspf, ns)
+					#dfb todo: why was this origially @Id?  that didn't always work
+					iDlist = '@*="' + identifier + '"'
+					if isinstance(self.idKeys, dict):
+						for key in self.idKeys.keys():
+							idKey = self.idKeys[key]
+							iDlist = iDlist + " or @" + idKey + "='" +  identifier + "'"
+					query = '//*[' + iDlist + ']'
+					res = xpath.Evaluate(query, contextNode=refNode.ownerDocument)
+					if res:
+						signedObject = res[0]
+					else:
+						report_event(self.eventLog, "Problem Validating reference, query : %s" % (query), 
+							event.ERROR, 'validate-reference-location')
+						signedObject = refNode.ownerDocument
+				else:
+					signedObject = refNode.ownerDocument
+			else:
+				signedObject = file_get_contents(arUrl)
+		else:
+			signedObject = refNode.ownerDocument
+		canonicalData = self.processTransforms(refNode, signedObject)
+		if not canonicalData:
+			report_event(self.eventLog, "Problem Processing Transforms for node: %s" % (signedObject.toXML()), 
+					event.INFO, 'validate-reference-transform')
+		return self.validateDigest(refNode, canonicalData, signedObject)
+
+	def validateReference(self):
+		doc = self.sigNode.ownerDocument
+		if (doc != self.sigNode):
+			self.sigNode.parentNode.removeChild(self.sigNode)
+		xPath = self.getXPathObj()
+		query = "./secdsig:SignedInfo/secdsig:Reference"
+		nodeset = xpath.Evaluate(query, contextNode=self.sigNode, context=xPath)
+		if (not nodeset):
+			return None
+		for i in range(len(nodeset)):
+			refNode = nodeset[i]
+			if (not self.processRefNode(refNode)):
+				report_event(self.eventLog, "Error Validating reference node : %s" % (refNode.toprettyxml('   ', '\n')))
+				return None
+
+		return True
+
+	def addRefInternal(self, sinfoNode, node, algorithm, arTransforms=None, options=None):
+		prefix = None
+		prefix_ns = None
+		if isinstance(options, dict):
+			prefix = options['prefix']
+			prefix_ns = options['prefix_ns']
+			id_name = options['id_name']
+
+		refNode = self.createNewSignNode('Reference')
+		sinfoNode.appendChild(refNode)
+
+		if (node.nodeType == node.DOCUMENT_NODE):
+			uri = None
+		else:
+# Do we really need to set a prefix?
+			uri = XMLSecurityDSig.generate_GUID()
+			refNode.setAttribute("URI", '#' + uri)
+
+		transNodes = self.createNewSignNode('Transforms')
+		refNode.appendChild(transNodes)
+
+		if isinstance(arTransforms, dict):
+			for i in range(len(arTransforms)):
+				transform = nodelist[i]
+				transNode = self.createNewSignNode('Transform')
+				transNodes.appendChild(transNode)
+				transNode.setAttribute('Algorithm', transform)
+		elif (not empty(self.canonicalMethod)):
+			transNode = self.createNewSignNode('Transform')
+			transNodes.appendChild(transNode)
+			transNode.setAttribute('Algorithm', self.canonicalMethod)
+		
+		if (uri):
+			attname = id_name
+			if (prefix):
+				attname = prefix + ':' + attname
+			node.setAttributeNS(prefix_ns, attname, uri)
+		
+		canonicalData = self.processTransforms(refNode, node);
+		digValue = self.calculateDigest(algorithm, canonicalData);
+		
+		digestMethod = self.createNewSignNode('DigestMethod');
+		refNode.appendChild(digestMethod);
+		digestMethod.setAttribute('Algorithm', algorithm);
+
+		digestValue = self.createNewSignNode('DigestValue', digValue)
+		refNode.appendChild(digestValue)
+
+	def addReference(self, node, algorithm, arTransforms=None, options=None):
+		xpath = self.getXPathObj()
+		if (xpath):
+			query = "./secdsig:SignedInfo"
+			nodeset = xpath.xpathEval(query)
+			sInfo = nodeset[0]
+			if (sInfo):
+				self.addRefInternal(sInfo, node, algorithm, arTransforms, options)
+
+	def addReferenceList(self, arNodes, algorithm, arTransforms=None, options=None):
+		xpath = self.getXPathObj()
+		if (xpath):
+			query = "./secdsig:SignedInfo"
+			nodeset = xpath.xpathEval(query)
+			sInfo = nodeset[0]
+			if (sInfo):
+				for i in range(len(sInfo)):
+					node = sInfo[i]
+					self.addRefInternal(sInfo, node, algorithm, arTransforms, options)
+
+	def locateKey(self, node=None):
+		if (not node):
+			node = self.sigNode
+		if (not isinstance(node, minidom.Node)):
+			return None
+		doc = node.ownerDocument
+		if (doc):
+			xPath = xpath.CreateContext(node)
+			xPath.setNamespaces({'secdsig' : XMLSecurityDSig.XMLDSIGNS})
+			query = "string(./secdsig:SignedInfo/secdsig:SignatureMethod/@Algorithm)";
+			algorithm = xpath.Evaluate(query, contextNode=node, context=xPath)
+			if (algorithm):
+#				try {
+				objKey = XMLSecurityKey(algorithm, self.eventLog, {'type':'public'})
+#				} catch (Exception e) {
+#					return NULL;
+#				}
+				return objKey
+		return None
+	
+	def verify(self, objKey):
+		xPath = xpath.CreateContext(self.sigNode)
+		xPath.setNamespaces({'secdsig' : XMLSecurityDSig.XMLDSIGNS})
+		query = "string(./secdsig:SignatureValue)"
+		sigValue = xpath.Evaluate(query, contextNode=self.sigNode, context=xPath)
+		if (not sigValue):
+			return None
+		return objKey.verifySignature(self.signedInfo, base64.b64decode(sigValue))
+	
+	def signData(self, objKey, data):
+		return objKey.signData(data)
+	
+	def sign(self, objKey):
+		xpath = self.getXPathObj()
+		if (xpath):
+			query = "./secdsig:SignedInfo";
+			nodeset = xpath.xpathEval(query)
+			sInfo = nodeset[0]
+			if (sInfo):
+				query = "./secdsig:SignatureMethod"
+				nodeset = xpath.xpathEval(query)
+				sMethod = nodeset[0]
+				sMethod.setAttribute('Algorithm', objKey.type)
+				data = self.canonicalizeData(sInfo, self.canonicalMethod)
+				sigValue = base64.b64encode(self.signData(objKey, data))
+				sigValueNode = self.createNewSignNode('SignatureValue', sigValue)
+				infoSibling = sInfo.nextSibling
+				if (infoSibling):
+					infoSibling.parent.insertBefore(sigValueNode, infoSibling)
+				else:
+					self.sigNode.appendChild(sigValueNode)
+
+	def appendCert(self):
+		return None
+
+	def appendKey(self, objKey, parent=None):
+		objKey.serializeKey(parent)
+
+	def appendSignature(self, parent, insertBefore = False):
+		if (parent.nodeType == parent.DOCUMENT_NODE):
+			baseDoc = parent
+		else:
+			parent.ownerDocument
+		newSig = baseDoc.importNode(self.sigNode, True)
+		if (insertBefore):
+			parent.insertBefore(newSig, parent.firstChild)
+		else:
+			parent.appendChild(newSig)
+
+	# TODO: Convert get509XCert
+	def get509XCert(cert, isPEMFormat=True):
+		if (isPEMFormat):
+			data = ''
+			arCert = explode("\n", cert)
+			inData = False
+			for i in range(len(arCert)):
+				curData = arCert[i]
+				if (not inData) :
+					if (strncmp(curData, '-----BEGIN CERTIFICATE', 22) == 0):
+						inData = True
+				else:
+					if (strncmp(curData, '-----END CERTIFICATE', 20) == 0):
+						break
+					data += trim(curData)
+		else:
+			data = cert
+		return data
+	
+	def add509Cert(self, cert, isPEMFormat=True):
+		data = XMLSecurityDSig.get509XCert(cert, isPEMFormat)
+		xpath = self.getXPathObj()
+		if (xpath):
+			query = "./secdsig:KeyInfo"
+			nodeset = xpath.xpathEval(query)
+			keyInfo = nodeset[0]
+			if (not keyInfo):
+				inserted = False
+				keyInfo = self.createNewSignNode('KeyInfo')
+				query = "./secdsig:Object"
+				nodeset = xpath.xpathEval(query)
+				sObject = nodeset[0]
+				if (sObject):
+					sObject.parent.insertBefore(keyInfo, sObject)
+					inserted = True
+				if (not inserted):
+					self.sigNode.appendChild(keyInfo)
+
+			x509DataNode = self.createNewSignNode('X509Data')
+			keyInfo.appendChild(x509DataNode)
+			x509CertNode = self.createNewSignNode('X509Certificate', data)
+			x509DataNode.appendChild(x509CertNode)
+
+class XMLSecEnc:
+
+
+	Element = 'http://www.w3.org/2001/04/xmlenc#Element'
+	Content = 'http://www.w3.org/2001/04/xmlenc#Content'
+	URI = 3
+	XMLENCNS = 'http://www.w3.org/2001/04/xmlenc#'
+
+	template = "<xenc:EncryptedData xmlns:xenc='http://www.w3.org/2001/04/xmlenc#'>\
+   <xenc:CipherData>\
+      <xenc:CipherValue></xenc:CipherValue>\
+   </xenc:CipherData>\
+</xenc:EncryptedData>"
+	
+	def __init__(self, eventLog = None):
+		self.eventLog = eventLog
+		self.rawNode = None
+		self.type = None
+		self.encdoc = minidom.parseString(XMLSecEnc.template)
+	
+	def setNode(self, node):
+		self.rawNode = node
+
+	def encryptNode(self, objKey, replace=True):
+		data = ''
+		if (not self.rawNode):
+#			throw new Exception('Node to encrypt has not been set')
+			return None
+
+		doc = self.rawNode.ownerDocument
+		xPath = self.encdoc.xpathNewContext()
+		objList = xPath.xpathEval('/xenc:EncryptedData/xenc:CipherData/xenc:CipherValue')
+		cipherValue = objList[0]
+		xPath.xpathFreeContext()
+		if (cipherValue == None):
+			#throw new Exception('Error locating CipherValue element within template')
+			return None
+
+		if (self.type == XMLSecEnc.Element):
+			data = doc.saveXML(self.rawNode)
+			self.encdoc.getRootElement().setAttribute('Type', XMLSecEnc.Element)
+		elif (self.type == XMLSecEnc.Content):
+			children = self.sawNode.childNodes
+			for i in range(len(children)):
+				data += doc.saveXML(children[i])
+			self.encdoc.getRootElement().setAttribute('Type', XMLSecEnc.Content)
+		else:
+				#throw new Exception('Type is currently not supported')
+				return None
+		
+		encMethod = self.encdoc.getRootElement().appendChild(self.encdoc.createElementNS(XMLSecEnc.XMLENCNS, 'xenc:EncryptionMethod'))
+		encMethod.setAttribute('Algorithm', objKey.getAlgorith())
+		cipherValue.parent.parent.insertBefore(encMethod, cipherValue.parent)
+		
+		strEncrypt = base64.b64encode(objKey.encryptData(data))
+		value = self.encdoc.createTextNode(strEncrypt)
+		cipherValue.appendChild(value)
+		
+		if (replace):
+			if (self.type == XMLSecEnc.Element):
+				if (self.rawNode.nodeType == self.rawNode.DOCUMENT_NODE):
+					return self.encdoc
+				importEnc = self.rawNode.ownerDocument.importNode(self.encdoc.getRootElement(), True)
+				self.rawNode.parent.replaceChild(importEnc, self.rawNode)
+				return importEnc
+			elif (self.type == XMLSecEnc.Content):
+				importEnc = self.rawNode.ownerDocument.importNode(self.encdoc.getRootElement(), True)
+				while(self.rawNode.firstChild):
+					self.rawNode.firstChild.unlinkNode()
+
+				self.rawNode.appendChild(importEnc)
+
+	def decryptNode(self, objKey, replace=True):
+		data = ''
+		if (not self.rawNode):
+			#throw new Exception('Node to decrypt has not been set')
+			print 'Node to decrypt has not been set'
+			return None
+		doc = self.rawNode.ownerDocument
+		xPath = xpath.CreateContext(self.rawNode)
+		xPath.setNamespaces({'xmlencr' : XMLSecEnc.XMLENCNS})
+		query = "string(./xmlencr:CipherData/xmlencr:CipherValue)"
+		encryptedData = xpath.Evaluate(query, context=xPath)
+		if (encryptedData):
+			encryptedData = base64.b64decode(encryptedData)
+			decrypted = objKey.decryptData(encryptedData)
+			if (replace):
+				if (self.type == XMLSecEnc.Element):
+					newdoc = minidom.parseString(decrypted)
+					if (self.rawNode.nodeType == newdoc.DOCUMENT_NODE):
+						return newdoc
+
+					importEnc = self.rawNode.ownerDocument.importNode(newdoc.getRootElement(), True)
+					self.rawNode.parent.replaceChild(importEnc, self.rawNode)
+					return importEnc
+				elif (self.type == XMLSecEnc.Content):
+					if (self.rawNode.nodeType == self.rawNode.DOCUMENT_NODE):
+						doc = self.rawNode
+					else:
+						doc = self.rawNode.ownerDocument
+					newFrag = doc.createDOMDocumentFragment()
+					newFrag.appendXML(decrypted)
+					self.rawNode.parent.replaceChild(newFrag, self.rawNode)
+					return self.rawNode.parent
+			return decrypted
+		else:
+			#throw new Exception("Cannot locate encrypted data")
+			return None
+
+	def encryptKey(self, srcKey, rawKey, append=True):
+		strEncKey = base64.b64encode(srcKey.encryptData(rawKey.key))
+		root = self.encdoc.getRootElement()
+		encKey = self.encdoc.createElementNS(XMLSecEnc.XMLENCNS, 'xenc:EncryptedKey')
+		if (append):
+			keyInfo = root.appendChild(self.encdoc.createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo'))
+			keyInfo.appendChild(encKey)
+
+		encMethod = encKey.appendChild(self.encdoc.createElementNS(XMLSecEnc.XMLENCNS, 'xenc:EncryptionMethod'))
+		encMethod.setAttribute('Algorithm', srcKey.getAlgorith())
+		if (srcKey.name):
+			keyInfo = encKey.appendChild(self.encdoc.createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo'))
+			keyInfo.appendChild(self.encdoc.createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyName', srcKey.name))
+
+		cipherData = encKey.appendChild(self.encdoc.createElementNS(XMLSecEnc.XMLENCNS, 'xenc:CipherData'))
+		cipherData.appendChild(self.encdoc.createElementNS(XMLSecEnc.XMLENCNS, 'xenc:CipherValue', strEncKey))
+		return None
+	
+	def decryptKey(self, encKey):
+		if (not encKey.isEncrypted):
+			#throw new Exception("Key is not Encrypted")
+			return None
+		if (not encKey.key):
+			#throw new Exception("Key is missing data to perform the decryption")
+			return None
+		return self.decryptNode(encKey, False)
+	
+	def locateEncryptedData(self, element):
+		if (element.nodeType == element.DOCUMENT_NODE):
+			doc = element
+		else:
+			doc = element.ownerDocument
+		if (doc):
+			query = "//*[local-name()='EncryptedData' and namespace-uri()='" + XMLSecEnc.XMLENCNS + "']"
+			nodeset = xpath.Evaluate(query, doc)
+			return nodeset[0]
+		return None
+	
+	def locateKey(self, node=None):
+		if (not node):
+			node = self.rawNode
+		if (not isinstance(node,minidom.Node)):
+			return None
+	
+		xPath = xpath.CreateContext(node)
+		xPath.setNamespaces({'xmlsecenc' : XMLSecEnc.XMLENCNS})
+		query = ".//xmlsecenc:EncryptionMethod";
+		nodeset = xpath.Evaluate(query, contextNode=node, context=xPath)
+		encmeth = nodeset[0]
+		if (encmeth):
+			attrAlgorithm = encmeth.getAttribute("Algorithm")
+			return XMLSecurityKey(attrAlgorithm, self.eventLog, {'type':'private'})
+
+		return None
+	
+	def staticLocateKeyInfo(objBaseKey=None, node=None, eventLog=None):
+		"""
+			Todo: eleminiate side effects
+		"""
+		if (not isinstance(node, minidom.Node)):
+			return None
+			
+		xPath = xpath.CreateContext(node)
+		xPath.setNamespaces({'xmlsecdsig' : XMLSecurityDSig.XMLDSIGNS, 'xmlsecenc' : XMLSecEnc.XMLENCNS})
+		query = "./xmlsecdsig:KeyInfo";
+		nodeset = xpath.Evaluate(query, contextNode=node, context=xPath)
+		encmeth = nodeset[0]
+		if (encmeth):
+			for child in encmeth.childNodes:
+				if (child.localName == 'KeyName'):
+					if (objBaseKey):
+						objBaseKey.localName = child.firstChild.data
+				elif (child.localName == 'KeyValue'):
+					for keyval in child.childNodes:
+						if (keyval.localName == 'DSAKeyValue'):
+							report_event(eventLog, "XMLSecEnc.staticLocateKeyinfo KeyInfo DSAKeyValue not supported",
+								event.ERROR, 'unsupported-DSAKeyValue')
+						elif (keyval.localName == 'RSAKeyValue'):
+							objBaseKey.modulus = None
+							objBaseKey.exponent = None
+							modulusNode = keyval.getElementsByTagNameNS(XMLSecurityDSig.XMLDSIGNS, 'Modulus')
+							if (modulusNode):
+								objBaseKey.modulus = base64.b64decode(modulusNode[0].firstChild.data)
+							exponentNode = keyval.getElementsByTagNameNS(XMLSecurityDSig.XMLDSIGNS, 'Exponent')
+							if (exponentNode):
+								objBaseKey.exponent = base64.b64decode(exponentNode[0].firstChild.data)
+							if ((not objBaseKey.modulus) or (not objBaseKey.exponent)):
+								report_event(eventLog, "XMLSecEnc.staticLocateKeyinfo"\
+									" missing Modulus or Exponent on RSAKeyValue ",
+									event.ERROR, 'invalid-digitalsig-missing')
+							publicKey = XMLSecurityKey.convertRSA(objBaseKey.modulus, objBaseKey.exponent)
+							objBaseKey.loadKey(publicKey)
+				elif (child.localName == 'RetrievalMethod'):
+					# Not currently supported
+					report_event(eventLog, "XMLSecEnc.staticLocateKeyinfo KeyInfo RetrievalMethod not supported",
+						event.ERROR, 'unsupported-retrievalMethod')
+				elif (child.localName == 'EncryptedKey'):
+					objenc = XMLSecEnc(eventLog)
+					objenc.setNode(child)
+					objKey = objenc.locateKey()
+					if (not objKey):
+						report_event(eventLog, "XMLSecEnc.staticLocateKeyinfo"\
+							" KeyInfo EncryptedKey "\
+							"Unable to locate algorithm for this Encrypted Key",
+							event.ERROR, 'unsupported-retrievalMethod')
+					objKey.isEncrypted = True;
+					objKey.encryptedCtx = objenc
+					XMLSecEnc.staticLocateKeyInfo(objKey, child)
+					return objKey
+				elif (child.localName == 'X509Data'):
+					x509certNodes = child.getElementsByTagNameNS('*', 'X509Certificate')
+					if (x509certNodes and (len(x509certNodes) > 0)):
+						count = 0;
+						while count < len(x509certNodes) :
+							x509Cert = x509certNodes[count].firstChild.data
+							if x509Cert:
+								objBaseKey.loadKey(_string_to_certstring(x509Cert))
+								break
+							count = count + 1
+					else:
+						#heh, this feels like a hack and a half  
+						objBaseKey.loadKey(_string_to_certstring(
+							child.firstChild.firstChild.data))
+				else:
+					report_event(eventLog, "XMLSecEnc.staticLocateKeyinfo"\
+						" KeyInfo %s is unsupported from %s" % (child.localName,
+							child.toxml()),	event.INFO, 'unsupported-keyinfo')
+			return objBaseKey
+		else:
+			report_event(eventLog, "XMLSecEnc.staticLocateKeyinfo"\
+						" Nothing to see here",
+						event.INFO)
+		return None
+
+	def locateKeyInfo(self, objBaseKey=None, node=None):
+		if (not node):
+			node = self.rawNode
+		return XMLSecEnc.staticLocateKeyInfo(objBaseKey, node, self.eventLog)
+
+	staticLocateKeyInfo = staticmethod(staticLocateKeyInfo)
+	
+def _string_to_certstring(body):
+	newStr = "-----BEGIN CERTIFICATE-----\n"
+	temp = body.encode('utf-8').replace("\r", "\n")
+	start = 0
+	end = len(temp)
+	chunk_size = 63
+	while start < end:
+		newStr += temp[start:start+chunk_size-1] + "\n"
+		start += chunk_size
+	newStr +="-----END CERTIFICATE-----\n"
+	return newStr
