1

We recently had an issue where an IdP was not trusting SAML 2.0 logout request signatures from our RP/SP. We were seeking alternative ways to validate the SAML request signature because both the IdP and samltool.com complained about the signature validation. Below is an example answer that we used to check the signed data could be validated against the signature.

Douglas Plumley
  • 565
  • 5
  • 21
  • This code fails to properly validate the signature and is vulnerable to XML Signature wrapping attacks, as it doesn't check the references. I wrote a [blog post](https://coding.abel.nu/2015/12/xml-signatures-and-references/) about what references are and why checking them is a must. – Anders Abel Mar 23 '16 at 07:32
  • @AndersAbel, I'm not sure I follow your comment 100%. In my tests of this code when I alter signed data the XML signature validation then returns false as expected. If you are talking about validating the trust of the whole XML document then that's beyond the scope of what I was trying to show. This is just a simple script that validates the signature of signed data. In both examples I'm providing the key information in the $signed object, are you indicating I still need to supply it as an argument in $signed.CheckSignature()? – Douglas Plumley Mar 23 '16 at 12:26
  • @AndersAbel I think I understand now, however this code isn't intended to checkthe SAML tokens trustworthiness/validity, only validate the signature passes or not based on signed data. – Douglas Plumley Mar 23 '16 at 13:16
  • @AndersAbel This code is for troubleshooting validation issues, not for writing an IdP/SP/RP which needs to verify references as you've pointed out. In our scenario we had an IdP that wasn't able to validate signatures on logout requests, all we needed was an example of the signature being validated, it really didn't matter if the token was tampered with. All this code was used to do was prove that the IdP was misconfigured. If you'd like to suggest some code to improve upon what we used it for that would be great! – Douglas Plumley Mar 23 '16 at 18:41
  • Ok, I see that in your particular use case it might not be needed to check the references. But please include a clear comment in the example that it is not done, or someone else will find the code and use it without understanding the consequences. – Anders Abel Mar 23 '16 at 21:05

1 Answers1

2

Add required types and definition for SHA256

Add-Type -AssemblyName System.Security

# Add SHA-256 per http://stackoverflow.com/questions/30759119/verifying-xml-signature-in-powershell-with-pem-certificate
Add-Type @'
        public class RSAPKCS1SHA256SignatureDescription : System.Security.Cryptography.SignatureDescription
            {
                public RSAPKCS1SHA256SignatureDescription()
                {
                    base.KeyAlgorithm = "System.Security.Cryptography.RSACryptoServiceProvider";
                    base.DigestAlgorithm = "System.Security.Cryptography.SHA256Managed";
                    base.FormatterAlgorithm = "System.Security.Cryptography.RSAPKCS1SignatureFormatter";
                    base.DeformatterAlgorithm = "System.Security.Cryptography.RSAPKCS1SignatureDeformatter";
                }

                public override System.Security.Cryptography.AsymmetricSignatureDeformatter CreateDeformatter(System.Security.Cryptography.AsymmetricAlgorithm key)
                {
                    System.Security.Cryptography.AsymmetricSignatureDeformatter asymmetricSignatureDeformatter = (System.Security.Cryptography.AsymmetricSignatureDeformatter)
                        System.Security.Cryptography.CryptoConfig.CreateFromName(base.DeformatterAlgorithm);
                    asymmetricSignatureDeformatter.SetKey(key);
                    asymmetricSignatureDeformatter.SetHashAlgorithm("SHA256");
                    return asymmetricSignatureDeformatter;
                }
            }
'@
$RSAPKCS1SHA256SignatureDescription = New-Object RSAPKCS1SHA256SignatureDescription
[System.Security.Cryptography.CryptoConfig]::AddAlgorithm($RSAPKCS1SHA256SignatureDescription.GetType(), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256")

Validate SAML 2.0 HTTP-POST Request without certificate included in request:

$saml = "insert real saml request here"

$decoded = [System.Convert]::FromBase64String($saml)
$stream = [System.IO.MemoryStream]::new($decoded, 0, $decoded.length)

$xml = New-Object System.Xml.XmlDocument
$xml.PreserveWhitespace = $true
$xml.Load($stream)

$signed = New-Object System.Security.Cryptography.Xml.SignedXml -ArgumentList $xml
$signed.LoadXml($xml.DocumentElement.Assertion.Signature)

$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new("C:\Users\username\Desktop\idp.cer")

$keyinfo = [System.Security.Cryptography.Xml.KeyInfo]::new()
$clause = [System.Security.Cryptography.Xml.KeyInfoX509Data]::new($cert)
$keyinfo.AddClause($clause)

$signed.KeyInfo = $keyinfo

$signed.CheckSignature()

Modify XML so signature cannot be validated in above example:

$xml.Response.Assertion.Subject.NameID.'#text' = 'asdasdasd'

$signed = New-Object System.Security.Cryptography.Xml.SignedXml -ArgumentList $xml
$signed.LoadXml($xml.DocumentElement.Assertion.Signature)

$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new("C:\Users\username\Desktop\idp.cer")

$keyinfo = [System.Security.Cryptography.Xml.KeyInfo]::new()
$clause = [System.Security.Cryptography.Xml.KeyInfoX509Data]::new($cert)
$keyinfo.AddClause($clause)

$signed.KeyInfo = $keyinfo

$signed.CheckSignature()

Validate SAML 2.0 HTTP-POST Request with certificate included in request:

$saml = "insert example saml request here"
$decoded = [System.Convert]::FromBase64String($saml)
$stream = [System.IO.MemoryStream]::new($decoded, 0, $decoded.length)

$xml = New-Object System.Xml.XmlDocument
$xml.PreserveWhitespace = $true
$xml.Load($stream)

$signed = New-Object System.Security.Cryptography.Xml.SignedXml -ArgumentList $xml
$signed.LoadXml($xml.DocumentElement.Signature)
$signed.CheckSignature()

Modify XML so signature cannot be validated in above example:

$xml.LogoutRequest.NameID.'#text' = "dasdasd"

$signed = New-Object System.Security.Cryptography.Xml.SignedXml -ArgumentList $xml
$signed.LoadXml($xml.DocumentElement.Signature)


# Should return false since we modified the data
$signed.CheckSignature()

Hopefully this saves someone else some time if they need to accomplish the same task. Please let me know if you have any input/suggestions.

Thanks!

Douglas Plumley
  • 565
  • 5
  • 21