验证SAML断言上的签名
本文关键字:SAML 断言 验证 | 更新日期: 2023-09-27 18:18:40
我有两个签名,一个在响应上(进行验证),另一个在嵌套的SAML断言上(不进行验证)。这是我正在使用的压缩代码:
foreach (XmlElement node in xmlDoc.SelectNodes("//*[local-name()='Signature']"))
{// Verify this Signature block
SignedXml signedXml = new SignedXml(node.ParentNode as XmlElement);
signedXml.LoadXml(node);
KeyInfoX509Data x509Data = signedXml.Signature.KeyInfo.OfType<KeyInfoX509Data>().First();
// Verify certificate
X509Certificate2 cert = x509Data.Certificates[0] as X509Certificate2;
log.Info(string.Format("Cert s/n: {0}", cert.SerialNumber));
VerifyX509Chain(cert);// Custom method
// Check for approval
X509Store store = new X509Store(StoreName.TrustedPublisher, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection collection = store.Certificates.Find(X509FindType.FindBySerialNumber, cert.SerialNumber, true);
Debug.Assert(collection.Count == 1);// Standing in for brevity
// Verify signature
signedXml.CheckSignature(cert, true);
}
为完整起见,下面是XML的概要:
<samlp2:Response Destination="http://www.testhabaGoba.com" ID="ResponseId_934151edfe060ceec3067670c2f0f1ea" IssueInstant="2013-09-24T14:33:29.507Z" Version="2.0" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:samlp2="urn:oasis:names:tc:SAML:2.0:protocol">
...
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
...
</ds:Signature>
...
<saml2:Assertion ID="SamlAssertion-05fd8af7f2c9972e69cdbca612d3f3b8" IssueInstant="2013-09-24T14:33:29.496Z" Version="2.0" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
...
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
...
</ds:Signature>
...
</saml2:Assertion>
</samlp2:Response>
我也尝试过只使用带符号的断言,但也失败了。我做错了什么?为什么CheckSignature总是在SAML断言上失败?
编辑事实证明,只有断言签名的那个是java生成的(OpenSAML),并且有更多的困难要克服。请建议。
此代码使用Ultimate SAML (http://www.componentpro.com/saml.net/)验证SAML响应。它有助于验证响应内嵌套的SAML断言签名。
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(samlResponseXmlToVerify);
XmlDocument xmlDocumentMetadata = new XmlDocument();
xmlDocumentMetadata.Load(samlMetadataXmlToExtractCertData);
// Load the SAML response from the XML document.
Response samlResponse = new Response(xmlDocument.DocumentElement);
// Is it signed?
if (samlResponse.IsSigned())
{
// Validate the SAML response with the certificate.
if (!samlResponse.Validate(xmlDocumentMetadata.DocumentElement))
{
throw new ApplicationException("SAML response signature is not valid.");
}
}
查看此在线示例代码了解更多详细信息:http://www.componentpro.com/doc/saml/ComponentPro.Saml.SignableSamlObject.Validate().htm
我发现这个答案引用了另一个答案(由同一个人),并总结如下:.net实现是古怪的,签名块必须是发现这篇文章也以更好的方式解决了这个问题。DocumentElement
的一个子。
foreach (XmlElement node in xmlDoc.SelectNodes("//*[local-name()='Signature']"))
{// Verify this Signature block
// *** BEGIN: ADDED CODE ***
XmlDocument doc = new XmlDocument();
doc.LoadXml(node.ParentNode.OuterXml);
XmlElement signature = doc.SelectSingleNode("//*[local-name()='Signature']") as XmlElement;
// This variable ^^^ is the same as node, just in doc instead of xmlDoc (important distinction)
// *** END: ADDED CODE ***
// Setup
SignedXml signedXml = new SignedXml(node.ParentNode as XmlElement);
signedXml.LoadXml(node);
KeyInfoX509Data x509Data = signedXml.Signature.KeyInfo.OfType<KeyInfoX509Data>().First();
// Verify certificate
...
不幸的是,这仍然不能解释java生成的SAML。需要一些更复杂、甚至更少记录的东西来解释这一点。我收到了一个新的java生成的SAML令牌,它与给定的代码一起工作。
我不知道为什么,但看起来SignedXml
只能验证根文档元素中的签名。我发现从现有的XmlElement
片段创建新的XmlDocument
的最快方法是使用ImportNode
方法。
更新后的验证码看起来像这样:
foreach (XmlElement item in xmlDoc.SelectNodes("//*[local-name()='Signature']"))
{
var node = item;
if (node.ParentNode != xmlDoc.DocumentElement)
{
var doc = new XmlDocument();
var parentNode = doc.ImportNode(item.ParentNode, true);
doc.AppendChild(parentNode);
node = (XmlElement) parentNode.SelectSingleNode("*[local-name()='Signature']");
}
var signedXml = new SignedXml((XmlElement) node.ParentNode);
signedXml.LoadXml(node);
//TODO: validate
}
这比根据我使用8kB SAML响应文档进行的测试所建议的重新解析OuterXml
的速度快两倍。