尝试导出X509私钥的RSAParameters时CryptographicException“密钥无法在指定状态下使用。”

时间:2021-05-22 18:25:21

I am staring at this for quite a while and thanks to the MSDN documentation I cannot really figure out what's going. Basically I am loading a PFX file from the disc into a X509Certificate2 and trying to encrypt a string using the public key and decrypt using the private key.

我盯着这看了很长一段时间,感谢MSDN文档,我无法弄清楚会发生什么。基本上我正在将光盘中的PF​​X文件加载到X509Certificate2中,并尝试使用公钥加密字符串并使用私钥解密。

Why am I puzzled: the encryption/decryption works when I pass the reference to the RSACryptoServiceProvider itself:

为什么我感到困惑:加密/解密在我将引用传递给RSACryptoServiceProvider本身时起作用:

byte[] ed1 = EncryptRSA("foo1", x.PublicKey.Key as RSACryptoServiceProvider);
string foo1 = DecryptRSA(ed1, x.PrivateKey as RSACryptoServiceProvider);

But if the export and pass around the RSAParameter:

但是如果导出并传递RSAParameter:

byte[] ed = EncryptRSA("foo", (x.PublicKey.Key as RSACryptoServiceProvider).ExportParameters(false));
string foo = DecryptRSA(ed, (x.PrivateKey as RSACryptoServiceProvider).ExportParameters(true));

...it throws a "Key not valid for use in specified state." exception while trying to export the private key to RSAParameter. Please note that the cert the PFX is generated from is marked exportable (i.e. I used the pe flag while creating the cert). Any idea what is causing the exception?

...它会抛出“密钥无法在指定状态下使用”。尝试将私钥导出到RSAParameter时出现异常。请注意,生成PFX的证书标记为可导出(即我在创建证书时使用了pe标志)。知道导致异常的是什么吗?

 static void Main(string[] args)
    {
        X509Certificate2 x = new X509Certificate2(@"C:\temp\certs\1\test.pfx", "test");
        x.FriendlyName = "My test Cert";

        X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
        store.Open(OpenFlags.ReadWrite);
        try
        {
            store.Add(x);
        }
        finally
        {
            store.Close();
        }

        byte[] ed1 = EncryptRSA("foo1", x.PublicKey.Key as RSACryptoServiceProvider);
        string foo1 = DecryptRSA(ed1, x.PrivateKey as RSACryptoServiceProvider);

        byte[] ed = EncryptRSA("foo", (x.PublicKey.Key as RSACryptoServiceProvider).ExportParameters(false));
        string foo = DecryptRSA(ed, (x.PrivateKey as RSACryptoServiceProvider).ExportParameters(true));
    }

private static byte[] EncryptRSA(string data, RSAParameters rsaParameters)
{
    UnicodeEncoding bytConvertor = new UnicodeEncoding();
    byte[] plainData = bytConvertor.GetBytes(data);

    RSACryptoServiceProvider publicKey = new RSACryptoServiceProvider();
    publicKey.ImportParameters(rsaParameters);
    return publicKey.Encrypt(plainData, true);
}

private static string DecryptRSA(byte[] data, RSAParameters rsaParameters)
{
    UnicodeEncoding bytConvertor = new UnicodeEncoding();

    RSACryptoServiceProvider privateKey = new RSACryptoServiceProvider();
    privateKey.ImportParameters(rsaParameters);

    byte[] deData = privateKey.Decrypt(data, true);
    return bytConvertor.GetString(deData);
}

private static byte[] EncryptRSA(string data, RSACryptoServiceProvider publicKey)
{
    UnicodeEncoding bytConvertor = new UnicodeEncoding();
    byte[] plainData = bytConvertor.GetBytes(data);

    return publicKey.Encrypt(plainData, true);
}

private static string DecryptRSA(byte[] data, RSACryptoServiceProvider privateKey)
{
    UnicodeEncoding bytConvertor = new UnicodeEncoding();

    byte[] deData = privateKey.Decrypt(data, true);
    return bytConvertor.GetString(deData);
}

Just to clarify in the code above the bold part is throwing: string foo = DecryptRSA(ed, (x.PrivateKey as RSACryptoServiceProvider)**.ExportParameters(true)**);

只是为了澄清上面的代码,粗体部分是throw:string foo = DecryptRSA(ed,(x.PrivateKey as RSACryptoServiceProvider)**。ExportParameters(true)**);

6 个解决方案

#1


88  

I believe that the issue may be that the key is not marked as exportable. There is another constructor for X509Certificate2 that takes an X509KeyStorageFlags enum. Try replacing the line:

我认为问题可能是密钥未标记为可导出。 X509Certificate2还有另一个构造函数,它接受X509KeyStorageFlags枚举。尝试更换该行:

X509Certificate2 x = new X509Certificate2(@"C:\temp\certs\1\test.pfx", "test");

With this:

有了这个:

X509Certificate2 x = new X509Certificate2(@"C:\temp\certs\1\test.pfx", "test", X509KeyStorageFlags.Exportable);

#2


8  

I've met some similar issue, and X509KeyStorageFlags.Exportable solved my problem.

我遇到了一些类似的问题,X509KeyStorageFlags.Exportable解决了我的问题。

#3


6  

For the issue I encountered a code change was not an option as the same library was installed and working elsewhere.

对于我遇到的问题,代码更改不是一个选项,因为安装了相同的库并在其他地方工作。

Iridium's answer lead me to look making the key exportable and I was able to this as part of the MMC Certificate Import Wizard.

铱星的回答让我看起来使密钥可导出,我能够将其作为MMC证书导入向导的一部分。

Hope this helps someone else. Thanks heaps

希望这有助于其他人。谢谢堆

尝试导出X509私钥的RSAParameters时CryptographicException“密钥无法在指定状态下使用。”

#4


3  

I'm not exactly an expert in these things, but I did a quick google, and found this:

我不是这些东西的专家,但我做了一个快速谷歌,发现这个:

http://social.msdn.microsoft.com/Forums/en/clr/thread/4e3ada0a-bcaf-4c67-bdef-a6b15f5bfdce

http://social.msdn.microsoft.com/Forums/en/clr/thread/4e3ada0a-bcaf-4c67-bdef-a6b15f5bfdce

"if you have more than 245 bytes in your byte array that you pass to your RSACryptoServiceProvider.Encrypt(byte[] rgb, bool fOAEP) method then it will throw an exception."

“如果您的字节数组中有超过245个字节传递给RSACryptoServiceProvider.Encrypt(byte [] rgb,bool fOAEP)方法,那么它将抛出异常。”

#5


1  

AFAIK this should work and you're likely hitting a bug/some limitations. Here's some questions that may help you figure out where's the issue.

AFAIK这应该工作,你可能会遇到一个bug /一些限制。这里有一些问题可以帮助您找出问题所在。

  • How did you create the PKCS#12 (PFX) file ? I've seen some keys that CryptoAPI does not like (uncommon RSA parameters). Can you use another tool (just to be sure) ?

    你是如何创建PKCS#12(PFX)文件的?我见过CryptoAPI不喜欢的一些密钥(不常见的RSA参数)。你能用另一种工具(只是为了确定)吗?

  • Can you export the PrivateKey instance to XML, e.g. ToXmlString(true), then load (import) it back this way ?

    您可以将PrivateKey实例导出到XML,例如ToXmlString(true),然后以这种方式加载(导入)它?

  • Old versions of the framework had some issues when importing a key that was a different size than the current instance (default to 1024 bits). What's the size of your RSA public key in your certificate ?

    导入与当前实例大小不同的密钥时,旧版本的框架存在一些问题(默认为1024位)。证书中RSA公钥的大小是多少?

Also note that this is not how you should encrypt data using RSA. The size of the raw encryption is limited wrt the public key being used. Looping over this limit would only give you really bad performance.

另请注意,这不是您应该如何使用RSA加密数据。原始加密的大小受到使用的公钥的限制。超过此限制只会给你带来非常糟糕的表现。

The trick is to use a symmetric algorithm (like AES) with a totally random key and then encrypt this key (wrap) using the RSA public key. You can find C# code to do so in my old blog entry on the subject.

诀窍是使用带有完全随机密钥的对称算法(如AES),然后使用RSA公钥加密此密钥(换行)。你可以在我关于这个主题的旧博客文章中找到C#代码。

#6


1  

For others that end up here through Google, but don't use any X509Certificate2, if you call ToXmlString on RSACryptoServiceProvider but you've only loaded a public key, you will get this message as well. The fix is this (note the last line):

对于通过Google结束的其他人,但不使用任何X509Certificate2,如果您在RSACryptoServiceProvider上调用ToXmlString但是您只加载了一个公钥,那么您也会收到此消息。修复就是这个(注意最后一行):

var rsaAlg = new RSACryptoServiceProvider();

rsaAlg.ImportParameters(rsaParameters);

var xml = rsaAlg.ToXmlString(!rsaAlg.PublicOnly);

#1


88  

