【问题标题】:Verify signature on SAML assertion验证 SAML 断言的签名
【发布时间】:2014-08-19 22:40:42
【问题描述】:

我有两个签名,一个在响应上(验证),一个在嵌套 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),并且有更多的箍要跳过。请指教。

【问题讨论】:

    标签: c# saml


    【解决方案1】:

    此代码使用 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

    【讨论】:

    • 在这种情况下 samlMetadataXmlToExtractCertData 是什么?
    【解决方案2】:

    我不知道为什么,但看起来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
    }
    

    这大约是重新解析 OuterXml 的两倍,这是其他人根据我使用 8kB SAML 响应文档进行的测试所建议的。

    【讨论】:

      【解决方案3】:

      我发现this answer 引用了another answerthe same guy)并总结如下: 的实现很古怪,签名块必须是DocumentElement 的子代。 找到this article 也更好地解决了问题。

      修改后的代码

      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 令牌,它与给定的代码一起工作。我用的那个肯定有问题。

      【讨论】:

      • 你的评论下面有什么代码 // 验证证书?
      • 这是我最终文件的gist。有些事情是特定于我的需求或我的设置的,但这些应该是显而易见的并且易于调整。
      猜你喜欢
      • 2018-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-12
      • 1970-01-01
      • 1970-01-01
      • 2016-01-15
      相关资源
      最近更新 更多