SignedXml checksignature返回false

我在这里看了关于这个问题的其他post,但似乎没有一个能解决我的情况。

我一直在尝试validation上周的SAML断言,我有2个客户端已经发送给我SAML但我无法validation它。

主要过程是我们得到一个base64编码的断言,我解码它。 使用PreserveWhitespace = true将其加载到XmlDocment中。

validation方法是

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."); } 

我从Web.Config读取客户端的证书(它存储为base64编码的字符串)xmlelement是signed元素,signedXml是使用新的SignedXml(xmlElement)创建的SignedXml对象

两个客户端都因checkignature返回错误,但是当我使用我的证书创建自己的签名saml时,它将返回true。

我在这里想念的是什么?

编辑:是的两个客户端都在Java上,我发布了SetSigningKeyFromKeyInfo方法

我过去经常处理签名的XML。 我只能说这是一场噩梦。 基本上,当您签署XML时,它会经历一个名为canonicalization(C14N)的过程。 它需要将XML文本转换为可以签名的字节流。 XML C14N标准中的空白和命名空间处理等很难理解,甚至难以正确实现。 甚至有多种类型的C14N。

.NET实现对它接受的内容非常挑剔。 你的其他实现很可能与.NET的实现方式完全不同。 这确实非常悲伤。 例如,如果您可以在签名之前从源XML中消除空格和命名空间,那可能会有所帮助。 此外,如果您可以确保两个实现使用相同的C14N设置。

否则很多调试都在等着你。 您可以调试到框架中,或者通过reflection手动调用其内部方法,以查看它如何计算XML片段和签名。 并对其他实现执行相同的操作。 基本上,您需要查看在两种情况下都签名的确切字节流。 这是签署前转换的最后一步。 如果这些字节流匹配,那么根据我的经验,您对RSA签名部分没有任何问题。 如果那些不匹配,就像你的情况一样,至少你会看到问题所在。

我只是遇到了类似的问题并且浪费了很多时间,也许这可以帮助某些人。

我的环境是100%.Net 4.5,我的代码只使用SignedXml类。 但SAML断言在一个地方被接受而在另一个地方被拒绝。

事实certificate,一个地方通过使用PreserveWhitespace = true初始化的XmlDocument实例加载断言,而另一个不是。

并且断言是相当印刷的,所以它有回车和很多缩进空间。 删除所有回车符和缩进空格修复了我的问题。

Saml和Timores有类似的问题。 Saml需要从Base64解码,但我首先使用:

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

但是这使用了ASCII解码并且遇到了特殊字符的问题。 Witch意味着XML与签名时略有不同,这就是失败的原因。 将其更改为:

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

它适用于所有情况。

所以请确保您使用正确的编码!