SignedXml checksignature returns false

戏子无情 提交于 2019-12-21 03:57:17

问题


I've looked at other posts on here regarding this issue and none of them seem to address my situation.

I've been trying to verify a SAML assertion for the last week and I have 2 clients that have sent me SAML but I cannot verify it.

The main process is we get a base64 encoded assertion and I decode it. Load it into an XmlDocment with PreserveWhitespace = true.

The verify method is

  public static bool Verify(X509Certificate2 cert, XmlElement xmlElement, SignedXml signedXml)
  {
       bool flag;
       try
       {
           KeyInfo keyInfo = new KeyInfo();
           var clause = new KeyInfoX509Data(cert);
           keyInfo.AddClause(clause);

            XmlElement signatureElement = GetSignatureElement(xmlElement);
            if (signatureElement == null)
            {
                string message = "The XML does not contain a signature.";
                throw new SAMLSignatureException(message);
            }
            signedXml.LoadXml(signatureElement);
            if (keyInfo != null)
            {
                signedXml.KeyInfo = keyInfo;
            }
            SetSigningKeyFromKeyInfo(signedXml);
            flag = signedXml.CheckSignature(cert.PublicKey.Key);
        }
        catch (Exception exception)
        {
            throw new SAMLSignatureException("Failed to verify the XML signature.", exception);
        }
        return flag;
    }

 private static void SetSigningKeyFromKeyInfo(SignedXml signedXml)
    {
        IEnumerator enumerator = signedXml.KeyInfo.GetEnumerator();
        while (enumerator.MoveNext())
        {
            if (enumerator.Current is KeyInfoX509Data)
            {
                var current = (KeyInfoX509Data) enumerator.Current;
                if (current.Certificates.Count != 0)
                {
                    var certificate = (X509Certificate) current.Certificates[0];
                    var certificate2 = new X509Certificate2(certificate);
                    AsymmetricAlgorithm key = certificate2.PublicKey.Key;
                    signedXml.SigningKey = key;
                    return;
                }
            }
            else
            {
                if (enumerator.Current is RSAKeyValue)
                {
                    var value2 = (RSAKeyValue) enumerator.Current;
                    signedXml.SigningKey = value2.Key;
                    return;
                }
                if (enumerator.Current is DSAKeyValue)
                {
                    var value3 = (DSAKeyValue) enumerator.Current;
                    signedXml.SigningKey = value3.Key;
                    return;
                }
            }
        }
        throw new SAMLSignatureException("No signing key could be found in the key info.");
    }

I have the certificate from the client that I read in from Web.Config (its stored as base64 encoded string) xmlelement is the signed element, signedXml is a SignedXml object that was created with new SignedXml(xmlElement)

Both clients get false returned by checksignature but when I create my own signed saml with my certificate it will return true.

What am I missing here?

EDIT: Yes both of the clients are on Java and I posted the SetSigningKeyFromKeyInfo method


回答1:


I dealt with signed XML's a lot in the past. All I can say is that it was a nightmare. Basically, when you sign XML, it goes through a process called canonicalization (C14N). It needs to turn XML text to a byte stream which can be signed. Whitespace & namespace handling, among others, in XML C14N standards are hard to understand, even harder to implement right. There are even multiple types of C14N.

The .NET implementation is very selective about what it accepts. It's quite possible that your other implementation doesn't work in the exact same way as the .NET one. This is very sad indeed. If you can eliminate whitespace and namespaces from your source XML before signing, for example, that could help. Also if you could make sure that both implementations use the same C14N settings.

Otherwise a lot of debugging awaits you. You could debug into the framework, or call its internal methods by hand with reflection, to see how it calculates the XML fragment and the signature. And do the same with the other implementation. Basically you need to see the exact byte streams that are signed in both cases. This is the final step of the conversion before signing. If those byte streams match, then you'll have no problems with the RSA signing part in my experience. If those don't match, as is in your case, at least you'll see where the problem is.




回答2:


I just had a similar issue and lost a lot of time, maybe this can help someone.

My environment is 100% .Net 4.5, and my code uses only the SignedXml class. But a SAML assertion was accepted at one place and refused at another one.

Turned out that one place was loading the assertion through an XmlDocument instance initialized with PreserveWhitespace = true, while the other one was not.

And the assertion had been pretty-printed, so it had carriage returns and a lot of indentation spaces. Removing all carriage returns and the indentation spaces fixed my issue.




回答3:


Had a similar issue with Saml as Timores. The Saml needed to be decoded from Base64 but first i used:

var saml = System.Text.Encoding.Default.GetString(Convert.FromBase64String(samlToken))

But this used ASCII decoding and had trouble with special characters. Which means the XML was slightly different from when it was signed and that is why it failed. Changed it to:

var saml = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(samlToken))

and it worked for all cases.

So be sure that you are using the right encoding!




回答4:


I've spent a lot of time on this issue and then realized that I am not checking the signature with the right certificate.

So I decided to check the certificate I am receiving in the XML response file from Azure:

signedXml.LoadXml((XmlElement)nodeList[0]);

X509Certificate2 serviceCertificate = null;
foreach (KeyInfoClause clause in signedXml.KeyInfo)
{
    if (clause is KeyInfoX509Data)
    {
        if (((KeyInfoX509Data)clause).Certificates.Count > 0)
        {
            serviceCertificate = (X509Certificate2)((KeyInfoX509Data)clause).Certificates[0];
        }
    }
}

Then:

bool bTest = signedXml.CheckSignature(serviceCertificate , true);

The bTest value was finally set to true.



来源:https://stackoverflow.com/questions/3935600/signedxml-checksignature-returns-false

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!