【问题标题】:CryptographicException "Key not valid for use in specified state." while trying to export RSAParameters of a X509 private keyCryptographicException “密钥在指定状态下无效。”在尝试导出 X509 私钥的 RSAParameters 时
【发布时间】:2012-03-10 14:47:46
【问题描述】:

我盯着这个看了很长一段时间,感谢MSDN documentation,我真的不知道发生了什么。基本上,我将 PFX 文件从光盘加载到 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)**);

【问题讨论】:

  • 在德语中,错误消息显示为“Schlüssel ist im angegebenen Status nicht gültig.”。

标签: c# .net encryption cryptography rsa


【解决方案1】:

我认为问题可能在于密钥未标记为可导出。 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);

【讨论】:

  • 谢谢!你帮我省了很多麻烦! :)
  • 8 年后,这拯救了我的精神错乱!从心底里感谢你。
  • 设置此可导出标志有哪些负面影响/陷阱?
  • @ElFik - 由于私钥用于解密和签名,因此确保其安全非常重要。设置 Exportable 标志后,可以将私钥保存到文件中并复制到其他地方,如果您希望能够这样做,这很好。将密钥复制到另一台机器等,它确实会降低安全性,因为它也可以更容易地被未经授权的第 3 方提取(例如,如果您让您的 PC 保持登录状态并解锁)。这并不是说设置了 Exportable 标志 not不可能 提取密钥,但它确实使提取密钥变得更加困难。
  • 知道了,所以指导可以概括为“仅在需要时使用exportable,但如果需要,请随意使用”?
【解决方案2】:

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

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

希望这对其他人有所帮助。多谢了

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

      • 对不起,如果我没有很好地澄清自己,我可以打破这一行: string foo = DecryptRSA(ed, (x.PrivateKey as RSACryptoServiceProvider).ExportParameters(true));进入: var pvk = (x.PrivateKey as RSACryptoServiceProvider); var pvkParam = pvk.ExportParameters(true);字符串 foo = DecryptRSA(ed, pvkParam); ...如果我这样做 pvk.ExportParameters(true);会抛出。
      • 这个错误是有史以来最烦人的错误之一。它可能意味着很多事情。对我来说,正如 Jeroen 所说:我的消息文本太大而无法通过密钥加密(这是异步加密的缺点)。
      【解决方案5】:

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

      var rsaAlg = new RSACryptoServiceProvider();
      
      rsaAlg.ImportParameters(rsaParameters);
      
      var xml = rsaAlg.ToXmlString(!rsaAlg.PublicOnly);
      

      【讨论】:

        【解决方案6】:

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

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

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

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

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

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

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-10-09
            • 2012-01-04
            • 1970-01-01
            • 2017-10-03
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多