.netxmlpowershellsamlxml-dsig

PowerShell Validate SAML Signed XML


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.


Solution

  • 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!