使用来自C#.Net CNG存储的密钥对文件进行ECDSA签名。

23

我正在尝试使用CNG API和来自Microsoft证书存储的证书使用ECDSA对文件进行签名。我已经阅读了大量文档并接近完成,但我遇到了从证书导入私钥的问题。我已经用RSA做过这件事,但似乎做法完全不同。以下是我目前的代码:

    static void signFile()
    {
        X509Certificate2 myCert = 
             selectCert(StoreName.My, 
                        StoreLocation.CurrentUser, 
                        "Select a Certificate",
                        "Please select a certificate from the list below:");

        Console.Write("Path for file to sign: ");
        string path = Console.ReadLine();
        TextReader file = null;
        try
        {
            file = new StreamReader(path);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
            Console.Write("\nPress any key to return to the main menu: ");
            Console.ReadKey();
        }
        UnicodeEncoding encoding = new UnicodeEncoding();
        byte[] data = encoding.GetBytes(file.ReadToEnd());
        ECDsaCng dsa = new ECDsaCng(
            CngKey.Import(StringToByteArray(myCert.PrivateKey.ToString()),
                          CngKeyBlobFormat.EccPrivateBlob,
                          CngProvider.MicrosoftSoftwareKeyStorageProvider));

        dsa.HashAlgorithm = CngAlgorithm.Sha384;
        byte[] sig = dsa.SignData(data);
        TextWriter signatureFile = new StreamWriter("signature.txt");
        signatureFile.WriteLine("-----BEGIN SHA384 SIGNATURE-----" + 
                                ByteArrayToString(sig) + 
                                "-----END SHA384 SIGNATURE-----");
        signatureFile.Close();
    }

我遇到了以下错误:

System.NotSupportedException:不支持证书密钥算法。

我的证书是ECDSA_P256 sha384ECDSA,具有以下扩展:

Digital Signature, Non-repudiation, independent signing revocation list (CRL), CRL Signing (CRL) (c2)
Server Authentication (1.3.6.1.5.5.7.3.1)
Client Authentication (1.3.6.1.5.5.7.3.2)
Code Signing (1.3.6.1.5.5.7.3.3)
Unknown Key Usage (1.3.6.1.4.1.311.2.1.22)
Unknown Key Usage (1.3.6.1.4.1.311.2.1.21)
IKE-intermediary IP-security (1.3.6.1.5.5.8.2.2)

看起来证书可能是问题所在,但我不确定是否可能是代码的问题。


这是我的带有公钥的证书:

Certificate:
Data:
    Version: 3 (0x2)
    Serial Number: 2 (0x2)
Signature Algorithm: ecdsa-with-SHA384
    Issuer: C=##, O=#######, OU=#####, OU=#####, CN=###########
    Validity
        Not Before: Apr 27 16:35:51 2012 GMT
        Not After : Apr 26 16:35:51 2017 GMT
    Subject: C=##, O=###########, OU=#####, CN=#############
    Subject Public Key Info:
        Public Key Algorithm: id-ecPublicKey
            Public-Key: (256 bit)
            pub:
                04:fc:d5:ce:ad:1f:0c:19:b9:3d:2b:bd:7d:f0:8c:
                44:46:db:e3:42:14:b1:1a:9f:7c:ab:e1:be:ad:a5:
                0c:03:2d:0f:ff:3f:10:d4:69:eb:4c:82:a1:2a:61:
                56:45:03:04:a6:49:f7:16:6e:dd:60:22:c6:20:c5:
                4d:44:49:21:41
            ASN1 OID: prime256v1
    X509v3 extensions:
        X509v3 Key Usage: critical
            Digital Signature, Non Repudiation, CRL Sign
        X509v3 Extended Key Usage: critical
            TLS Web Server Authentication, TLS Web Client Authentication, Co
de Signing, Microsoft Commercial Code Signing, Microsoft Individual Code Signing
, 1.3.6.1.5.5.8.2.2
        X509v3 Authority Key Identifier:
            DirName:/C=##/O=#######/OU=#####/OU=#####/CN=######
            serial:01
        X509v3 Subject Key Identifier:
            B7:A8:F9:55:9A:43:9E:BE:1C:4B:62:52:91:C2:F1:39:72:E1:CE:1B
        X509v3 Basic Constraints: critical
            CA:FALSE
Signature Algorithm: ecdsa-with-SHA384
     30:81:88:02:42:01:75:55:f3:64:f9:aa:2a:66:55:b1:ca:dc:
     86:ac:1f:7d:2a:ec:10:87:db:74:88:0e:77:e3:18:82:15:a7:
     32:91:1a:2d:ea:07:2e:78:8d:dc:8a:18:3c:2b:5a:9b:6a:0f:
     97:f6:f8:8d:c5:fc:0e:9f:20:e9:b0:16:90:1a:c4:58:ac:02:
     42:01:dc:b3:88:ae:44:54:c4:e0:b7:c2:37:88:0b:19:6b:96:
     99:f7:21:12:45:12:21:e5:ab:83:39:a6:47:3a:08:87:b0:fa:
     0e:31:1b:97:83:8d:65:30:a1:43:c1:82:27:77:6e:93:89:1b:
     bd:57:b1:7a:54:9e:cc:e1:44:cc:74:16:c5

