.NET

How to check X509 signature of saml token

Last I was creating a module to read a saml token response. One of the features of this module was validating the token.
One of the required validations was checking the signature.
And here I had some difficulties to complete this task.
I had 2 main problems with this task.

1. The first problem was that the SignedXml.CheckSignature always returned false.
Here my mistake was that I used an XmlDocument object where the whitespaces were deleted. (by default). To keep the XmlDocument in its original format you need to set the PreserveWhitespace attribute of the XmlDocument to true.

2. From then of I got a new error message. Every time I tried to run SignedXml.CheckSignature() I got the error message ‘Malformed reference element’

Here I discovered the solution was creating a SamlSignedXml object instead of creating a SingedXml object.
The SamlSignedXml is not a default class. You need to create the class yourself by inheriting from SignedXml and override the GetIdElement() method. The code for this class you can find here:

public class SamlSignedXml : SignedXml{
    private string _referenceAttributeId = "";
    public SamlSignedXml(XmlElement element, string referenceAttributeId) : base(element) {
        _referenceAttributeId = referenceAttributeId;
    }
    public override XmlElement GetIdElement(XmlDocument document, string idValue) {
        return (XmlElement)document.SelectSingleNode(string.Format("//*[@{0}='{1}']", _referenceAttributeId, idValue));
    }
}

After changing the SignedXml object into a SamlSignedXml object my code looked like this and worked:

String samlresponse ="Response base64 encoded";
String decodedSaml = Global.DecodeSaml(samlresponse);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load(new StringReader(decodedSaml));
XmlNamespaceManager nSpace = new XmlNamespaceManager(xmlDoc.NameTable);
nSpace.AddNamespace("ds", SignedXml.XmlDsigNamespaceUrl);
XmlElement signNode = (XmlElement)xmlDoc.DocumentElement.SelectSingleNode("ds:Signature", nSpace);
SamlSignedXml samlSignedXml = new SamlSignedXml((XmlElement)xmlDoc.DocumentElement, "ResponseID");
samlSignedXml.LoadXml((XmlElement)signNode);
X509Certificate2 certificate = new X509Certificate2("C:\\PathToCertificate.cer");
bool samlValid = samlSignedXml.CheckSignature(certificate, true);

Hopefully this solution saved you a lot of searching or gave you a solution after a long time trying.

6 thoughts on “How to check X509 signature of saml token

  1. Nice solution. In my case I replace string of code XmlElement signNode = (XmlElement)xmlDoc.DocumentElement.SelectSingleNode("ds:Signature", nSpace); with
    XmlNodeList nodeList = xmlDoc.GetElementsByTagName("ds:Signature");
    XmlElement signNode = (XmlElement)nodeList[0];

  2. Nice stuff, one comment: it does not work if the referenced attribute is qualified with a namespace. Perhaps it never happens in case of Saml, but it does in my case. It can be solved with replacing raw 7 of SamlSignedXml class with the following:

    XmlNamespaceManager nsManager = new XmlNamespaceManager(document.NameTable);
    return (XmlElement)document.SelectSingleNode(string.Format(“//*[@{0}='{1}’]”, _referenceAttributeId, idValue), nsManager);

  3. Still I am getting Malformed XML exception below is my code

    public bool Validate(string samlDecodedString)
    {

    try
    {
    String samlresponse = “Response base64 encoded”;

    var xmlDoc = new XmlDocument();
    xmlDoc.PreserveWhitespace = true;
    xmlDoc.Load(new StringReader(samlDecodedString));
    var nSpace = new XmlNamespaceManager(xmlDoc.NameTable);
    nSpace.AddNamespace(“ds”, SignedXml.XmlDsigNamespaceUrl);

    if (xmlDoc.DocumentElement != null)
    {
    //var signNode = (XmlElement)xmlDoc.DocumentElement.SelectSingleNode(“ds:SignatureValue”, nSpace);

    var signNode = (XmlElement)xmlDoc.DocumentElement.SelectSingleNode(“//*[local-name()=’SignatureValue’]”, nSpace);
    var samlSignedXml = new SamlSignedXml((XmlElement)xmlDoc.DocumentElement, “ResponseID”);

    if (signNode != null) samlSignedXml.LoadXml(signNode);

    const string thumbprint = “f0 45 46 95 52 b5 4d 3d 2c ab 9b 2e 5d 6d ef 18 55 df c3 f8”;
    var certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
    // Try to open the store.

    certStore.Open(OpenFlags.ReadOnly);

    // Find the certificate that matches the thumbprint.
    X509Certificate2Collection certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint,
    thumbprint, false);
    certStore.Close();

    bool samlValid = samlSignedXml.CheckSignature(certCollection[0], true);

    return samlValid;
    }

    return false;
    }
    catch (Exception exSAML)
    {
    throw exSAML;
    }

    }

  4. I am getting {“Malformed reference element.”} System.Security.Cryptography.CryptographicException when I call
    bool samlValid = samlSignedXml.CheckSignature(certificate, true);

  5. For me I had to change ResponseID to ID:

    SamlSignedXml samlSignedXml = new SamlSignedXml((XmlElement)xmlDoc.DocumentElement, “ID”);

Leave a Reply

Your email address will not be published. Required fields are marked *