web-dev-qa-db-ja.com

復号化例外-復号化するデータの長さが無効です

C#アプリケーションで作業しています。データをファイルに保存する一般的な方法があります。これらのメソッドは、データを暗号化してファイルシステムに保存します。データが必要になると、ReadDataメソッドがデータを復号化し、プレーンテキストを返します。

テキストのサイズが小さい場合、このコードは通常の場合は問題なく機能します。ただし、以下に示すテキストの例では、復号化コードが例外をスローしています-復号化するデータの長さが無効です。

例外は行で発生します

_        // close the CryptoStream
        x_cryptostream.Close();
_

別の方法を試しましたが、うまくいきませんでした。いくつかのplsは助けることができます。

すでに暗号化されたデータを暗号化する理由-巨大なアプリケーションの一般的な方法を使用してファイルに保存しようとしています。一般的なメソッドstoredata(key,data) nad readdata(key)は、回避できない暗号化/復号化を実行します。

_   public static byte[] Decrypt(byte[] ciphertext, string Key, string IV)
    {
        byte[] k = Encoding.Default.GetBytes(Key);
        byte[] iv = Encoding.Default.GetBytes(IV);

        // create the encryption algorithm
        SymmetricAlgorithm x_alg = SymmetricAlgorithm.Create("Rijndael");
        x_alg.Padding = PaddingMode.PKCS7;

        // create an ICryptoTransform that can be used to decrypt data
        ICryptoTransform x_decryptor = x_alg.CreateDecryptor(k, iv);

        // create the memory stream
        MemoryStream x_memory_stream = new MemoryStream();

        // create the CryptoStream that ties together the MemoryStream and the 
        // ICryptostream
        CryptoStream x_cryptostream = new CryptoStream(x_memory_stream,
        x_decryptor, CryptoStreamMode.Write);

        // write the ciphertext out to the cryptostream
        x_cryptostream.Write(ciphertext, 0, ciphertext.Length);

        // close the CryptoStream
        x_cryptostream.Close();

        // get the plaintext from the MemoryStream
        byte[] x_plaintext = x_memory_stream.ToArray();
_

以下は暗号化メソッドのコードです。

_        public static byte[] Encrypt(string strplain, string Key, string IV)
        {
        byte[] k = Encoding.Default.GetBytes(Key);
        byte[] iv = Encoding.Default.GetBytes(IV);

        byte[] plaintext = Encoding.Default.GetBytes(strplain);

        // create the encryption algorithm
        SymmetricAlgorithm x_alg = SymmetricAlgorithm.Create("Rijndael");
        x_alg.Padding = PaddingMode.PKCS7;
        // create an ICryptoTransform that can be used to encrypt data
        ICryptoTransform x_encryptor = x_alg.CreateEncryptor(k, iv);

        // create the memory stream
        MemoryStream x_memory_stream = new MemoryStream();

        // create the CryptoStream that ties together the MemoryStream and
        // the ICryptostream
        CryptoStream x_cryptostream = new CryptoStream(x_memory_stream,
        x_encryptor, CryptoStreamMode.Write);

        // write the plaintext out to the cryptostream
        x_cryptostream.Write(plaintext, 0, plaintext.Length);

        // close the CryptoStream
        x_cryptostream.Close();

        // get the ciphertext from the MemoryStream
        byte[] x_ciphertext = x_memory_stream.ToArray();

        // close memory stream
        x_memory_stream.Close();

        // convert from array to string
        string cipher_Tx = Encoding.Default.GetString(x_ciphertext, 
            0, x_ciphertext.Length);

        x_encryptor.Dispose();

        x_alg.Clear();
        byte[] cipher = Encoding.Default.GetBytes(cipher_Tx);

        return cipher;
    }  
_
11
Kevin Morocco

あなたの問題はstring cipher_Tx = Encoding.Default.GetString(x_ciphertext, 0, x_ciphertext.Length);です。

_x_ciphertext_はテキストの有効なバイト表現ではなく、表現できない多くの文字が含まれているため、_byte[]_からstringへの変換を実行すると、情報が失われます。これを行う正しい方法は、 Convert.ToBase64String(byte[]) および Convert.FromBase64String(string) のようなものを使用してバイナリデータを表すように設計された文字列形式を使用することです。 。

_string cipher_Tx = Convert.ToBase64String(x_ciphertext)

x_encryptor.Dispose();

x_alg.Clear();
byte[] cipher = Convert.FromBase64String(cipher_Tx)
_

そうは言っても、コードには他にも多くの「おかしなこと」があります。たとえば、usingステートメントを使用しないでください。また、文字列への変換やその逆変換はまったく必要ありません。単に_x_ciphertext_を返します。コードには他にも問題がある可能性があります(KeyIVの文字列がどこから来たのかなど)および他の多くのベストプラクティス(ランダムIVを生成して書き込む必要があるなど)出力に追加し、キーはユーザーのテキストから直接ではないキー派生関数を使用して生成する必要があります)が、文字列変換の問題が見つかったため、チェックを停止しました。

10

上記のコードは、復号化に使用されるキーとivが、暗号化に使用されるキーとivに一致する限り機能します。これを試して:

byte[] test = new byte[1000000];
for (int i = 0; i < 256; i++)
{
    test[i] = (byte)i;
}
var ciphertext = Encrypt(Encoding.Default.GetString(test), "0000000000000000", "0000000000000000");
byte[] check = Decrypt(ciphertext, "0000000000000000", "0000000000000000");
for (int i = 0; i < 256; i++)
{
    Debug.Assert(check[i] == (byte)i, "round trip");
}

ご覧のとおり、100万バイトはコードで問題なく暗号化および復号化されているため、データサイズとは関係ありません。

ただし、IVを次のように変更します。

byte[] check = Decrypt(ciphertext, "0000000000000000", "000000000000000X"); // note X

そしてDebug.Assertが起動します-復号化は一致しません。ただし、x_cryptostream.Close()は成功します。

次に、次のようにキーを変更してみてください。

byte[] check = Decrypt(ciphertext, "000000000000000X", "0000000000000000"); // note X

現在、x_cryptostream.Close()はCryptographicExceptionで失敗します。「パディングは無効であり、削除できません。」

キーが破損すると、復号化が失敗し、x_cryptostream.Close()が失敗します。

問題は、キーバイトを保存して後で復元することにあると思います。

ところで、うまくいけば、キーの完全なバイナリ範囲を使用していて、ASCII文字だけに基づいているわけではありません。そうでなければ、本当に強力なキーを持っていません。

0
Jim Flood