【问题标题】:Casting private key to RSACryptoServiceProvider not working将私钥投射到 RSACryptoServiceProvider 不起作用
【发布时间】:2019-09-20 19:15:09
【问题描述】:

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

RSACryptoServiceProvider pkey = (RSACryptoServiceProvider)cert.PrivateKey;

但是我得到了这个异常。

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

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

【问题讨论】:

  • The docs say “它不是 RSACryptoServiceProvider 现有用途的替代品。”所以看起来你需要将它转换为 RSACng 并使用它。如果这个 API(我不知道)可以返回其中任何一个,那么您需要在运行时检查类型,例如使用as
  • 那么你要么必须更改下一个操作以使用 RSACng(如果可能的话),要么调试你必须找出为什么你得到一个 RSACng 而不是如您所料,RSACryptoServiceProvider。乍一看,从参考源来看,它似乎无法为您提供 RSACng:referencesource.microsoft.com/#system/security/system/security/… 这是 .NET 的哪个版本?您是自己设置 PrivateKey 属性还是让该类从它正在解析的数据中创建它?
  • 根据问题说明删除我的答案。我再看看:)
  • 你能帮我试试这个吗:RSAParameters RSAParams = cert.ExportParameters(true);。我对 RSACng 对象了解不多,但文档表明这会将关键信息导出到 RSAParams 对象中,这是我认为您应该瞄准的目标。
  • 不,它在 RSACng 和 RSACryptoProviderService 实现的RSA interface 上。将 .privateKey 转换为 RSA 并在其上调用 ExportParameters。

标签: c# rsa private-key


【解决方案1】:

所以在 cmets 中经过几次尝试和讨论后,我想出了以下解决方案。

            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。现在完美运行

【讨论】:

  • 您可能需要使(cert.PrivateKey as RSACng).Key 位以(cert.PrivateKey is RSACng) 为条件,就好像您曾经获得过 RSACryptoServiceProvider(或 RSAOpenSsl)一样,那么这里就会出现 NullPointerException。但很高兴你能成功!
【解决方案2】:

在我的情况下,尝试将 本地商店证书 转换为 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();
}

【讨论】:

    【解决方案3】:

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

    这就不需要:

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

    【讨论】:

      【解决方案4】:

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

      using System.Security.Cryptography.X509Certificates;
      
      ...
      
      //certificate is a X509Certificate2
      using (var rsa = certificate.GetRSAPrivateKey())
      {
        //the var rsa is an RSA object
        //...
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-05-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-19
        • 2013-05-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多