首页 > 解决方案 > X509Certificate2 与使用 SHA256withRSA 的私钥签名数据

问题描述

这与store with private key 对 X509Certificate2的答案有点相关。

似乎当我想使用 SHA256withRSA 时,我不能直接从证书的 PrivateKey 使用服务提供者——我需要创建新的加密服务提供者:

  var bytes = new byte[] { 0, 1, 2, 3 };

  //_cert - X509Certificate2 with private key
  //csp1 is of type I need, but it won't work
  var csp1 = _cert.PrivateKey as RSACryptoServiceProvider;

  var cspParameters = new CspParameters
  {
    KeyContainerName = csp1.CspKeyContainerInfo.KeyContainerName,
    KeyNumber = csp1.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2,
  };

  var csp2 = new RSACryptoServiceProvider(cspParameters);

  //I can't use csp1 here - will throw "CryptographicException : Invalid algorithm specified."
  //I can use csp1 with "SHA1" though
  var signature = csp2.SignData(bytes, CryptoConfig.MapNameToOID("SHA256"));

我在这里找到了一些关于此的信息:

https://blogs.msdn.microsoft.com/shawnfa/2008/08/25/using-rsacryptoserviceprovider-for-rsa-sha256-signatures/

但是上面的解决方案取自评论部分,我真的不明白为什么我需要跳过箍来使用一种常见的算法。所以我想问的是:

如果需要,可以按如下方式生成带有私钥的证书:

openssl req -x509 -sha256 -newkey rsa:2048 -keyout ./temp/key.key -out ./temp/crt.crt -days 10 –nodes
openssl pkcs12 -export -out .\temp\cert.pfx -inkey .\temp\key.key –in .\temp\crt.crt

标签: c#.netcryptography

解决方案


这完全取决于您的证书来自哪里。就像 MSDN 评论所说,如果它来自Microsoft Base Cryptographic Provider,那么它将不适用于 SHA256。这个 CSP 早在 1996 年就推出了 CryptoAPI 的第一个版本,并且不理解 SHA256,因为那时 SHA256 还不存在。

优雅地检查和处理这个问题的方法是:

public byte[] SignData(RSACryptoServiceProvider csp, byte[] bytes)
{
    byte[] sig = null;
    if ((csp.CspKeyContainerInfo.ProviderType == PROV_RSA_FULL || csp.CspKeyContainerInfo.ProviderType == PROV_RSA_SCHANNEL) && !csp.CspKeyContainerInfo.HardwareDevice)
    {
        var cspParameters = new CspParameters
        {
            KeyContainerName = csp.CspKeyContainerInfo.KeyContainerName,
            KeyNumber = csp.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2,
        };
        using (var csp2 = new RSACryptoServiceProvider(cspParameters))
        {
            sig = csp2.SignData(bytes, CryptoConfig.MapNameToOID("SHA256"));
        }
    }
    else {
        sig = csp.SignData(bytes, CryptoConfig.MapNameToOID("SHA256"));
    }
    return sig;
}

仅供参考, CryptoAPI被弃用,取而代之的是Cryptography API: Next Generation。在 C# 中使用 CNG 做你想做的事情的一种方法是使用System.Security.Cryptography.Cng

...
using (RSA rsa = new RSACng())
{
    byte[] signature = rsa.SignData(message, hashAlgorithm, paddingMode);
    ...
}

推荐阅读