我已经在这里查看了有关此问题的其他帖子,但这些帖子似乎都没有解决我的情况。
我一直在尝试验证上周的SAML断言,我有2个客户端已经发送给我SAML但我无法验证它。
主要过程是我们获得base64编码的断言并对其进行解码。使用PreserveWhitespace = true将其加载到XmlDocment中。
验证方法是
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返回false,但是当我使用我的证书创建自己签名的saml时,它将返回true。
我在这里缺少什么?
编辑:是的,两个客户端都在Java上,我发布了SetSigningKeyFromKeyInfo方法
答案 0 :(得分:8)
我过去经常处理签名的XML。我只能说这是一场噩梦。基本上,当您对XML进行签名时,它会经历一个名为canonicalization(C14N)的过程。它需要将XML文本转换为可以签名的字节流。空白& XML C14N标准中的命名空间处理很难理解,甚至更难实现。甚至有多种类型的C14N。
.NET实现对它接受的内容非常挑剔。你的其他实现很可能与.NET的实现方式完全不同。这确实非常悲伤。例如,如果您可以在签名之前从源XML中消除空格和命名空间,那可能会有所帮助。此外,如果您可以确保两个实现都使用相同的C14N设置。
否则很多调试都在等着你。您可以调试框架,或者通过反射手动调用其内部方法,以查看它如何计算XML片段和签名。并对其他实现做同样的事情。基本上,您需要查看在两种情况下都签名的确切字节流。这是签署前转换的最后一步。如果这些字节流匹配,那么根据我的经验,您对RSA签名部分没有任何问题。如果那些不匹配,就像你的情况一样,至少你会看到问题所在。
答案 1 :(得分:2)
我遇到了类似的问题并且浪费了很多时间,也许这可以帮助别人。
我的环境是100%.Net 4.5,我的代码只使用SignedXml类。但SAML断言在一个地方被接受而在另一个地方被拒绝。
原来,有一个地方是通过用PreserveWhitespace = true
初始化的XmlDocument实例加载断言,而另一个是没有。
断言已经打印得非常漂亮,所以它有回车和很多缩进空间。 删除所有回车符和缩进空格修复了我的问题。
答案 2 :(得分:1)
与萨米尔(Saml)有类似的问题,如Timores。 Saml需要从Base64解码,但是首先我使用了:
var saml = System.Text.Encoding.Default.GetString(Convert.FromBase64String(samlToken))
但是这使用了ASCII解码,并且在特殊字符方面遇到了麻烦。这意味着XML与签名时的XML略有不同,这就是失败的原因。更改为:
var saml = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(samlToken))
它适用于所有情况。
因此请确保您使用的编码正确!
答案 3 :(得分:0)
我在这个问题上花了很多时间,然后意识到我没有使用正确的证书来检查签名。
因此,我决定检查我在Azure的XML响应文件中收到的证书:
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];
}
}
}
然后:
bool bTest = signedXml.CheckSignature(serviceCertificate , true);
bTest
的值最终设置为true。