CryptographicException «Ключ недействителен для использования в указанном состоянии.»при попытке экспорта параметров RSAP закрытого ключа X509 - PullRequest
52 голосов
/ 20 февраля 2012

Я довольно долго смотрю на это, и благодаря документации MSDN я не могу понять, что происходит.В основном я загружаю файл 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)**);

Ответы [ 6 ]

107 голосов
/ 21 февраля 2012

Я полагаю, что проблема может заключаться в том, что ключ не помечен как экспортируемый.Есть другой конструктор для 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);
12 голосов
/ 04 мая 2016

Для проблемы, с которой я столкнулся, изменение кода не было возможным, поскольку та же библиотека была установлена ​​и работает в другом месте.

Ответ Iridium заставил меня посмотреть, как сделать ключ экспортируемым, и я смог сделать это как частьМастер импорта сертификатов MMC.

Надеюсь, это поможет кому-то еще.Спасибо, куча

Cert import wizard

8 голосов
/ 18 апреля 2012

Я встретил похожую проблему, и X509KeyStorageFlags.Exportable решил мою проблему.

3 голосов
/ 20 февраля 2012

Я не совсем эксперт в этих вещах, но я сделал быстрый Google, и нашел это:

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

"если у вас есть более 245 байтов в вашем байтовом массиве, который вы передаете в свой метод RSACryptoServiceProvider.Encrypt (byte [] rgb, bool fOAEP), он вызовет исключение."

2 голосов
/ 11 ноября 2015

Для других, которые попадают сюда через Google, но не используют X509Certificate2, если вы вызываете ToXmlString для RSACryptoServiceProvider, но вы только загрузили открытый ключ, вы также получите это сообщение. Исправление таково (обратите внимание на последнюю строку):

var rsaAlg = new RSACryptoServiceProvider();

rsaAlg.ImportParameters(rsaParameters);

var xml = rsaAlg.ToXmlString(!rsaAlg.PublicOnly);
1 голос
/ 20 февраля 2012

AFAIK это должно сработать, и вы, вероятно, столкнетесь с ошибкой / некоторыми ограничениями. Вот несколько вопросов, которые могут помочь вам выяснить, в чем проблема.

  • Как вы создали файл PKCS # 12 (PFX)? Я видел некоторые ключи, которые CryptoAPI не нравятся (необычные параметры RSA). Можете ли вы использовать другой инструмент (просто чтобы быть уверенным)?

  • Можете ли вы экспортировать экземпляр PrivateKey в XML, например, ToXmlString(true), затем загрузить (импортировать) его обратно таким образом?

  • В старых версиях инфраструктуры возникали некоторые проблемы при импорте ключа, размер которого отличался от текущего экземпляра (по умолчанию 1024 бита). Каков размер вашего открытого ключа RSA в сертификате?

Также обратите внимание, что это не , как вы должны шифровать данные с использованием RSA. Размер необработанного шифрования ограничен используемым открытым ключом. Цикл за этим пределом даст вам действительно плохую производительность.

Хитрость заключается в том, чтобы использовать симметричный алгоритм (например, AES ) с полностью случайным ключом, а затем зашифровать этот ключ (оболочку) с помощью открытого ключа RSA. Вы можете найти код C # для этого в моей старой записи в блоге на эту тему.

...