Closed Tarig0 closed 5 years ago
Looks like this feature was removed at commit 8cd95c84f1e833fcdfbcb54b56bd8dadb858e1c1
@Tarig0 yes, we deliberately only support a single signature as we were focused on Saml tokens. I agree that xmldsig has the ability for multiple signatures. Can you share why you need this support?
I'm trying to extend support for samlp. Currently I could use signedxml, only need it for the protocol level signature, since that is now part of core but I was hoping to keep it out.
@Tarig0 What are you trying to keep out? Sorry it is not clear.
Trying to avoid taking on the following dependency
https://www.nuget.org/packages/System.Security.Cryptography.Xml/
The only reason why I need it is to validate the signature of the SAMLP response
Did my last comment clear up what I meant?
@Tarig0 it was simply a work scoping tactic to allow a single signature. The code was developed to extend to multiple. What is your time frame?
I think this fits with https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/759
I am in exploratory/learning phase so no formal timeline. I agree that this request is covered by the work required for #759
@Tarig0 I'm afraid nested signatures are more complex than merely taking the first. There is a horrible class attacks on SAML2 implemenations that use XML Signature Wrapping in various creative ways. The short-cut the team has taken so far with only one signature and hard coding it to one reference which is hard coded to be the entire document is safe. But as soon as those assumptions are no longer valid, I think that steps must be taken to ensure that the right signature (exact location in XML doc must follow SAML2 spec) is validated and that it contain exactly one reference, which points to the right element (once again according to the SAML2 spec). The filtering out of the signature itself is done by the enveloped signature transform, which then should be used to ensure that the right signature block is excluded.
Sorry if I'm a bit pessimistic, but in my work with SAML2P I've found out that XML signatures are hard. Not just to implement, but even to use by calling the APIs in a secure way.
The assumptions that I have is.
Little more fleshed out
@AndersAbel yes, when we wrote WS-Security for WCF that has multiple signatures it was tricky.
Having multiple references is not as hard a problem. @Tarig0 yes, embedded signatures where the inner is simply data to the outer AND the reading is strict (signature follows the node) should be fine.
I'm not sure I understand the meaning of "signature follows the node".
Does that mean that you cant have the following?
@Tarig0 Yes, the structure you show is valid. Although the ordering is different, the response signature must be before the assertion:
I talked to @brentschmaltz yesterday and we discussed this issue (and some others). Our suggestion is to change the EnvelopedSignatureReader so that it preserves the ID of the initial node, and then checks each Signature to see if it contains a single reference to the ID. Any Signatures not matchin will be kept as part of the data. That will solve the multiple signature issue.
@Tarig0 @AndersAbel We could allow for the Response signature to be after the assertions. We would need logic to ensure ID match AND there is only one such ID.
@brentschmaltz Yes, my bad - I'm too stuck into the SAML2 world where the signature is always right after the issuer. You said that the same isn't true for WS-Fed.
@AndersAbel I'll lobby to get resources to put this in the next release.
I did some code reading on this (and checked the spec).
The first problem to solve is to identity the right Signature to set as EnvelopedSignatureReader.Signature
. This can be done by checking the reference. Which can only be done after it has been read. So if the <ds:Signature>
node is read and it turns out it's the wrong one, the reader must somehow be rewinded to output the <ds:Signature>
node. For SAML2, it could be acceptable to filter out inner signatures in most cases, but sometimes they are relevant (such as when an assertion in a response is signed by someone else than the response creator).
The second problem is the EnvelopedSignatureTransform
that right now filters out any signature node. That will obviously not work as an outer signature will also protect an inner signature. This is tricky, as there is no requirement for a <ds:Signature>
element to have an ID. So somehow when a signature node is encountered in the XmlStream, it must be possible to check whether it's the right one to filter out or not. One idea is to save a reference to the XmlTokenEntry
for the <ds:Signature>
in the Transform and then do a reference comparison when processing/filtering.
Looking at the job needed to extend the EnvelopedSignatureReader
to allow for nested signatures, it feels more and more like reinventing a complete SignedXml implementation. And there already is one. What is the reason for having a separate implementation here, instead of relying on System.Security.Cryptography.Xml.SignedXml
?
@AndersAbel couple of reasons for xmlreaders.
@brentschmaltz Thanks for the explanations
we are not providing a 'general' DSig library, but a fast token specific reader
If we focus on what can actually happen in the SAML world, I've done some digging in the SAML2 specs and there's some good news: The relevant signature is always the first one in the token stream. The signature covering the root node is required to be before any contained data that can be signed. A abbreviated example:
<Response ID="R">
<Signature><Reference URI="#R"></Signature>
<Assertion ID="A">
<Signature><Reference URI="A"></Signature>
</Assertion>
</Response>
If validating the signature of the Response, the EnvelopedSignatureReader token stream will start at the <Response>
element. If validating the signature of the assertion, the EnvelopedSignatureReader token stream will start at the <Assertion>
element.
So a flag in XmlTokenStreamWriter.WriteTo
that is set when an element has been excluded and ensures only one element is excluded would do.
This is at least true in the SAML2/SAML2P world. But what about WS-Fed? If I recall what you've said previously there is never more than one signature in WS-Fed?
@AndersAbel for general WS-Security signatures, the signature could be anywhere (almost) in the SOAP envelope and contain multiple parts. That's what I mean by 'general' DSig. These XML libraries 'could' be extended to provide multiple signatures over multiple parts of the envelope.
@brentschmaltz Trying to solve one problem at a time - would you accept a PR that implements a flag like I suggested to only filter out the first signature? It would become SAML2/SAML2P compatible, and would not break anything existing.
@AndersAbel in general I am not a big fan of flags. I prefer a policy strategy. Are you thinking on the DSigSerializer or EnvelopeSignatureReader?
I feel this feature should be easily to fit into the next release, whatever form it takes. We are shooting for 11/19 (was stated as 10/19, but that was a typo).
It's not a flag for the user - it's an internal flag in the processing that marks when one signature has been excluded and then doesn't exclude the rest. I'll try to write up a PR including tests in the next few days so you can see what it looks like.
Added with: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/1181 We still have work todo to ensure the InclusivePrefixes is correct. See https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/1184
Sorry to resurrect such an old issue, but this seemed like the most appropriate place, considering this is what you find when you search for the IDX30019 error message.
I've received such a Saml2 token, but when I call ReadSaml2Token
on the xml string, it throws IDX30019. How do I actually indicate that I do not want it to throw on nested signatures?
My structure of my SAML is:
<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
<saml:Issuer>…</saml:Issuer>
<dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
<dsig:SignedInfo>
<dsig:CanonicalizationMethod/>
<dsig:SignatureMethod/>
<dsig:Reference>
<dsig:Transforms>
<dsig:Transform/>
<dsig:Transform/>
</dsig:Transforms>
<dsig:DigestMethod/>
<dsig:DigestValue>…</dsig:DigestValue>
</dsig:Reference>
</dsig:SignedInfo>
<dsig:SignatureValue>…</dsig:SignatureValue>
<dsig:KeyInfo>
<dsig:KeyName>…</dsig:KeyName>
</dsig:KeyInfo>
</dsig:Signature>
<saml:Subject>
<saml:NameID>…</saml:NameID>
<saml:SubjectConfirmation>
<saml:SubjectConfirmationData/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions>
<saml:AudienceRestriction>
<saml:Audience>…</saml:Audience>
<saml:Audience>…</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:Advice>
<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
<saml2:Issuer>…</saml2:Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod/>
<ds:SignatureMethod/>
<ds:Reference>
<ds:Transforms>
<ds:Transform/>
<ds:Transform/>
</ds:Transforms>
<ds:DigestMethod/>
<ds:DigestValue>…</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>…</ds:SignatureValue>
<ds:KeyInfo>
<ds:KeyName>…</ds:KeyName>
</ds:KeyInfo>
</ds:Signature>
<saml2:Subject>
<saml2:NameID>…</saml2:NameID>
<saml2:SubjectConfirmation>
<saml2:SubjectConfirmationData/>
</saml2:SubjectConfirmation>
</saml2:Subject>
<saml2:Conditions>
<saml2:AudienceRestriction>
<saml2:Audience>…</saml2:Audience>
</saml2:AudienceRestriction>
</saml2:Conditions>
<saml2:AuthnStatement>
<saml2:AuthnContext>
<saml2:AuthnContextClassRef>…</saml2:AuthnContextClassRef>
</saml2:AuthnContext>
</saml2:AuthnStatement>
<saml2:AttributeStatement>
<saml2:Attribute>
<saml2:AttributeValue>…</saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute>
<saml2:AttributeValue>
<saml2:EncryptedID>
<xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:EncryptionMethod/>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:RetrievalMethod/>
</ds:KeyInfo>
<xenc:CipherData>
<xenc:CipherValue>…</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
<xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:EncryptionMethod>
<ds:DigestMethod xmlns:ds="http://www.w3.org/2000/09/xmldsig#"/>
</xenc:EncryptionMethod>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:KeyName>…</ds:KeyName>
</ds:KeyInfo>
<xenc:CipherData>
<xenc:CipherValue>…</xenc:CipherValue>
</xenc:CipherData>
<xenc:ReferenceList>
<xenc:DataReference/>
</xenc:ReferenceList>
</xenc:EncryptedKey>
</saml2:EncryptedID>
</saml2:AttributeValue>
</saml2:Attribute>
</saml2:AttributeStatement>
</saml2:Assertion>
</saml:Advice>
<saml:AuthnStatement>
<saml:AuthnContext>
<saml:AuthnContextClassRef>…</saml:AuthnContextClassRef>
<saml:AuthenticatingAuthority>…</saml:AuthenticatingAuthority>
</saml:AuthnContext>
</saml:AuthnStatement>
<saml:AttributeStatement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<saml:Attribute>
<saml:AttributeValue>
<saml2:EncryptedID xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
<xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:EncryptionMethod/>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:RetrievalMethod/>
</ds:KeyInfo>
<xenc:CipherData>
<xenc:CipherValue>…</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
<xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:EncryptionMethod>
<ds:DigestMethod xmlns:ds="http://www.w3.org/2000/09/xmldsig#"/>
</xenc:EncryptionMethod>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:KeyName>…</ds:KeyName>
</ds:KeyInfo>
<xenc:CipherData>
<xenc:CipherValue>…</xenc:CipherValue>
</xenc:CipherData>
<xenc:ReferenceList>
<xenc:DataReference/>
</xenc:ReferenceList>
</xenc:EncryptedKey>
</saml2:EncryptedID>
</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute>
<saml:AttributeValue xsi:type="xs:string">…</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
Looks like the only reason why signatures, that include another signature in its calculation, are not supported is because the XML token stream will take out ALL signatures and not just the first instance of it.
I think, with the assumption that the first signature is the one being verified, an element remove flag could be added and if true include any other signature elements.
https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/6e7a53e241e4566998d3bf365f03acd0da699a31/src/Microsoft.IdentityModel.Xml/XmlTokenStreamWriter.cs#L158-L160