I have to verify the signature of a signed SAMLResponse .
I obtain the exception :
org.opensaml.xml.validation.ValidationException: Signature did not validate against the credential's key
The used credential (X509 certificat) is the good one.
Putting the debug log for org.apache.xml.security.utils.
I obtain :
[org.apache.xml.security.utils.DigesterOutputStream] Pre-digested input:
[org.apache.xml.security.utils.DigesterOutputStream] <samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Destination="https://comptePS/ACS" ID="_6bf020b4-2334-11df-833b-d91e3055817a" IssueInstant="2010-02-27T00:09:50Z" Version="2.0" xsi:schemaLocation="urn:oasis:names:tc:SAML:2.0:assertion http://docs.oasis-open.org/security/saml/v2.0/saml-schema-assertion-2.0.xsd">
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">urn:interops:samu:1.0</saml:Issuer>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"></samlp:StatusCode>
</samlp:Status>
</samlp:Response>
[org.apache.xml.security.signature.Reference] Verification failed for URI "#_6bf020b4-2334-11df-833b-d91e3055817a"
[org.apache.xml.security.signature.Reference] Expected Digest: oju8vu1ZMmqMfYMU4uJHT9sdPmQ=
[org.apache.xml.security.signature.Reference] Actual Digest: lCMYYIs+Pv0q2II32b1s7EXgS2Q=
[org.apache.xml.security.signature.Manifest] The Reference has Type
ONLY the saml:Issuer is included in pre-digest input. The saml assertion is not complete.
The signature URI is #_6bf020b4-2334-11df-833b-d91e3055817a and _6bf020b4-2334-11df-833b-d91e3055817a is the ID of the Response including the assertion.
The document is :
<samlp:Response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="_6bf020b4-2334-11df-833b-d91e3055817a" Version="2.0" IssueInstant="2010-02-27T00:09:50Z" Destination="https://comptePS/ACS" xsi:schemaLocation="urn:oasis:names:tc:SAML:2.0:assertion http://docs.oasis-open.org/security/saml/v2.0/saml-schema-assertion-2.0.xsd">
<saml:Issuer>urn:interops:samu:1.0</saml:Issuer><Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<Reference URI="#_6bf020b4-2334-11df-833b-d91e3055817a">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>oju8vu1ZMmqMfYMU4uJHT9sdPmQ=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>UCK9adw+i+rfgVdf7OICYsGV++dTqbpH30escUaoHxEGwai1kGPCrJmuyIMwu3Zu
nStX6OeQvD+jnUuz04IsX2lXCRxhJEa99BLGgbOkA93nqCL/bBGgfuQ4+5HnOR/R
FCaEm+bqKolXkj4lKgh0mC9GKfTcrpGyMrKJfAli7oY=</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIICiDCCAfGgAwIBAgIFFQHV8/MwDQYJKoZIhvcNAQEFBQAwOTELMAkGA1UEBhMC
RlIxFDASBgNVBAoTC0NOQU1UUy1URVNUMRQwEgYDVQQLEwtBQy1URVNULVNTTDAe
Fw0wOTAyMjUxMDIwNTFaFw0xOTAxMDQxMDIwNTFaMIGCMQswCQYDVQQGEwJGUjEP
MA0GA1UEChMGQ05BTVRTMRIwEAYDVQQLEwkxODAwMzUwMjQxIDAeBgNVBAMTF3Rl
c3QtaW50ZXJvcHMuY25hbXRzLmZyMSwwKgYKCZImiZPyLGQBARMcdGVzdC1pbnRl
cm9wcy5jbmFtdHMuZnItc2lnbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
psGwOZyCTWp3aH9mVjUziYKKaPHBpaG8Dm8xhyR5IAzZ9wEPfHlLIYaVWhGoqswV
LC5kn0eq2EFsZSfTTV7AHB+7wBTJAsnflslWjQ9kFYHq05VgKpnGFblad8ATrsl6
jXZa2XknlnCuxsgt606ybm4CaWAOXn19GnNIqxgYmFMCAwEAAaNSMFAwLgYJYIZI
AYb4QgENBCEWH0NlcnRpZmljYXQgc2VydmV1ciBTU0wgaW50cmFuZXQwEQYJYIZI
AYb4QgEBBAQDAgZAMAsGA1UdDwQEAwIFoDANBgkqhkiG9w0BAQUFAAOBgQAUQLv5
dQl5YF62T96BwP93XbIfQ7A46gBtP2soLmDO58kQf0FubT5rAG2z1FLPuWFIeFIe
dlxB9Le20JWWAs8DISKjZxj/furjMWVHEeS9o9YnYUZAzOrFA5fYyD4MnSATHzJc
Z/ON4FYVZ4J4FxljGOOIGwmcRmiKKzcoyU7KEg==</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<saml:Assertion ID="_6bf039e6-2334-11df-833b-d91e3055817a" Version="2.0" IssueInstant="2010-02-27T00:09:50Z">
<saml:Issuer>urn:interops:samu:1.0</saml:Issuer>
<saml:Subject>
<saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">identifiantTest</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData NotOnOrAfter="2010-02-27T00:19:50Z" Recipient="urn:interops:180035024:sp"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="2010-02-27T00:04:50Z" NotOnOrAfter="2010-02-27T00:19:50Z">
<saml:AudienceRestriction>
<saml:Audience>urn:interops:service:test:samu:compte_ps</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2010-02-26T13:44:13Z" SessionIndex="_6bf039e6-2334-11df-833b-d91e3055817a">
<saml:AuthnContext>
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
<saml:AttributeStatement>
<saml:Attribute Name="PAGM">
<saml:AttributeValue>COMPTE_PS</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
</samlp:Response>
Thanks for your help
JC Estienney
On 3/2/10 4:16 AM, jc.est...@cnamts.fr wrote:
>
> [org.apache.xml.security.utils.DigesterOutputStream] Pre-digested input:
> [org.apache.xml.security.utils.DigesterOutputStream] <samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Destination="https://comptePS/ACS" ID="_6bf020b4-2334-11df-833b-d91e3055817a" IssueInstant="2010-02-27T00:09:50Z" Version="2.0" xsi:schemaLocation="urn:oasis:names:tc:SAML:2.0:assertion http://docs.oasis-open.org/security/saml/v2.0/saml-schema-assertion-2.0.xsd">
> <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">urn:interops:samu:1.0</saml:Issuer>
> <samlp:Status>
> <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"></samlp:StatusCode>
> </samlp:Status>
> </samlp:Response>
It's good that you are noticing this info. In general, the way in which
this is useful, though, is you collect the pre-digested input from both
the signing and validation sides, and compare. In doing that you can
pretty easily spot the differences.
> [org.apache.xml.security.signature.Reference] Verification failed for URI "#_6bf020b4-2334-11df-833b-d91e3055817a"
> [org.apache.xml.security.signature.Reference] Expected Digest: oju8vu1ZMmqMfYMU4uJHT9sdPmQ=
> [org.apache.xml.security.signature.Reference] Actual Digest: lCMYYIs+Pv0q2II32b1s7EXgS2Q=
Yes, this is a clear indication that what you are validating has been
changed from what was signed.
> >
>
> ONLY the saml:Issuer is included in pre-digest input. The saml assertion is not complete.
At first I wasn't sure what you meant, but if you mean that what was
purportedly signed is below, but the validation above is only indicating
a subset of that, then it seems that there is something else majorly
wrong here, not with the signing. Can you visually confirm that the
Assertion is even in the Response that you are receiving? From the
above, it appears that the Assertion is actually omitted from the
Response you are being sent and trying to validate. If the signature was
being generated over the whole document as below, but later the
Assertion is stripped out somehow, that certainly counts as modifying
the document and breaking the signature...
On 3/4/10 2:19 AM, JC Estienney wrote:
>
>
> The signed SamlResponse indicated at the end of my message if dumped
> just before the unmarshall operation.
> After that when i get the assertion form the Response object :
>
> XMLHelper.prettyPrintXML( (Assertion)reponseSAML.getAssertions().get(0))
> give :
Ok, well, the full response there looked ok, and the assertion looks ok
too, as far as I can tell.
>
> I will work to obtain then exact digest stream of then signature operation.
> It is not buid by openSaml2 but bye a third tool using C++ xmlsec API.
>
> The verification with the same tool is ok.
That's fine. The problem though, was this:
> [org.apache.xml.security.utils.DigesterOutputStream] <samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Destination="https://comptePS/ACS" ID="_6bf020b4-2334-11df-833b-d91e3055817a" IssueInstant="2010-02-27T00:09:50Z" Version="2.0" xsi:schemaLocation="urn:oasis:names:tc:SAML:2.0:assertion http://docs.oasis-open.org/security/saml/v2.0/saml-schema-assertion-2.0.xsd">
> <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">urn:interops:samu:1.0</saml:Issuer>
> <samlp:Status>
> <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"></samlp:StatusCode>
> </samlp:Status>
> </samlp:Response>
If this is the actual output from Apache xmlsec, and hasn't been edited,
etc., then something is wrong, because it's not including the Assertion
in the data over which it's calculating the digest. If you're saying
that you have a trace which shows that the Response does in fact include
the Assertion, then I don't have any explanation for that. Either
something very strange is going on with xmlsec, or there's a bug
somewhere else, maybe in how the DOM is being parsed and built.
Getting the digest stream from the signing side will be helpful in the
long run, but until you figure what's going on here with the missing
Assertion in digest calculation, it's not going to help.
--Brent
Please excuse this late answer, my responses was rejected because of
their size.
So i tunk it this time dropping the assertions samples..
I found the problème : as you suggested it the SAMLResponse was altered
: by a "printed function :-("
A mashalling operation on the assersion between the Response object
contsruction and the verification had broken this object.
I got the assertion and marshall it to print it but a lot of elements of
the assertion was droped from the response object.
A sample (without signature verification) :
String fic_response = "response.txt";
//Logger logger =
LoggerFactory.getLogger(PbVerifSAML.class.getName());
//logger.info(PbVerifSAML.class.getName());
// Init Config OpenSAML
DefaultBootstrap.bootstrap();
// Configuration du processeur de serialisation
unmarshallerFactory = Configuration.getUnmarshallerFactory();
// Lecture le la reponse SAML et parsing
// Get parser pool manager
BasicParserPool ppMgr = new BasicParserPool();
ppMgr.setNamespaceAware(true);
InputStream is = new ByteArrayInputStream(
Base64.decodeFromFile(fic_response) );
Document inCommonMDDoc = ppMgr.parse(is);
Element racine = inCommonMDDoc.getDocumentElement();
// Preparation de la deserialisation de la racine du doc
Unmarshaller unmarshaller =
unmarshallerFactory.getUnmarshaller(racine);
Response reponseSAML = (Response)
unmarshaller.unmarshall(racine);
System.out.println("Destination : " +
reponseSAML.getDestination());
System.out.println(XMLHelper.prettyPrintXML(reponseSAML.getDOM()));
System.out.println(XMLHelper.prettyPrintXML(reponseSAML.getSignature().getDOM()));
Assertion assertion =
(Assertion)reponseSAML.getAssertions().get(0);
//Validation structurelle de l'assertion SAML
assertion.validate(true);
AssertionMarshaller marshaller = new AssertionMarshaller();
// HERE IS THE PB
Element element = marshaller.marshall(assertion);
// THE object reponseSAML is altered
System.out.println(XMLHelper.prettyPrintXML(element));
System.out.println(XMLHelper.prettyPrintXML(reponseSAML.getSignature().getDOM()));
// THE output is different 2
If i get the assertion after signature verification. it is OK
Excuse me for the inconvenience (and for my english)
JC Estienney
On 3/10/10 3:26 AM, JC Estienney wrote:
>
>
> // Preparation de la deserialisation de la racine du doc
> Unmarshaller unmarshaller =
> unmarshallerFactory.getUnmarshaller(racine);
>
> Response reponseSAML = (Response)
> unmarshaller.unmarshall(racine);
> System.out.println("Destination : " +
> reponseSAML.getDestination());
>
> System.out.println(XMLHelper.prettyPrintXML(reponseSAML.getDOM()));
>
> System.out.println(XMLHelper.prettyPrintXML(reponseSAML.getSignature().getDOM()));
> Assertion assertion =
> (Assertion)reponseSAML.getAssertions().get(0);
> //Validation structurelle de l'assertion SAML
> assertion.validate(true);
> AssertionMarshaller marshaller = new AssertionMarshaller();
> // HERE IS THE PB
> Element element = marshaller.marshall(assertion);
> // THE object reponseSAML is altered
> System.out.println(XMLHelper.prettyPrintXML(element));
>
>
Ah! Ok, now I see what is going on. Yes, that marshalling operation on
the Assertion is absolutely causing that weird behavior of the Response
with the missing Assertion. The reason: The single-arg
marshall(XMLObject) method impl actually marshalls the XMLObject into a
newly constructed Document. If the XMLObject is already marshalled,
it's not a no-op as you might think, it actually winds up
unconditionally adopting the DOM Element subtree into the new Document,
which removes it from the original DOM tree. Off-hand, I don't know why
the marshaller always unconditionally marshalls into a new Document like
that, rather than detecting whether the target is already marshalled.
Chad might be able to comment further, but it's possible we might need
to look at changing that behavior, or at least provide some option for
not doing that, like an overloaded marshall(XMLObject target, boolean
newDocumentIfAlreadyMarshalled) or something similar. At the very least,
unnecessarily adopting into a new Document is somewhat expensive.
In any case, you probably realize that you don't really need to
re-marshall there, since you just unmarshalled the object, and so it
already has a DOM. Although this perhaps highlights a similar issue
with the API - it nominally wasn't intended that people call getDOM() to
get the XMLObject's Element, but instead call marshall(XMLObject) - but
if marshall() always has potentially unwanted side-effects as it does,
then that's a problem too. For now I suppose the only option is just
use getDOM(), if you need a sub-Element from an already marshalled (or
unmarshalled) tree.
--Brent
FWIW, my code doesn't give you the option, it just reuses the DOM tree if
it's cached, or uses a new document if none is supplied in the call. If I
needed it to do what you're describing, I'd just create my own document to
give it, or release the DOM first.
> In any case, you probably realize that you don't really need to
> re-marshall there, since you just unmarshalled the object, and so it
> already has a DOM. Although this perhaps highlights a similar issue
> with the API - it nominally wasn't intended that people call getDOM() to
> get the XMLObject's Element, but instead call marshall(XMLObject) - but
> if marshall() always has potentially unwanted side-effects as it does,
> then that's a problem too. For now I suppose the only option is just
> use getDOM(), if you need a sub-Element from an already marshalled (or
> unmarshalled) tree.
In my case, I definitely count on the fact that XMLObject::marshall()
returns the existing DOM if I need it, because getDOM() would return NULL if
it didn't have one, pushing extra checks into my code.
-- Scott
Chad might be able to comment further, but it's possible we might need to look at changing that behavior, or at least provide some option for not doing that, like an overloaded marshall(XMLObject target, boolean newDocumentIfAlreadyMarshalled) or something similar. At the very least, unnecessarily adopting into a new Document is somewhat expensive.
FWIW, my code doesn't give you the option, it just reuses the DOM tree if it's cached, or uses a new document if none is supplied in the call. If I needed it to do what you're describing, I'd just create my own document to give it, or release the DOM first.In any case, you probably realize that you don't really need to re-marshall there, since you just unmarshalled the object, and so it already has a DOM. Although this perhaps highlights a similar issue with the API - it nominally wasn't intended that people call getDOM() to get the XMLObject's Element, but instead call marshall(XMLObject) - but if marshall() always has potentially unwanted side-effects as it does, then that's a problem too. For now I suppose the only option is just use getDOM(), if you need a sub-Element from an already marshalled (or unmarshalled) tree.In my case, I definitely count on the fact that XMLObject::marshall() returns the existing DOM if I need it, because getDOM() would return NULL if it didn't have one, pushing extra checks into my code. -- Scott
KeyInfoCredentialResolver resolver =
SecurityHelper.buildBasicInlineKeyInfoResolver();
KeyInfo keyInfo =
sig.getKeyInfo();
CriteriaSet criteriaSet = new
CriteriaSet(new
KeyInfoCriteria(keyInfo));
try
{
for (Credential cred :
resolver.resolve(criteriaSet))
{
SignatureValidator sigValidator = new SignatureValidator(cred);
sigValidator.validate(sig);
}
}
Where sig is the signature i pulled out of the SamlResponse. Inspecting the signature shows that it has the right key in there as far as I can tell.
// have SignatureAlgorithm sign the input
bytes and compare them to
On 3/16/10 3:18 PM, Tom Delorenzi wrote:
> We were never able to confirm wether my code was rejecting signatuers as
> invalid was because of our testbed saml simulator was not signing them
> correctly or if I was not verifying correctly. We finally got to go up
> against an existing system machine in a known working situation and
> unfortunately I am still claiming their signatures are invalid. I know
> its not as secure but for now we just want to use the public key in the
> assertion to validate the assertion. Here is what I am doing:
>
>
> KeyInfoCredentialResolver resolver =
> SecurityHelper./buildBasicInlineKeyInfoResolver/();
> KeyInfo keyInfo = sig.getKeyInfo();
> CriteriaSet criteriaSet = new CriteriaSet(new KeyInfoCriteria(keyInfo));
> try
> {
> for (Credential cred : resolver.resolve(criteriaSet))
> {
> SignatureValidator sigValidator = new SignatureValidator(cred);
> sigValidator.validate(sig);
> }
> }
>
> Where sig is the signature i pulled out of the SamlResponse. Inspecting
> the signature shows that it has the right key in there as far as I can tell.
>
> I have had it fail in 2 different spots for 2 different test scenarios.
> First was using our simulator(homgrown) and the 2nd was having a apache
> server return back a precanned signed response that our customer provided.
>
> The simulator failed in XMLSignature which for me was line 625 doing:
>
> // have SignatureAlgorithm sign the input bytes and compare them to
> // the bytes that were stored in the signature.
>
> if (!sa.verify(sigBytes))
>
>
> The precanned one passed that but failed a bit later in
> Reference.verify() in the following block
>
> byte[] elemDig = this.getDigestValue();
> byte[] calcDig = this.calculateDigest(true);
> boolean equal = MessageDigestAlgorithm./isEqual/(elemDig, calcDig);
> if (!equal) {
> / log/.warn("Verification failed for URI \"" + this.getURI() + "\"");
> / log/.warn("Expected Digest: " + Base64./encode/(elemDig));
> / log/.warn("Actual Digest: " + Base64./encode/(calcDig));
> }
>
> If anyone has any suggestions let me know. Thanks!
--
Chad La Joie
www.itumi.biz
trusted identities, delivered
On 3/16/10 10:18 AM, Tom Delorenzi wrote:
> >
>
> KeyInfoCredentialResolver resolver =
> SecurityHelper./buildBasicInlineKeyInfoResolver/();
> KeyInfo keyInfo = sig.getKeyInfo();
> CriteriaSet criteriaSet = new CriteriaSet(new KeyInfoCriteria(keyInfo));
> try
> {
> for (Credential cred : resolver.resolve(criteriaSet))
> {
> SignatureValidator sigValidator = new SignatureValidator(cred);
> sigValidator.validate(sig);
> }
> }
That code looks fine to me, as far as just validating the signature
against the KeyInfo-supplied key. That code itself can't be causing any
of your problems, it's too simple and too high level. Your failures are
much lower down at the Apache xmlsec layer.
>
> The simulator failed in XMLSignature which for me was line 625 doing:
>
> // have SignatureAlgorithm sign the input bytes and compare them to
> // the bytes that were stored in the signature.
>
> if (!sa.verify(sigBytes))
>
That's indicating a failure to validate the SignedInfo with the supplied
key. I don't think we see this failing as much as the next case.
Nominally either 1) the key is wrong, or else 2) the SignedInfo has been
modified from what was signed, or is at least being evaluated
differently by the verifier than the signer. Assuming the latter is the
case, and that they aren't any obvious ways in which the document is
being modified (intentionally or unintentially reformatted after
signing, possibly by some aspect the
serialization/deserialization/unmarshalling process), one thing you
might check is the canonicalization algorithm in use. If it's not one
of the exclusive ones, then that might be causing it, e.g signing
environment is different than the validation one wrt parent element
namespace decls and so forth.
>
> The precanned one passed that but failed a bit later in
> Reference.verify() in the following block
>
> byte[] elemDig = this.getDigestValue();
> byte[] calcDig = this.calculateDigest(true);
> boolean equal = MessageDigestAlgorithm./isEqual/(elemDig, calcDig);
> if (!equal) {
> / log/.warn("Verification failed for URI \"" + this.getURI() + "\"");
> / log/.warn("Expected Digest: " + Base64./encode/(elemDig));
> / log/.warn("Actual Digest: " + Base64./encode/(calcDig));
> }
That's the more typical case that the target of the signature that was
signed (the target of the Reference) is in some way not the same bytes
that are being verified. That's almost always due to some unintentional
modification of the data (reformattting after the signature, etc), but
could also be the c14n issue above, I'd check that.
>
> If anyone has any suggestions let me know. Thanks!
As always, you'll get some useful info from turning on the Apache xmlsec
logs at DEBUG for categories:
org.apache.xml.security.utils.SignerOutputStream (for the SignedInfo)
org.apache.xml.security.utils.DigesterOutputStream (for the Reference)
Looking at those closely by themselves alone might give you some hints
(i.e. look for what namespace decls might be getting pulled in there by
the canonicalizer), but they are mostly only useful if you have the same
data from the signing side with which to compare. If you can get both,
the problem usually becomes immediately clear - you'll see what the
differences are, and can then usually infer what is causing them in the
source document in the processing flow.
--Brent
-----Original Message-----
From: Brent Putman [mailto:put...@georgetown.edu]
Sent: Tuesday, March 16, 2010 2:47 PM
To: mace-open...@internet2.edu
Subject: Re: [OpenSAML] Yet another signature verification problem