尽管设置了私钥,但密钥集不存在

Keyset does not exist although the PrivateKey is set

我正在尝试使用 CmsSigner 签署消息并附加 X509 证书:

public static byte[] Sign(X509Certificate2 certificate, string keyXml)
{
    ContentInfo contentInfo = new ContentInfo(new Oid("1.2.840.113549.1.7.1"), Encoding.ASCII.GetBytes("hello"));

    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
    rsa.FromXmlString(keyXml);

    using (certificate.GetRSAPrivateKey())
    {
        certificate.PrivateKey = rsa;
    }
    var signer = new CmsSigner(certificate);

    signer.DigestAlgorithm = new Oid("2.16.840.1.101.3.4.2.1");
    signer.SignedAttributes.Add(new Pkcs9SigningTime());

    var signedCms = new SignedCms(contentInfo, false);
    signedCms.ComputeSignature(signer);
    var encodeByteBlock = signedCms.Encode();
    return encodeByteBlock;
}

证书没有密钥(HasPrivateKeyfalse)所以我使用 OpenSSL 生成的正确密钥设置它:

openssl req -new -sha256 -x509 -days 7300 -out ca.crt -keyout ca.key.pem -nodes

我将 ca.key.pem 转换为 XML。

但是调用ComputeSignature时,抛出这个异常:

System.Security.Cryptography.CryptographicException: 'Keyset does not exist'

堆栈跟踪:

at System.Security.Cryptography.Pkcs.PkcsUtils.CreateSignerEncodeInfo(CmsSigner signer, Boolean silent, SafeCryptProvHandle& hProv) at System.Security.Cryptography.Pkcs.SignedCms.Sign(CmsSigner signer, Boolean silent) at System.Security.Cryptography.Pkcs.SignedCms.ComputeSignature(CmsSigner signer, Boolean silent) at System.Security.Cryptography.Pkcs.SignedCms.ComputeSignature(CmsSigner signer) at ConsoleTest472.Program.Sign(X509Certificate2 certificate, String keyXml) in D:\me\Projects\ConsoleTest472\ConsoleTest472\Program.cs:line 56 at ConsoleTest472.Program.Main(String[] args) in D:\me\Projects\ConsoleTest472\ConsoleTest472\Program.cs:line 63

我使用的代码有什么问题? keyset 是不是在设置私钥时设置的?

私钥必须是:

  • 在执行代码的用户的 MY Key Store 中
  • 在机器的 MY 密钥库中,已授予用户权限
  • 与证书一起存储在 PFX/PKCS#12 文件中并作为 X509Certificate2 加载并使用密码保护它。

What is wrong with the code I'm using?

using (certificate.GetRSAPrivateKey())
{
    certificate.PrivateKey = rsa;
}

没有多大意义。您正在获取私钥,然后忽略它,尝试替换它,然后处置它。

更好的版本是

var signedCms = new SignedCms(contentInfo, false);

using (X509Certificate2 certWithKey = certificate.CopyWithPrivateKey(rsa))
{
    var signer = new CmsSigner(certWithKey);

    signer.DigestAlgorithm = new Oid("2.16.840.1.101.3.4.2.1");
    signer.SignedAttributes.Add(new Pkcs9SigningTime());

    signedCms.ComputeSignature(signer);
}

var encodeByteBlock = signedCms.Encode();
return encodeByteBlock;

哪个负责将私钥绑定到证书的副本(而不是进行修改),并做任何必要的工作(在这种情况下,它最终将替换 RSACryptoServiceProvider 密钥使用 RSACng 密钥,因为该平台仅支持 memory-only(临时)密钥的 CNG。