使用sha256进行SSO SAML的Signed XML签名验证

8

使用VS 2008和.NET Framework 3.5在Windows 2003服务器上。

我们已经实施了基于SAML的SSO以提高安全性。我们在服务提供商端工作,验证来自客户系统的已签名XML SAML Assertuib令牌。 到目前为止,我们遇到的所有已签名文档都使用签名算法“rsa-sha1”,但现在我们有一个新客户发送的文件签名算法为“rsa-sha256”,问题就开始了。

public static string VerifySignature()
{
    if (m_xmlDoc == null)
        return "Could not load XMLDocument ";

    try
    {
        XmlNamespaceManager nsm = new XmlNamespaceManager(new NameTable());
        nsm.AddNamespace("dsig", SignedXml.XmlDsigNamespaceUrl);
        XmlElement sigElt = (XmlElement)m_xmlDoc.SelectSingleNode(
            "//dsig:Signature", nsm);

        // Load the signature for verification
        SignedXml sig = new SignedXml(m_xmlDoc);
        sig.LoadXml(sigElt);

        if (!sig.CheckSignature())
            return "Invalid Signature";
    }
    catch (Exception ex)
    {
        return ex.Message;
    }
    return string.Empty;
}

现在,当我尝试为这个新客户使用相同的代码(使用 rsa-sha256h 签名算法)时,它不能正常工作,我收到了错误消息“无法为提供的签名算法创建 SignatureDescription。”通过阅读过去2-3天许多博客和文章,我发现 SignedXml 不支持 sha256。好的,但接下来怎么做呢?有些地方提到使用 WIF,我也检查并尝试了this。我还试图使用 RSAPKCS1SignatureDeformatter 的 VerifySignature 方法,但不确定要传递哪两个参数。

我曾经认为加密算法被支持是因为它支持X509Certificate2对象。sig.CheckSignature(...)方法有一个重载,接受两个参数,即X509Certificate2和bool。你尝试过使用它并传入证书和true吗? - Randall Borck
4个回答

9
Dotnet 4.6.2+具有较新的内置sha哈希。对于dotnet 4 +,要访问rsa-sha512、rsa-sha384和rsa-sha256,应在某个地方包含此代码。
/// <summary>Declare the signature type for rsa-sha512</summary>
public class RsaPkCs1Sha512SignatureDescription : SignatureDescription
{
    public RsaPkCs1Sha512SignatureDescription()
    {
        KeyAlgorithm = typeof(RSACryptoServiceProvider).FullName;
        DigestAlgorithm = typeof(SHA512CryptoServiceProvider).FullName;
        FormatterAlgorithm = typeof(RSAPKCS1SignatureFormatter).FullName;
        DeformatterAlgorithm = typeof(RSAPKCS1SignatureDeformatter).FullName;
    }

    public override AsymmetricSignatureDeformatter CreateDeformatter(AsymmetricAlgorithm key)
    {
        var sigProcessor = (AsymmetricSignatureDeformatter)CryptoConfig.CreateFromName(DeformatterAlgorithm);
        sigProcessor.SetKey(key);
        sigProcessor.SetHashAlgorithm("SHA512");
        return sigProcessor;
    }

    public override AsymmetricSignatureFormatter CreateFormatter(AsymmetricAlgorithm key)
    {
        var sigProcessor =
            (AsymmetricSignatureFormatter)CryptoConfig.CreateFromName(FormatterAlgorithm);
        sigProcessor.SetKey(key);
        sigProcessor.SetHashAlgorithm("SHA512");
        return sigProcessor;
    }
}

/// <summary>Declare the signature type for rsa-sha384</summary>
public class RsaPkCs1Sha384SignatureDescription : SignatureDescription {
    public RsaPkCs1Sha384SignatureDescription()
    {
        KeyAlgorithm = typeof(RSACryptoServiceProvider).FullName;
        DigestAlgorithm = typeof(SHA384CryptoServiceProvider).FullName;
        FormatterAlgorithm = typeof(RSAPKCS1SignatureFormatter).FullName;
        DeformatterAlgorithm = typeof(RSAPKCS1SignatureDeformatter).FullName;
    }

    public override AsymmetricSignatureDeformatter CreateDeformatter(AsymmetricAlgorithm key)
    {
        var sigProcessor = (AsymmetricSignatureDeformatter) CryptoConfig.CreateFromName(DeformatterAlgorithm);
        sigProcessor.SetKey(key);
        sigProcessor.SetHashAlgorithm("SHA384");
        return sigProcessor;
    }

    public override AsymmetricSignatureFormatter CreateFormatter(AsymmetricAlgorithm key)
    {
        var sigProcessor =
            (AsymmetricSignatureFormatter)CryptoConfig.CreateFromName(FormatterAlgorithm);
        sigProcessor.SetKey(key);
        sigProcessor.SetHashAlgorithm("SHA384");
        return sigProcessor;
    }
}