微软的椭圆曲线密码学(ECC)支持相当有限。它只提供“基于NIST标准素数曲线P-256、P-384和P-521的椭圆曲线DSA(ECDSA)”,并且这些应该是“命名”曲线。您知道证书中有哪些EC域参数吗?或者,您可以粘贴证书的十六进制、base64或ASN.1文本,以便我们找出。 - Maarten Bodewes
您可以编辑您的问题而不是回答来提供更多信息。重新阅读问题,证书本身不包含私钥。通常,您需要创建一个密钥对,创建一个包含公钥的证书请求,并使用私钥签名。然后CA从中制作证书并将其发送回来。该证书仅包含公钥。因此,当您尝试导入私钥时,它是以什么格式?PKCS#12?那将是.pkf.p12文件扩展名。 - Maarten Bodewes
啊,我明白你错在哪里了,应该戴上我的眼镜。ToString方法不会对私钥进行编码,它只是打印一些关于私钥的信息。你可以使用DSACryptoServiceProvider,它实际上扩展了PrivateKey属性,而不是对私钥进行编码/重新编码。 - Maarten Bodewes
那么我应该使用DSACryptoServiceProvider来实际完成签名,还是只需要处理EC密钥的导入?如果您可以提供一些样例代码,我就可以把这个问题标记为已解决了。顺便再次感谢您的帮助。 - Jim
此外,根据互联网工程任务组(http://tools.ietf.org/search/rfc3279#page-13)的规定,公钥算法属性是正确的,因此似乎这是微软的问题。我测试了一个微软的示例程序(丢失了URL),在那里遇到了同样的错误。看来,与http://msdn.microsoft.com/en-us/library/windows/desktop/bb204775(v=vs.85).aspx#suite_b_support所说的不同,他们实际上无法支持ECC。 - Jim
你能解决这个问题吗?你可以发布你的解决方案吗? - DeepSpace101
3个回答

7

.NET 4.6.1解决了这个问题的核心需求。 新代码将是

...
byte[] sig;
using (ECDsa ecdsa = cert.GetECDsaPrivateKey())
{
    if (ecdsa == null) throw new Exception("Not an ECDSA cert, or has no private key");

    sig = ecdsa.SignData(data, HashAlgorithmName.SHA384);
}

.NET 4.6.1修复了一些证书密钥返回为ECDH并因此失败的问题。(好吧,它没有解决某些私钥被视为ECDH的问题 - 这与服务器身份验证EKU无关,但是这是一个好的猜测 - 但现在将这些密钥视为有效)。


7

我一直在与 ECDsa 和 CngKey 以及 X509 证书进行斗争,这些问题困扰了我很长一段时间。最终,我们使用了自己创建的带有 ECDsa_P256 SHA256 的 CngKeys,但我确信在深入研究 CryptoApi 时学到了一些东西:

当您拥有一个被标记为“服务器验证(1.3.6.1.5.5.7.3.1)”(用于 SSL 证书)的证书时,您的证书将包含密钥交换算法 ECDH。某种程度上,这会“优先于”ECDsa 算法组。因此,您会遇到可怕的“不支持证书密钥算法”的问题。

我花了一个多小时与赛门铁克(Symantec)合作,但他们无法解决这个难题,最后告诉我“抱歉,我们不支持除 SSL 外的任何 SSL 证书用途。”

您可以通过 Codeplex 的 CLRSecurity(http://clrsecurity.codeplex.com/)从证书中获取您的私有 CngKey。这会使您的 x509Certificate2 具有一个允许执行以下代码的扩展程序:

X509Certificate cer = <getyourcertcode>;
CngKey k = cer.GetCngPrivateKey();

检查 "k" 并确认你的算法组可能与预期不同。我的是 ECDH...

我现在尝试的解决方案是设置一个新的CA服务器并强制它做我想要的事情。基本上,这将是一个仅用于代码签名的X509证书...

"X509v3密钥用途:关键数字签名"可能是唯一允许的用途...

希望这能帮助其他人:-)


你如何从公钥创建一个CngKey?CLRSecurity库似乎没有提供“GetCngPublicKey()”扩展。我该如何使用证书存储中的签名者公钥验证ECDsa签名? - Dan Turner
看一下我的答案:https://dev59.com/jWLVa4cB1Zd3GeqP1fqx#22637599。像这样验证:using (ECDsaCng ecsdKey = new ECDsaCng(CngKey.Import(key, CngKeyBlobFormat.EccPublicBlob))) { if (ecsdKey.VerifyData(data, signature)) Console.WriteLine("数据正确"); else Console.WriteLine("数据错误"); } - Henrik N.
你的回答似乎与证书存储中的X509证书无关? - Dan Turner
上帝保佑你!我在互联网上搜索了4个多小时,试图找到如何从证书中获取该死的私钥!!你提供的Codeplex安全程序集链接正是我一直在寻找的!! - Sudhanshu Mishra

2

1
我之前使用的是Windows 7,但现在我已经“升级”到了8。 - Jim
好的,你可以忽略我的回答,因为这个问题在Windows 7中不会发生,预计在Windows 8中也不会发生。 - Anssssss

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