I believe that the issue may be that the key is not marked as exportable. There is another constructor for X509Certificate2 that takes an X509KeyStorageFlags enum. Try replacing the line:

我认为问题可能是密钥未标记为可导出。 X509Certificate2还有另一个构造函数,它接受X509KeyStorageFlags枚举。尝试更换该行:

X509Certificate2 x = new X509Certificate2(@"C:\temp\certs\1\test.pfx", "test");

With this:

有了这个:

X509Certificate2 x = new X509Certificate2(@"C:\temp\certs\1\test.pfx", "test", X509KeyStorageFlags.Exportable);

#2


8  

I've met some similar issue, and X509KeyStorageFlags.Exportable solved my problem.

我遇到了一些类似的问题,X509KeyStorageFlags.Exportable解决了我的问题。

#3


6  

For the issue I encountered a code change was not an option as the same library was installed and working elsewhere.

对于我遇到的问题,代码更改不是一个选项,因为安装了相同的库并在其他地方工作。

Iridium's answer lead me to look making the key exportable and I was able to this as part of the MMC Certificate Import Wizard.

铱星的回答让我看起来使密钥可导出,我能够将其作为MMC证书导入向导的一部分。

Hope this helps someone else. Thanks heaps

希望这有助于其他人。谢谢堆

尝试导出X509私钥的RSAParameters时CryptographicException“密钥无法在指定状态下使用。”

#4


3  

I'm not exactly an expert in these things, but I did a quick google, and found this:

我不是这些东西的专家,但我做了一个快速谷歌,发现这个:

http://social.msdn.microsoft.com/Forums/en/clr/thread/4e3ada0a-bcaf-4c67-bdef-a6b15f5bfdce

http://social.msdn.microsoft.com/Forums/en/clr/thread/4e3ada0a-bcaf-4c67-bdef-a6b15f5bfdce

"if you have more than 245 bytes in your byte array that you pass to your RSACryptoServiceProvider.Encrypt(byte[] rgb, bool fOAEP) method then it will throw an exception."

“如果您的字节数组中有超过245个字节传递给RSACryptoServiceProvider.Encrypt(byte [] rgb,bool fOAEP)方法,那么它将抛出异常。”

#5


1  

AFAIK this should work and you're likely hitting a bug/some limitations. Here's some questions that may help you figure out where's the issue.

AFAIK这应该工作,你可能会遇到一个bug /一些限制。这里有一些问题可以帮助您找出问题所在。

  • How did you create the PKCS#12 (PFX) file ? I've seen some keys that CryptoAPI does not like (uncommon RSA parameters). Can you use another tool (just to be sure) ?

    你是如何创建PKCS#12(PFX)文件的?我见过CryptoAPI不喜欢的一些密钥(不常见的RSA参数)。你能用另一种工具(只是为了确定)吗?

  • Can you export the PrivateKey instance to XML, e.g. ToXmlString(true), then load (import) it back this way ?

    您可以将PrivateKey实例导出到XML,例如ToXmlString(true),然后以这种方式加载(导入)它?

  • Old versions of the framework had some issues when importing a key that was a different size than the current instance (default to 1024 bits). What's the size of your RSA public key in your certificate ?

    导入与当前实例大小不同的密钥时,旧版本的框架存在一些问题(默认为1024位)。证书中RSA公钥的大小是多少?

Also note that this is not how you should encrypt data using RSA. The size of the raw encryption is limited wrt the public key being used. Looping over this limit would only give you really bad performance.

另请注意,这不是您应该如何使用RSA加密数据。原始加密的大小受到使用的公钥的限制。超过此限制只会给你带来非常糟糕的表现。

The trick is to use a symmetric algorithm (like AES) with a totally random key and then encrypt this key (wrap) using the RSA public key. You can find C# code to do so in my old blog entry on the subject.

诀窍是使用带有完全随机密钥的对称算法(如AES),然后使用RSA公钥加密此密钥(换行)。你可以在我关于这个主题的旧博客文章中找到C#代码。

#6


1  

For others that end up here through Google, but don't use any X509Certificate2, if you call ToXmlString on RSACryptoServiceProvider but you've only loaded a public key, you will get this message as well. The fix is this (note the last line):

对于通过Google结束的其他人,但不使用任何X509Certificate2,如果您在RSACryptoServiceProvider上调用ToXmlString但是您只加载了一个公钥,那么您也会收到此消息。修复就是这个(注意最后一行):

var rsaAlg = new RSACryptoServiceProvider();

rsaAlg.ImportParameters(rsaParameters);

var xml = rsaAlg.ToXmlString(!rsaAlg.PublicOnly);