/// <summary>Declare the signature type for rsa-sha256</summary>
public class RsaPkCs1Sha256SignatureDescription : SignatureDescription
{
    public RsaPkCs1Sha256SignatureDescription()
    {
        KeyAlgorithm = typeof(RSACryptoServiceProvider).FullName;
        DigestAlgorithm = typeof(SHA256CryptoServiceProvider).FullName;
        FormatterAlgorithm = typeof(RSAPKCS1SignatureFormatter).FullName;
        DeformatterAlgorithm = typeof(RSAPKCS1SignatureDeformatter).FullName;
    }

    public override AsymmetricSignatureDeformatter CreateDeformatter(AsymmetricAlgorithm key)
    {
        var sigProcessor =
            (AsymmetricSignatureDeformatter) CryptoConfig.CreateFromName(DeformatterAlgorithm);
        sigProcessor.SetKey(key);
        sigProcessor.SetHashAlgorithm("SHA256");
        return sigProcessor;
    }

    public override AsymmetricSignatureFormatter CreateFormatter(AsymmetricAlgorithm key)
    {
        var sigProcessor =
            (AsymmetricSignatureFormatter)CryptoConfig.CreateFromName(FormatterAlgorithm);
        sigProcessor.SetKey(key);
        sigProcessor.SetHashAlgorithm("SHA256");
        return sigProcessor;
    }
}

然后,您需要通过调用以下代码来激活这些sig描述。您只需要调用一次,因此如果愿意,可以从静态构造函数中调用它。

    CryptoConfig.AddAlgorithm(typeof(RsaPkCs1Sha512SignatureDescription),
        "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512");
    CryptoConfig.AddAlgorithm(typeof(RsaPkCs1Sha384SignatureDescription),
        "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384");
    CryptoConfig.AddAlgorithm(typeof(RsaPkCs1Sha256SignatureDescription),
        "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");

Microsoft的Carlos LopezBitSchupster以及Andrew在SO上致敬。


3
. . . 并且 . . . 框架 4.6.2 可以解决这个问题,无需任何额外的代码。 - O. Jones
@OllieJones - 你知道你的代码在将来移植到目标4.6.2版本时是否会出现问题吗? - Stephan G
1
@StephanG,我已经顺利升级到4.6.1版本。据我了解,在4.6.2版本中不再需要此代码,因为他们添加了新的哈希值。请参阅https://blogs.msdn.microsoft.com/dotnet/2016/08/02/announcing-net-framework-4-6-2/中的X509部分。我还没有验证这个事实。 - O. Jones

4
对于 .net 4 及更早版本,我发现只需添加来自 http://clrsecurity.codeplex.com/ 的 Security.Cryptography,以下代码即可正常运行。(注意 X509CertificateFinder 是我自己编写的,用于通过指纹在证书存储中查找签名证书)
        /// <summary>
        /// Validate an XmlDocuments signature
        /// </summary>
        /// <param name="xnlDoc"> The saml response with the signature elemenet to validate </param>
        /// <returns> True if signature can be validated with certificate </returns>
        public bool ValidateX509CertificateSignature(XmlDocument xnlDoc)
        {
            XmlNodeList XMLSignatures = xnlDoc.GetElementsByTagName("Signature", "http://www.w3.org/2000/09/xmldsig#");

            // Checking If the Response or the Assertion has been signed once and only once.
            if (XMLSignatures.Count != 1) return false;

            var signedXmlDoc = new SignedXml(xnlDoc);
            signedXmlDoc.LoadXml((XmlElement)XMLSignatures[0]);

            var certFinder = new X509CertificateFinder();
            var foundCert = certFinder.GetSignatureCertificate();

            CryptoConfig.AddAlgorithm(typeof(RSAPKCS1SHA256SignatureDescription), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
            return signedXmlDoc.CheckSignature(foundCert,false);
        }

2
从.NET 4.5开始,与.NET 4.0及之前版本不同,您无需安装任何第三方库。您只需添加对System.Deployment的引用,并在进程中调用System.Security.Cryptography.CryptoConfig.AddAlgorithm(typeof (RSAPKCS1SHA256SignatureDescription), RsaSha256Namespace);一次,之后原始问题中的代码将能够使用SHA256哈希。另请参考http://blogs.msdn.com/b/winsdk/archive/2015/11/15/using-sha256-with-signedxml.aspx - Rory
const string RsaSha256Namespace = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; - maddoxej

2
这可能算得上是“简单”的解决方案,但不一定是“完美”的。对于我们遇到的少数客户,我们建议他们更改其IdP,以使用SHA-1进行签名。他们能够做出更改,当他们这样做时,它就可以正常工作。
虽然这不是技术解决方案,但在实际应用中已经被证明是有效的,因此我想提一下。

我曾经有客户拒绝进行那个更改,唉。 - O. Jones

0
只需将其更新到.NET Framework 4.6.01590或更高版本,即可支持SHA-512,无需进行任何代码更改。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接