将私钥转换为 RSACryptoServiceProvider 不起作用

10
我有一个X509Certificate2变量,我正在尝试将变量的私钥转换为RSACryptoServiceProvider。
RSACryptoServiceProvider pkey = (RSACryptoServiceProvider)cert.PrivateKey;

然而我遇到了这个异常。

System.InvalidCastException:'无法将类型为'System.Security.Cryptography.RSACng'的对象强制转换为类型'System.Security.Cryptography.RSACryptoServiceProvider''。

这很奇怪,因为stackoverflow上其他答案建议使用和我的相同的过程,但我却遇到了异常。 有什么解决方案吗?


2
文档中提到:“它不能完全替代现有的RSACryptoServiceProvider使用。”因此,看起来您需要将其转换为RSACng并与之一起使用。如果此API(我不了解)可以返回其中任何一个,则需要在运行时检查类型,例如使用“as”。 - Rup
1
然后,您将不得不更改下一个操作以使用 RSACng(如果可能),或者调试您所拥有的内容,以查找为什么会得到预期的 RSACryptoServiceProvider 而不是 RSACng。从参考源代码的第一眼看来,它似乎无法提供 RSACng:https://referencesource.microsoft.com/#system/security/system/security/cryptography/x509/x509certificate2.cs,776 这是哪个版本的 .NET?您是自己设置 PrivateKey 属性还是让类根据其解析的数据创建它? - Rup
1
鉴于问题的说明已经更加清晰,我会重新查看并删除我的回答。 :) - James
1
你能帮我试一下这个吗:RSAParameters RSAParams = cert.ExportParameters(true);。我对RSACng对象不是很了解,但文档建议这将把密钥信息导出到一个RSAParams对象中,这应该是你的目标。 - James
1
@James 我认为你是对的,如果你想发布这个答案的话:将输出转换为RSA并更新随后使用RSA的代码,或者导出参数并继续使用。在我看来,.NET Core 2代码将导出RSACng或RSACryptoServiceProvider,与文档相反 :-/ https://github.com/dotnet/corefx/blob/v2.2.4/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/CertificatePal.PrivateKey.cs#L175 - Rup
显示剩余10条评论
4个回答

8

在几次尝试和评论讨论后,我得出了以下解决方案。

            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以更改导出策略。现在完美运行。

2
你可能需要将(cert.PrivateKey as RSACng).Key这一部分的执行条件设置为(cert.PrivateKey is RSACng),因为如果你得到了一个RSACryptoServiceProvider(或RSAOpenSsl),那么这里就会出现NullPointerException。但很高兴你已经解决了这个问题! - Rup

6

我想指出还有一种可以使用的扩展方法:

using System.Security.Cryptography.X509Certificates;

...

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

3
在我的情况下,当尝试将本地存储证书转换为 RSACryptoServiceProvider 时,会出现相同的问题,如下所示:

RSACryptoServiceProvider encryptProvider = 
                         certificate.PrivateKey as RSACryptoServiceProvider;

通过使用RSA而不是RSACryptoServiceProvider修复了问题。
在这里提供说明,以防有人想知道如何做)。
要将某个证书存储到您的计算机中,请打开Visual Studio Developer Command并键入以下命令:
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();
}

0
您可以通过在创建证书时就正确设置出口策略来避免设置出口策略的代码。我使用了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));

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