如何在.NET中验证RSA-SHA512 XML签名?

6
通过MSDN签名XML网站的帮助,我可以轻松验证XML数字签名是否正确。如果使用sha1签名方法,它可以完美地工作。
然而,当我收到SignatureMethodRSA-SHA512 (http://www.w3.org/2001/04/xmldsig-more#rsa-sha512)时,CheckSignature()会出现CryptograhicException错误:无法为提供的签名算法创建SignatureDescription。 看起来CheckSignature()无法验证RSA-SHA512签名。 有人知道如何检查这些类型的签名吗?
代码取自MSDN网站,如下所示:
public static bool VerifyXml(XmlDocument doc, bool removeSignatureElement = false)
{
    // Check arguments.
    if (doc == null)
        throw new ArgumentException("doc");

    // Create a new SignedXml object and pass it the XML document class.
    SignedXml signedXml = new SignedXml(doc);

    // Find the "Signature" node and create a new XmlNodeList object.
    XmlNodeList nodeList = doc.GetElementsByTagName("Signature", Constants.NamespaceDSig);

    // Throw an exception if no signature was found.
    if (nodeList.Count < 1)
    {
        throw new CryptographicException("Verification failed: No Signature was found in the document.");
    }

    // This example only supports one signature for the entire XML document.  Throw an exception if more than one signature was found.
    if (nodeList.Count > 1)
    {
        throw new CryptographicException("Verification failed: More that one signature was found for the document.");
    }

    // Load the first <signature> node.  
    signedXml.LoadXml((XmlElement)nodeList[0]);

    // Check the signature and return the result.
    bool signedCorrectly = signedXml.CheckSignature(); // throws the Exception!!!

    return signedCorrectly;
}

签名的XML是:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Notification xmlns="http://www.xxxxxxxxxxx.xx/xxxxx">
    <xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Type="http://www.w3.org/2001/04/xmlenc#Content"> ... </xenc:EncryptedData>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:SignedInfo>
            <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
            <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"/>
            <ds:Reference URI="">
                <ds:Transforms>
                    <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
                    <ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"/>
                </ds:Transforms>
                <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                <ds:DigestValue>WsHcyNL7Jh8HSzR9ArzTqomBkHs=</ds:DigestValue>
            </ds:Reference>
        </ds:SignedInfo>
        <ds:SignatureValue>
pWDatSEbypIUVQR9NFmLkB9kKWjMb6rKWGFFvGqT5tOUILeDhMHUqjCRB9v/g6yYdogC9TRWouhz
...VoZAIBs7EqCbLt7RgpB4GHWc9E3qp65NaCgluw==
        </ds:SignatureValue>
        <ds:KeyInfo>
            <ds:X509Data>
                <ds:X509Certificate>
MIIG+zCCBOOgAwIBAgIHAe2+sRfTfDANBgkqhkiG9w0BAQUFADCBkTELMAkGA1UEBhMCQVQxDTAL
...tvawqBjOfkw1yeDzsDMJHfMuAcpYfrEL
                </ds:X509Certificate>
            </ds:X509Data>
        </ds:KeyInfo>
    </ds:Signature>
</Notification>

1
你能否展示一份签名文档的例子吗?特别是,KeyInfo的内容是什么?用什么形式传递证书信息? - Wiktor Zychla
谢谢您的提示,我已经添加了XML。 KeyInfo 包含证书,经过base64解码。 - chanser
1
很遗憾,根据我的研究,RSA-SHA512 算法不受支持。我得到了非常明确的异常。反编译 CryptoConfig 类,特别是 DefaultNameHT 属性,以获取支持算法的列表。 - Wiktor Zychla
嗨,Wiktor,恐怕你是对的。我反编译了 SignedXml,只能找到一个用于 rsa-sha1 的命名空间:XmlDsigRSASHA1Url。 我认为你可以将此作为答案发布;这对其他人会很有用。 - chanser
您是否知道其他成功验证RSA SHA512签名的方法? - chanser
我编辑了我的回答。我可能找到了一种让它工作的方法。 - Wiktor Zychla
2个回答

9

您可以验证RSA SHA512签名,但您需要自行实现和注册签名描述。

签名描述:

public sealed class RSAPKCS1SHA512SignatureDescription : SignatureDescription
{
    public RSAPKCS1SHA512SignatureDescription()
    {
        KeyAlgorithm = typeof( RSACryptoServiceProvider ).FullName;
        DigestAlgorithm = typeof( SHA512Managed ).FullName;
        FormatterAlgorithm = typeof( RSAPKCS1SignatureFormatter ).FullName;
        DeformatterAlgorithm = typeof( RSAPKCS1SignatureDeformatter ).FullName;
    }

    public override AsymmetricSignatureDeformatter CreateDeformatter( AsymmetricAlgorithm key )
    {
        if( key == null )
        {
            throw new ArgumentNullException( "key" );
        }

        var deformatter = new RSAPKCS1SignatureDeformatter( key );
        deformatter.SetHashAlgorithm( "SHA512" );
        return deformatter;
    }

    public override AsymmetricSignatureFormatter CreateFormatter( AsymmetricAlgorithm key )
    {
        if( key == null )
        {
            throw new ArgumentNullException( "key" );
        }

        var formatter = new RSAPKCS1SignatureFormatter( key );
        formatter.SetHashAlgorithm( "SHA512" );
        return formatter;
    }
}

在你的代码中,你需要使用CryptoConfig注册这个描述:

const string XmlDsigRsaSha512 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512";
CryptoConfig.AddAlgorithm( typeof( RSAPKCS1SHA512SignatureDescription ), XmlDsigRsaSha512 );

我使用Windows 7 64位系统测试了.Net 4.0。

1
根据我的研究,只有以下签名方法得到了SignedXml实现的支持:
http://www.w3.org/2000/09/xmldsig#hmac-sha1
http://www.w3.org/2001/04/xmldsig-more#hmac-sha256
http://www.w3.org/2001/04/xmldsig-more#hmac-sha384
http://www.w3.org/2001/04/xmldsig-more#hmac-sha512
http://www.w3.org/2001/04/xmldsig-more#hmac-md5
http://www.w3.org/2001/04/xmldsig-more#hmac-ripemd160

这些可以用于签名和验证。不幸的是,

此处缺少上下文,无法翻译完整意思。
http://www.w3.org/2001/04/xmldsig-more#rsa-sha512

你使用的签名算法不受支持。

最终,所有加密方法都归结为 CryptoConfig.CreateFromName,其中 rsa-sha512 返回 null。

编辑: 我可能已经找到了一种使其工作的方法。以下代码片段适用于我:

        Dictionary<string, object> ht =
            (Dictionary<string, object>)typeof( CryptoConfig ).InvokeMember( 
            "DefaultNameHT", System.Reflection.BindingFlags.GetProperty | 
            System.Reflection.BindingFlags.Static | 
            System.Reflection.BindingFlags.NonPublic, null, typeof( CryptoConfig ), 
            null );

        var o = ht["http://www.w3.org/2000/09/xmldsig#rsa-sha1"];
        ht["http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"] = o;

在签署/验证之前应该称作 before

这是基于观察到实际的哈希验证来自证书,算法名称只是用作保护。如果你欺骗配置认为 RSA-SHA512 支持(通过指向相同的 RSA-SHA1 格式化程序,该程序未被使用),事情开始工作。

Edit2:经进一步调查并咨询来源后。

http://www.dotnetframework.org/default.aspx/4@0/4@0/untmp/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/clr/src/ManagedLibraries/Security/System/Security/Cryptography/Xml/SignedXml@cs/1305376/SignedXml@cs

我认为上述解决方案不会起作用。它只是更改已签署文档中的签名名称,但不幸的是,签名仍然使用RSA-SHA1计算。
唯一使其起作用的方法是将RSA-SHA512实现为KeyedHashAlgoritm,因为签名和验证似乎都支持重载版本:
signedXml.ComputeSignature( KeyedHashAlgorithm hash );
signedXml.CheckSignature( KeyedHashAlgorithm hash );

很遗憾,如果我使用它,它不起作用。在签名之前怎么使用它?它似乎没有与SignedXml连接。你可以分享更多的代码吗? - chanser
它没有连接,但SignedXml在内部使用它。您仍然会收到异常还是验证失败? - Wiktor Zychla
CheckSignature() 只返回 false,但我知道签名是正确的,因为我信任的公共 Web 服务 (http://www.buergerkarte.at/signature-verification/) 告诉我签名是正确的。即使我使用更改后的 ht 对 XML 进行签名,代码仍会生成 sha1。 - chanser
最终,我转向了Java,因为在C#中要做的工作似乎太多了。一个简单的Java示例直接可以验证签名:http://docs.oracle.com/javase/7/docs/technotes/guides/security/xmldsig/Validate.java - chanser
我已编辑了我的回答。经过相当大的努力,我最终放弃了。很抱歉给你带来了虚假的希望。 - Wiktor Zychla

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