web-dev-qa-db-ja.com

CryptographicException "指定された状態での使用にはキーが無効です。" X509秘密鍵のRSAParametersをエクスポートしようとしているとき

私はかなり長い間これを見つめていましたが、 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)**);

52
kalrashi

問題は、キーがエクスポート可能としてマークされていないことだと考えています。 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);
110
Iridium

同じライブラリがインストールされ、別の場所で動作しているため、コードの変更はオプションではありませんでした。

Iridiumの答えは、キーをエクスポート可能にすることに私を導き、MMC Certificate Import Wizardの一部としてこれを行うことができました。

これが他の人の助けになることを願っています。ありがとうヒープ

Cert import wizard

13
will webster

同様の問題に遭遇し、X509KeyStorageFlags.Exportableは私の問題を解決しました。

8
user1187372

私はこれらのことの正確な専門家ではありませんが、簡単なグーグルをして、これを見つけました:

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

「RSACryptoServiceProvider.Encrypt(byte [] rgb、bool fOAEP)メソッドに渡すバイト配列に245バイト以上ある場合、例外がスローされます。」

3
Jeroen Jacobs

Googleを介してここにたどり着くが、X509Certificate2を使用しない他のユーザーについては、RSACryptoServiceProviderでToXmlStringを呼び出し、公開キーのみをロードした場合、このメッセージも表示されます。修正はこれです(最後の行に注意してください):

var rsaAlg = new RSACryptoServiceProvider();

rsaAlg.ImportParameters(rsaParameters);

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

私の知る限り、これは動作するはずであり、おそらくバグ/いくつかの制限にぶつかっています。問題の場所を特定するのに役立ついくつかの質問を次に示します。

  • PKCS#12(PFX)ファイルをどのように作成しましたか? CryptoAPIが気に入らないキーをいくつか見ました(珍しいRSAパラメーター)。 (念のため)別のツールを使用できますか?

  • PrivateKeyインスタンスをXMLにエクスポートできますか。 ToXmlString(true)、それからこの方法でロード(インポート)しますか?

  • 古いバージョンのフレームワークには、現在のインスタンスとは異なるサイズ(デフォルトでは1024ビット)のキーをインポートする際にいくつかの問題がありました。証明書内のRSA公開キーのサイズは?

また、これはRSAを使用してデータを暗号化する方法notであることに注意してください。生の暗号化のサイズは、使用されている公開キーによって制限されます。この制限をループすると、実際に悪いパフォーマンスしか得られません。

トリックは、対称アルゴリズム([〜#〜] aes [〜#〜]など)とtotallyを使用することですランダムキーを選択し、RSA公開キーを使用してこのキーを暗号化(ラップ)します。私の古い ブログエントリ でそのためのC#コードを見つけることができます。

1
poupou