关于c#:如何在.NET中验证RSA-SHA512 XML签名?

How to Verify a RSA-SHA512 XML Signature in .NET?

在有关signedxml的msdn站点的帮助下,我可以轻松地验证XML DSIG是否正确。如果使用签名方法sha1,它就可以完美地工作。

但是,当我收到signaturemethod rsa-sha512(http://www.w3.org/2001/04/xmldsig more rsa-sha512)时,checkSignature()中断,出现一个加密异常:无法为提供的签名算法创建signatureDescription。

似乎checkSignature()无法验证RSA-SHA512签名。

有人知道如何检查这些签名吗?

代码取自msdn站点,是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?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>


您可以验证RSA SHA512签名,但是您必须自己实现和注册签名描述。

签名说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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注册此描述:

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

我在Windows7 64位上用.NET 4.0测试了它。


根据我的研究,SignedXml实现只支持以下签名方法:

1
2
3
4
5
6
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

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

1
http://www.w3.org/2001/04/xmldsig-more#rsa-sha512

不支持用作签名算法。

最终,所有的加密方法都会下降到CryptoConfig.CreateFromName,其中rsa-sha512返回空值。

编辑:我可能刚刚找到了一个让它工作的方法。以下代码段对我有用:

1
2
3
4
5
6
7
8
9
        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;

这应该在您签名/验证之前调用。

这是基于观察到实际散列验证来自证书,算法名称仅用作保护。如果您欺骗配置认为RSA-SHA512是受支持的(通过指向同一个未使用的RSA-SHA1格式化程序),事情就会开始工作。

编辑2:在进一步调查涉及咨询来源后

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来实现,因为签名和验证似乎都支持重载版本:

1
2
signedXml.ComputeSignature( KeyedHashAlgorithm hash );
signedXml.CheckSignature( KeyedHashAlgorithm hash );