将私钥投射到 RSACryptoServiceProvider 不起作用

Casting private key to RSACryptoServiceProvider not working

我有一个 X509Certificate2 变量,我正在尝试将该变量的私钥转换为 RSACryptoServiceProvider

RSACryptoServiceProvider pkey = (RSACryptoServiceProvider)cert.PrivateKey;

但是我遇到了这个异常。

System.InvalidCastException: 'Unable to cast object of type 'System.Security.Cryptography.RSACng' to type 'System.Security.Cryptography.RSACryptoServiceProvider'.'

发生这种情况很奇怪,因为 SO 中的其他答案建议的过程与我的相同,但我得到一个例外。有什么解决办法吗?

经过几次尝试和评论中的讨论,我想出了以下解决方案。

            RSA rsa = (RSA)cert.PrivateKey;
        (cert.PrivateKey as RSACng).Key.SetProperty(
            new CngProperty(
                "Export Policy",
                BitConverter.GetBytes((int)CngExportPolicies.AllowPlaintextExport),
                CngPropertyOptions.Persist));

        RSAParameters RSAParameters = rsa.ExportParameters(true);                      

        AsymmetricCipherKeyPair keypair = DotNetUtilities.GetRsaKeyPair(RSAParameters);

问题是变量 rsa 不可导出。为了改变这一点,我为导出策略设置了一个新的 CngProperty。现在完美运行

在我的例子中,尝试将 本地商店证书 转换为 RSACryptoServiceProvider 时发生了同样的问题,如下所示:

RSACryptoServiceProvider encryptProvider = 
                         certificate.PrivateKey as RSACryptoServiceProvider;

使用 RSA 而不是 RSACryptoServiceProvider 解决了问题。


将说明放在这里,以防有人好奇如何操作 ))。

要将一些证书存储到您的计算机中,请打开 Visual Studio 开发人员命令 并键入以下内容:

makecert -n "CN=JohnDoe" -sr currentuser -ss someCertStore

...您可以在其中指定和值,而不是 "JohnDoe" 和 "demoCertStore"。

现在您可以使用以下代码从本地证书存储访问证书:

public class Program
{
    static void DumpBytes(string title, byte[] bytes)
    {
        Console.Write(title);
        foreach (byte b in bytes)
        {
            Console.Write("{0:X} ", b);
        }

        Console.WriteLine();
    }

    static void Main(string[] args)
    {
        // This will convert our input string into bytes and back
        var converter = new ASCIIEncoding();

        // Get a crypto provider out of the certificate store
        // should be wrapped in using for production code
        var store = new X509Store("someCertStore", StoreLocation.CurrentUser);

        store.Open(OpenFlags.ReadOnly);

        // should be wrapped in using for production code
        X509Certificate2 certificate = store.Certificates[0];

        RSA rsa = (RSA)certificate.PrivateKey;
        (certificate.PrivateKey as RSACng)?.Key.SetProperty(
                                                new CngProperty(
                                                    "Export Policy",
                                                    BitConverter.GetBytes((int)CngExportPolicies.AllowPlaintextExport),
                                                    CngPropertyOptions.Persist));

        string messageToSign = "This is the message I want to sign";
        Console.WriteLine("Message: {0}", messageToSign);
        byte[] messageToSignBytes = converter.GetBytes(messageToSign);

        // need to calculate a hash for this message - this will go into the
        // signature and be used to verify the message
        // Create an implementation of the hashing algorithm we are going to us
        // should be wrapped in using for production code
        DumpBytes("Message to sign in bytes: ", messageToSignBytes);
        HashAlgorithm hasher = new SHA1Managed();

        // Use the hasher to hash the message
        byte[] hash = hasher.ComputeHash(messageToSignBytes);
        DumpBytes("Hash for message: ", hash);

        // Now sign the hash to create a signature
        byte[] signature = rsa.SignHash(hash, HashAlgorithmName.SHA1, RSASignaturePadding.Pss);
        DumpBytes("Signature: ", messageToSignBytes);

        // Now use the signature to perform a successful validation of the mess
        bool validSignature = rsa.VerifyHash(hash: hash,
                                             signature: signature,
                                             hashAlgorithm: HashAlgorithmName.SHA1,
                                             padding: RSASignaturePadding.Pss);
        Console.WriteLine("Correct signature validated OK: {0}", validSignature);

        // Change one byte of the signature
        signature[0] = 99;

        // Now try the using the incorrect signature to validate the message
        bool invalidSignature = rsa.VerifyHash(hash: hash,
                                               signature: signature,
                                               hashAlgorithm: HashAlgorithmName.SHA1,
                                               padding: RSASignaturePadding.Pss);

        Console.WriteLine("Incorrect signature validated OK: {0}", invalidSignature);
        Console.ReadKey();
}

您可以通过简单地创建导出策略已经正确的证书来完全避免设置导出策略的代码。我使用 New-SelfSignedCertificate PowerShell 实用程序创建了一个从一开始就可以导出的证书。
PS C:>New-SelfSignedCertificate -CertStoreLocation "Cert:\CurrentUser\" -Subject "CN=JUSTIN" -KeyExportPolicy Exportable

这不需要:

(certificate.PrivateKey as RSACng)?.Key.SetProperty(new CngProperty("Export Policy", BitConverter.GetBytes((int)CngExportPolicies.AllowPlaintextExport),CngPropertyOptions.Persist));

只是想注意还有一个扩展方法可以使用:

using System.Security.Cryptography.X509Certificates;

...

//certificate is a X509Certificate2
using (var rsa = certificate.GetRSAPrivateKey())
{
  //the var rsa is an RSA object
  //...
}