C# “加密例外”;密钥在指定状态下无效。”;尝试导出X509私钥的参数时

C# “加密例外”;密钥在指定状态下无效。”;尝试导出X509私钥的参数时,c#,.net,encryption,cryptography,rsa,C#,.net,Encryption,Cryptography,Rsa,我盯着这个看了很长一段时间,多亏了这个,我真的不知道发生了什么。基本上,我正在将一个PFX文件从光盘加载到X509Certificate2中,并尝试使用公钥加密字符串,使用私钥解密 为什么我感到困惑:当我将引用传递给rsacyptoserviceprovider本身时,加密/解密工作正常: byte[] ed1 = EncryptRSA("foo1", x.PublicKey.Key as RSACryptoServiceProvider); string foo1 = De

我盯着这个看了很长一段时间,多亏了这个,我真的不知道发生了什么。基本上,我正在将一个PFX文件从光盘加载到
X509Certificate2
中,并尝试使用公钥加密字符串,使用私钥解密

为什么我感到困惑:当我将引用传递给
rsacyptoserviceprovider
本身时,加密/解密工作正常:

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));
…它在尝试将私钥导出到
rsapameter
时引发“密钥在指定状态下无效”异常。请注意,生成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=decryptsa(ed,(x.PrivateKey作为rsacyptoserviceprovider)**.ExportParameters(true)**)

我不是这些方面的专家,但我在谷歌上快速搜索了一下,发现:


“如果传递给RSACryptServiceProvider.Encrypt(byte[]rgb,bool fOAEP)方法的字节数组中有超过245个字节,则该方法将引发异常。”

AFAIK这应该是可行的,并且您可能遇到错误/某些限制。下面的一些问题可能会帮助您找出问题所在

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

  • 能否将
    PrivateKey
    实例导出为XML,例如
    ToXmlString(true)
    ,然后以这种方式加载(导入)它

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

还请注意,这不是使用RSA加密数据的方式。原始加密的大小受所用公钥的限制。循环超过此限制只会给您带来非常糟糕的性能


诀窍是使用对称算法(如AES)和完全随机密钥,然后使用RSA公钥加密该密钥(wrap)。你可以在我的旧版本中找到C#代码来实现这一点。

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

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

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

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

对于其他通过Google到达这里但不使用任何X509Certificate2的用户,如果您在RSACryptServiceProvider上调用ToXmlString,但只加载了公钥,您也会收到此消息。解决方法如下(注意最后一行):


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

Iridium的回答让我看到了如何使密钥可导出,我能够将其作为MMC证书导入向导的一部分

希望这对其他人有帮助。谢谢你


这是一篇老文章,但也许可以帮助别人。
如果您使用的是自签名证书并使用其他用户登录,则必须从存储器中删除旧证书,然后重新创建它。我在opc ua软件方面也遇到了同样的问题

如果我没有很好地说明我自己,我可以打断这行:string foo=DecryptRSA(ed,(x.PrivateKey作为rsacyptoserviceprovider)。ExportParameters(true));into:var pvk=(x.PrivateKey作为RSACryptoServiceProvider);var pvkParam=pvk.ExportParameters(true);字符串foo=已解密RSA(已加密,pvkParam)。。。如果我这样做pvk.ExportParameters(true);将抛出。此错误是有史以来最烦人的错误之一。它可能意味着很多事情。对我来说,正如Jeroen所说:我的消息文本太大,无法用密钥加密(这是异步加密的缺点)。谢谢!你给我省了很多麻烦!:)8年后,这救了我的疯狂!发自内心的感谢。设置此可导出标志的负面影响/陷阱是什么?@ElFik-由于私钥用于解密和签名,因此确保其安全非常重要。设置Exportable标志后,私钥可以保存到文件中并复制到其他位置。如果您希望能够将密钥复制到另一台机器等,这一点很好,但确实会降低安全性,因为私钥也可以更容易地被未经授权的第三方提取(例如,如果您让电脑登录并解锁)。这并不是说在未设置Exportable标志的情况下,无法提取密钥,但这肯定会使提取变得更加困难。明白了,因此该指南可以概括为“仅在需要时使用Exportable,但如果需要,请随意使用”?在德语中,错误消息为“
Schlüssel的状态不正确。
”。
var rsaAlg = new RSACryptoServiceProvider();

rsaAlg.ImportParameters(rsaParameters);

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