OpenSAML and Apache Santuario (xmlsec) 1.5.1

Stephanie Stroka stephanie.stroka at adnovum.ch
Fri Apr 13 15:48:00 BST 2012


I found out that the problem is not in the registration of the id. The 
document holds identifiers that point to its Elements, e.g.:

{_1ac9178abf0f99838fdcc574fea92179=[xenc:EncryptedKey: null], 
Assertion_13253b842c2883ef5260df3bf0cc201013e9ac3d=[saml2:Assertion: 
null], _cdf68c16b77735622330aca8b2e310ab=[xenc:EncryptedData: null], 
Response_d52b993993f8cc6a004d3df24581d97f101d0e90=[saml2p:Response: null]}

But the resolving process fails when I use 
doc.getElementById("Assertion_13253b842c2883ef5260df3bf0cc201013e9ac3d")

I printed the document shortly before 
org.apache.xml.security.utils.resolver.implementations.ResolverFragment 
requests the "element by id" (doc.getElementById(id) line 84 in 
ResolverFragment). According to the identifier I should see a tag called 
<saml2:Assertion> .. but it does not exist. Instead there are 
<EncrptedAssertion> tags
Is it possible that something went wrong during the decryption process?

Here are the two methods that decrypt the XMLObject:

public XMLObject decryptWhereNeccessary(XMLObject obj) throws Exception {
   if (obj instanceof EncryptedElementType) {
     obj = decrypt(obj);
   }

   if (obj.getOrderedChildren() != null) {
     for (XMLObject child : obj.getOrderedChildren()) {
       if (child == null) continue;
         XMLObject newChild = decryptWhereNeccessary(child);
       if (newChild == child) continue;

       if (newChild instanceof Assertion && obj instanceof Response && 
child instanceof EncryptedAssertion) {
 
((org.opensaml.saml2.core.Response)obj).getEncryptedAssertions().remove(child);
 
((org.opensaml.saml2.core.Response)obj).getAssertions().add((Assertion) 
newChild);
       }				
       else if (newChild instanceof NameID) {				
         obj.getClass().getMethod("setEncryptedID", new Class[] 
{EncryptedID.class}).invoke(obj, new Object[]{null});
         obj.getClass().getMethod("setNameID", new Class[] 
{NameID.class}).invoke(obj, new Object[]{newChild});
       }
       else if (newChild instanceof Attribute && obj instanceof 
AttributeStatement && child instanceof EncryptedAttribute) {
         ((AttributeStatement)obj).getEncryptedAttributes().remove(child);
         ((AttributeStatement)obj).getAttributes().add((Attribute) 
newChild);
       }
       else {
         throw new Exception("Do now know how to handle decrypted 
element " + newChild  + " in its parent " + obj);
       }
     }
   }
   return obj;
}

private XMLObject decrypt(XMLObject obj) throws Exception {
   try {
     if (obj instanceof EncryptedAssertion) {
       obj = this.decrypter.decrypt((EncryptedAssertion)obj);
     }
     else if (obj instanceof EncryptedAttribute) {
       obj = this.decrypter.decrypt((EncryptedAttribute)obj);
     }
     else if (obj instanceof EncryptedID) {
       obj = this.decrypter.decrypt((EncryptedID)obj);
     }
     else {
       throw new Exception("Do not know how to decrypt element of type " 
+ obj.getElementQName().getLocalPart());
     }
     return obj;
   catch (Exception e) {
     throw new Exception("Failed to encrypt " + obj + ": " +  e,e);
   }
}

Sorry for all those mails and questions!
best wishes, Steffi :)




On 04/13/12 13:40, Stephanie Stroka wrote:
> Fun fact: When the ResponseUnmarshaller, but not the Decrypter calls
> attribute.getOwnerElement().setIdAttributeNode(attribute, true) in the
> same method (see stack trace below), the element can be resolved.
>
> That doesn't make sense, does it?
>
>
> Thread [main] (Suspended)	
> 	AssertionUnmarshaller.processAttribute(XMLObject, Attr) line: 81	
> 	AssertionUnmarshaller(AbstractXMLObjectUnmarshaller).unmarshallAttribute(XMLObject, Attr) line: 239	
> 	AssertionUnmarshaller(AbstractXMLObjectUnmarshaller).unmarshall(Element) line: 108	
> 	ResponseUnmarshaller(AbstractXMLObjectUnmarshaller).unmarshallChildElement(XMLObject, Element) line: 334	
> 	ResponseUnmarshaller(AbstractXMLObjectUnmarshaller).unmarshall(Element)
> line: 121	
> 	Toolbox.unmarshall(Element) line: 1339	
> 	Toolbox.unmarshall(InputSource) line: 1318	
> 	Toolbox.unmarshall(String) line: 1306	
> 	Toolbox.resolveHttpPostBinding(AuthRequest) line: 787	
> 	Toolbox.getSAMLMessage(AuthRequest, AuthResponse, Binding) line: 2521	
> 	ServiceProviderState.authenticate(AuthRequest, AuthResponse) line: 876	
> 	ServiceProviderState(AuthState).process(AuthRequest, AuthResponse)
> line: 91	
> 	AuthStateHarness.process(TestContext) line: 154	
> 	AuthStateHarness.authenticate(TestContext) line: 169	
> 	tSAMLUnitTests.encryptChildren_post_symmetric_key() line: 375	
> 	NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not
> available [native method]	
> 	NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39	
> 	DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25	
> 	Method.invoke(Object, Object...) line: 597	
> 	FrameworkMethod$1.runReflectiveCall() line: 44	
> 	FrameworkMethod$1(ReflectiveCallable).run() line: 15	
> 	FrameworkMethod.invokeExplosively(Object, Object...) line: 41	
> 	InvokeMethod.evaluate() line: 20	
> 	BlockJUnit4ClassRunner(ParentRunner<T>).runLeaf(Statement, Description,
> RunNotifier) line: 263	
> 	BlockJUnit4ClassRunner.runChild(FrameworkMethod, RunNotifier) line: 69	
> 	BlockJUnit4ClassRunner.runChild(Object, RunNotifier) line: 48	
> 	ParentRunner$3.run() line: 231	
> 	ParentRunner$1.schedule(Runnable) line: 60	
> 	BlockJUnit4ClassRunner(ParentRunner<T>).runChildren(RunNotifier) line: 229	
> 	ParentRunner<T>.access$000(ParentRunner, RunNotifier) line: 50	
> 	ParentRunner$2.evaluate() line: 222	
> 	RunBefores.evaluate() line: 28	
> 	RunAfters.evaluate() line: 31	
> 	BlockJUnit4ClassRunner(ParentRunner<T>).run(RunNotifier) line: 292	
> 	JUnit4TestAdapter.run(TestResult) line: 39	
> 	JUnitTestRunner.run() line: 421	
> 	JUnitTestRunner.launch(JUnitTest, boolean, boolean, boolean, boolean,
> boolean, boolean, Properties) line: 912	
> 	JUnitTestRunner.main(String[]) line: 766	
>
>
>
> On 04/13/12 13:34, Stephanie Stroka wrote:
>> Hey Brent,
>>
>> thanks for testing and investing your time into that!
>> I may have figured out what causes the trouble.
>> Though I'm still not sure about how to resolve it.
>>
>> First of all: unmarshalling non encrypted messages seems to work fine,
>> except when the unmarshalled msg is marshalled again for
>> logging/debugging purposes: This causes the Id to be lost.
>>
>> The second problem may be caused by using
>> org.opensaml.saml2.encryption.Decrypter	to decrypt an
>> org.opensaml.xml.XMLObject:
>>
>> There's the stack trace to where I figured out the problem:
>>
>> Thread [main] (Suspended)	
>> 	AssertionUnmarshaller.processAttribute(XMLObject, Attr) line: 81	
>> 	AssertionUnmarshaller(AbstractXMLObjectUnmarshaller).unmarshallAttribute(XMLObject, Attr) line: 239	
>> 	AssertionUnmarshaller(AbstractXMLObjectUnmarshaller).unmarshall(Element) line: 108	
>> 	Decrypter(Decrypter).decryptDataToList(EncryptedData, boolean) line: 467	
>> 	Decrypter(Decrypter).decryptData(EncryptedData, boolean) line: 400	
>> 	Decrypter.decryptData(EncryptedElementType) line: 141	
>> 	Decrypter.decrypt(EncryptedAssertion) line: 69	
>> 	Decrypter.decrypt(XMLObject) line: 152	
>> 	Decrypter.decryptWhereNeccessary(XMLObject, EncryptionPass) line: 193	
>> 	Decrypter.decryptWhereNeccessary(XMLObject, EncryptionPass) line: 209	
>> 	Toolbox.verifySignature(SignableSAMLObject, String, AuthRequest,
>> AuthResponse) line: 1477	
>> 	ServiceProviderState.consume(Response, AuthRequest, AuthResponse)
>> line: 223	
>> 	ServiceProviderState.authenticate(AuthRequest, AuthResponse) line: 920	
>> 	ServiceProviderState(AuthState).process(AuthRequest, AuthResponse)
>> line: 91	
>> 	AuthStateHarness.process(TestContext) line: 154	
>> 	AuthStateHarness.authenticate(TestContext) line: 169	
>> 	tSAMLUnitTests.encryptAssertion_redirect_noKeyInfo() line: 148	
>> 	NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not
>> available [native method]	
>> 	NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39	
>> 	DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25	
>> 	Method.invoke(Object, Object...) line: 597	
>> 	FrameworkMethod$1.runReflectiveCall() line: 44	
>> 	FrameworkMethod$1(ReflectiveCallable).run() line: 15	
>> 	FrameworkMethod.invokeExplosively(Object, Object...) line: 41	
>> 	InvokeMethod.evaluate() line: 20	
>> 	BlockJUnit4ClassRunner(ParentRunner<T>).runLeaf(Statement, Description,
>> RunNotifier) line: 263	
>> 	BlockJUnit4ClassRunner.runChild(FrameworkMethod, RunNotifier) line: 69	
>> 	BlockJUnit4ClassRunner.runChild(Object, RunNotifier) line: 48	
>> 	ParentRunner$3.run() line: 231	
>> 	ParentRunner$1.schedule(Runnable) line: 60	
>> 	BlockJUnit4ClassRunner(ParentRunner<T>).runChildren(RunNotifier) line: 229	
>> 	ParentRunner<T>.access$000(ParentRunner, RunNotifier) line: 50	
>> 	ParentRunner$2.evaluate() line: 222	
>> 	RunBefores.evaluate() line: 28	
>> 	RunAfters.evaluate() line: 31	
>> 	BlockJUnit4ClassRunner(ParentRunner<T>).run(RunNotifier) line: 292	
>> 	JUnit4TestAdapter.run(TestResult) line: 39	
>> 	JUnitTestRunner.run() line: 421	
>> 	JUnitTestRunner.launch(JUnitTest, boolean, boolean, boolean, boolean,
>> boolean, boolean, Properties) line: 912	
>> 	JUnitTestRunner.main(String[]) line: 766	
>> 	
>>
>> processAttribute(..) calls
>> attribute.getOwnerElement().setIdAttributeNode(attribute, true)
>> which should register the ID attribute as an id.
>>
>> Here's the content of attribute:
>>
>> "attribute"		(id=96)
>> 	flags		680
>> 	localName	"ID"
>> 	name		"ID"
>> 	namespaceURI	null
>> 	ownerNode 	ElementNSImpl (id=100)
>> 	type		null
>> 	value			"Assertion_d0bd8dd5b6d9b37731d6df773b369fde83408f45"
>>
>> However, I wanted to verify that the ID is indeed registered, so I called
>> attribute.getOwnerElement().getOwnerDocument().getElementById("Assertion_d0bd8dd5b6d9b37731d6df773b369fde83408f45")
>> in the debugger and null is returned.
>>
>> Do you have any ideas what might have gone wrong?
>>
>> We are using xerces 2.9.1 and xalan 2.7.0.
>> To be honest, I've no idea how it has been endorsed into the JVM nor do
>> I know anything about you install instructions ;-).
>> This project that I'm working on is about 10 years old and I just
>> started 1 month ago.
>>
>> I am using Java hotspot 1.6.0 under Linux x86 32bit, but the testing
>> environment also (additionally) runs on 64bit Linux and under Solaris.
>>
>> Steffi
>>
>>
>>
>> On 04/12/12 23:18, Brent Putman wrote:
>>>
>>>
>>> On 4/12/12 1:12 PM, Brent Putman wrote:
>>>> I have not tried running OpenSAML v2 with the newer Santuario yet.  I'll
>>>> do some quick tests later today or tomorrow to see if I experience the
>>>> same issue.
>>>
>>> I did some testing. I was not able to reproduce this by just switching
>>> OpenSAML 2 to use 1.5.1.  All our signature signing and validation tests
>>> work fine for me.
>>>
>>> Your and Scott's comments about the SAML signature profile validator
>>> made me realize that the ID-ness of the attributes and their resolution
>>> can't be fundamentally broken in OpenSAML, and the Santuario IdResolver
>>> must be finding the ID's by DOM search and not by the fallback behavior
>>> that was removed in 1.5.0.  If that were the case, we'd be seeing
>>> routine failures of things in the profile validator, which uses the DOM
>>> search exclusively, before it even attempted to validate the signature.
>>>
>>> I also confirmed this by turning on DEBUG logging level for category:
>>> org.apache.xml.security.utils.IdResolver.  With 1.4.x, I  see output
>>> similar to the following:
>>>
>>> DEBUG IdResolver - getElementByIdType() Search for ID
>>> _b32801700a13922734aeb359f296ddac
>>> DEBUG IdResolver - getElementByIdUsingDOM() Search for ID
>>> _b32801700a13922734aeb359f296ddac
>>> DEBUG IdResolver - I could find an Element using the simple
>>> getElementByIdUsingDOM method: saml2:Assertion
>>>
>>> The line about getElementByIdType() is a little misleading - looking at
>>> code, what that really does is attempt to resolve from the IdResolver
>>> cache, nothing more.  The last line indicates a successful resolution
>>> via the DOM method (despite the slightly awkward English phrasing there).
>>>
>>> In order to troubleshoot what's going on in your environment, you might
>>> try reverting back to 1.4.x, and turning on the debug logging above.  I
>>> suspect you'll see the first 2 lines, but not the "I could find..."
>>> line, which would mean the DOM search is failing.  Unfortunately, it
>>> doesn't explicitly log anything when it then does the manual search
>>> against well-known namespaces, but the absence of the "I could find..."
>>> line in conjunction with a successful resolution and signature
>>> validation would confirm that is what it's doing.
>>>
>>> Also, if you are running the SAMLSignatureProfileValidator, it shouldn't
>>> matter what version of Santuario you are using.  The DOM search there
>>> should fail if the ID-ness is lost, so that could be an indication of
>>> the problem.
>>>
>>> Since it's been working for a large number of people, I'd suspect
>>> either: 1) something you are doing after parsing but before validation
>>> is changing the DOM, as you earlier suggested or 2) an environment issue
>>> with your XML parsing environment.  Can you check and confirm what
>>> version of Xerces and Xalan you are using, and that you have properly
>>> endorsed them in your JVM per our install instructions?  If there is an
>>> issue with some combination of Xerces and Xalan, or a bug in some
>>> version of those libraries, we'd love to uncover that.
>>>
>>> Also, on what JRE vendor, version and OS platform are you seeing this error?
>>>
>>>
>>> --
>>> To unsubscribe from this list send an email to dev-unsubscribe at shibboleth.net
>> --
>> To unsubscribe from this list send an email to dev-unsubscribe at shibboleth.net
>

-- 
AdNovum Informatik AG
Stephanie Stroka
Dipl. Informatik-Ing. FH

Roentgenstrasse 22, CH-8005 Zurich
mailto:stephanie.stroka at adnovum.ch
phone: +41 44 272 6111, fax: +41 44 272 6312

http://www.adnovum.ch  AdNovum Offices: Bern, Budapest, Singapore, 
Zurich (HQ)


More information about the dev mailing list