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

时间:2012-02-20 08:33:00

标签: c# .net encryption cryptography rsa

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

为什么我感到困惑:当我将引用传递给RSACryptoServiceProvider时,加密/解密有效:

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

但如果导出并绕过RSAParameter

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

...它会抛出一个"密钥无法在指定状态下使用。"尝试将私钥导出到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);
}

为了澄清上面的代码,大胆的部分是抛出: string foo = DecryptRSA(ed, (x.PrivateKey as RSACryptoServiceProvider)**.ExportParameters(true)**);

7 个答案:

答案 0 :(得分:107)

我认为问题可能是密钥未标记为可导出。 X509Certificate2的另一个构造函数采用X509KeyStorageFlags枚举。尝试替换该行:

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

有了这个:

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

答案 1 :(得分:12)

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

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

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

Cert import wizard

答案 2 :(得分:8)

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

答案 3 :(得分:3)

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

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

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

答案 4 :(得分:2)

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

var rsaAlg = new RSACryptoServiceProvider();

rsaAlg.ImportParameters(rsaParameters);

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

答案 5 :(得分:1)

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

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

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

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

另请注意,这是如何使用RSA加密数据。原始加密的大小受到使用的公钥的限制。超过此限制只会给你真正糟糕的表现。

诀窍是使用对称算法(如 AES )和完全随机密钥,然后使用RSA公钥加密此密钥(换行)。您可以在我的旧版blog entry中找到关于此主题的C#代码。

答案 6 :(得分:0)

旧帖子,但也许可以帮助某人。 如果您使用的是自签名证书,并且使用其他用户登录,则必须从存储中删除旧证书,然后重新创建它。我在opc ua软件上遇到过同样的问题