【问题标题】:SignedXml checksignature returns falseSignedXml 检查签名返回 false
【发布时间】:2011-04-25 12:52:48
【问题描述】:

我已经查看了此处有关此问题的其他帖子,但似乎都没有解决我的情况。

上周我一直在尝试验证 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 是签名元素,signedXml 是使用 new SignedXml(xmlElement) 创建的 SignedXml 对象

checksignature 返回的两个客户端都是 false,但是当我使用我的证书创建自己的签名 saml 时,它会返回 true。

我在这里缺少什么?

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

【问题讨论】:

  • 我猜,你收到的断言是用非.net语言生成的,比如Java?
  • SetSigningKeyFromKeyInfo(signedXml); 是做什么的?
  • 当您对断言进行 base64 解码后,您是否可以将 xml 转储到文件中并将其与您自己的断言之一进行比较以检查(细微的)结构不一致?
  • 查看我的编辑。就比较这两个断言而言,是的。我已经查看了这两个断言,但找不到任何突出我的地方。
  • 您可能想查看这个实现:digitaliser.dk/resource/558794/artefact/oiosaml-dot-net-1.6.zip 这是我几年前参与编写的 .net 中 SAML 协议的开源实现。我没有检查当前版本,但您应该可以从中获得一些灵感。

标签: c# .net xml-signature


【解决方案1】:

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

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

否则,大量的调试等待着您。您可以调试框架,或通过反射手动调用其内部方法,以查看它如何计算 XML 片段和签名。并对其他实现做同样的事情。基本上,您需要查看在这两种情况下签名的确切字节流。这是签名前转换的最后一步。如果这些字节流匹配,那么根据我的经验,RSA 签名部分不会有任何问题。如果这些不匹配,就像你的情况一样,至少你会看到问题出在哪里。

【讨论】:

    【解决方案2】:

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

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

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

    而且断言打印得很漂亮,所以它有回车和很多缩进空格。 删除所有回车和缩进空格解决了我的问题。

    【讨论】:

    • XmlDocument.PreserveWhitespace 的默认值为 false。切换到 true 并能够从 CheckSignature 中获得“true”。我猜在计算带有证书的签名时使用了空格,而当剥离签名时,签名被视为无效。
    【解决方案3】:

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

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

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

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

    它适用于所有情况。

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

    【讨论】:

      【解决方案4】:

      我在这个问题上花了很多时间,然后意识到我没有用正确的证书检查签名。

      所以我决定检查我在 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。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-12-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多