1

Padding is invalid and cannot be removed一部のデータを復号化しようとすると、実行時エラーが発生します。stackoverflow で見つけた同様のスレッドをすべて読み、次の問題を検討しました。

  1. 暗号化に使用されたものとは異なる復号化用のキーを指定すると、上記の問題が発生する可能性があります。ただし、ソースで確認できるように、テスト目的でキーをハードコーディングしましたが、それでも実行時エラーが発生します。

  2. アプリケーションは、暗号化に使用されるものとは異なるパディング モードを復号化に使用しようとしている可能性があります。暗号化または復号化プロセス中にモードを明示的に設定しても、問題は解決しませんでした。

  3. FlushFinalBlockメソッドを使用して、アプリケーションに最後のビットをblocksize. Write メソッドの下に配置しようとしましたが、どちらも機能しませんでした。

using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
    //Write all data to the stream.
    swEncrypt.Write(plainText);
    csEncrypt.FlushFinalBlock();
}

以下は私のコードのスニペットです:

// Encrypt 
private byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
{
    Key = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6 };
    IV = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6 };

    // Check arguments. 
    if (plainText == null || plainText.Length <= 0)
        throw new ArgumentNullException("plainText");
    if (Key == null || Key.Length <= 0)
        throw new ArgumentNullException("Key");
    if (IV == null || IV.Length <= 0)
        throw new ArgumentNullException("Key");

    byte[] encrypted;

    // Create an RijndaelManaged object 
    // with the specified key and IV. 
    using (RijndaelManaged rijAlg = new RijndaelManaged())
    {
        rijAlg.Key = Key;
        rijAlg.IV = IV;
        rijAlg.Padding = PaddingMode.PKCS7;

        // Create a decrytor to perform the stream transform.
        ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);

        // Create the streams used for encryption. 
        using (MemoryStream msEncrypt = new MemoryStream())
        {
            using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
            {
                using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                {
                    // Write all data to the stream.
                    swEncrypt.Write(plainText);
                    csEncrypt.FlushFinalBlock();
                }

                encrypted = msEncrypt.ToArray();
                MessageBox.Show("Encrypted str: " + Encoding.UTF8.GetString(encrypted).ToString());
            }
        }
    }

    // Update buffer length
    this.Buflen = encrypted.Length;
    MessageBox.Show("Byte count: " + (Encoding.UTF8.GetByteCount(encrypted.ToString())).ToString());
    MessageBox.Show("Decrypted: " + DecryptStringFromBytes(encrypted, this.Encryption_key, this.Encryption_iv));

    // Return the encrypted bytes from the memory stream. 
    return encrypted;
}

// Decrypt
private string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
{
    Key = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6 };
    IV = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6 };

    UTF8Encoding e = new UTF8Encoding();
    MessageBox.Show("ciperText " + cipherText.Length);
    //cipherText = e.GetBytes(cipherText);
    //MessageBox.Show("ciperText2 Length " + cipherText.Length);

    // Check arguments. 
    if (cipherText == null || cipherText.Length <= 0)
        throw new ArgumentNullException("cipherText");
    if (Key == null || Key.Length <= 0)
        throw new ArgumentNullException("Key");
    if (IV == null || IV.Length <= 0)
        throw new ArgumentNullException("Key");

    // Declare the string used to hold 
    // the decrypted text. 
    string plaintext = null;

    // Create an RijndaelManaged object 
    // with the specified key and IV. 
    using (RijndaelManaged rijAlg = new RijndaelManaged())
    {
        rijAlg.Key = Key;
        rijAlg.IV = IV;
        rijAlg.Padding = PaddingMode.PKCS7;

        // Create a decrytor to perform the stream transform.
        ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);

        // Create the streams used for decryption. 
        using (MemoryStream msDecrypt = new MemoryStream(cipherText))
        {
            using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
            {
                using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                {
                    // Read the decrypted bytes from the decrypting stream 
                    // and place them in a string.
                    if (cipherText.Length != rijAlg.BlockSize)
                    {
                        throw new InvalidOperationException("Invalid cipherText length");
                    }

                    plaintext = srDecrypt.ReadToEnd();

                    csDecrypt.FlushFinalBlock();
                }
            }
        }
    }

    return plaintext;
}

byte[] encoded_message = new byte[16];
encoded_message = EncryptStringToBytes("test", this.Encryption_key,this.Encryption_iv);

昨日の朝からこれを機能させようとしています。これまでに見つけたソリューションはどれも、私のアプリケーションでは機能しませんでした。コードが少しごちゃごちゃしているように見えるかもしれませんが、使用されていない部分は気にしないでください。これを機能させたら、確かに少しリファクタリングが必要になります。

ご覧のとおり、テスト目的でハードコードされたキーと IV を使用している間も実行時エラーが発生するため、暗号化に使用されたものとは異なる資格情報を復号化に渡すことは原因ではありません。

誰でもこれを解決する方法についてアイデアを思いつくことができますか? ありがとう。

4

1 に答える 1

0

過去 3 日間、暗号化されたデータをソケット経由で送信しようとしたことがある人のために、このスレッドを書いています。ご存じのように、暗号化されたデータにはパディング文字や特殊な記号が含まれる場合があり、パケットがソケットを介して送信される方法 (通常はバイナリ形式およびチャンク) と混ざり合って、期待どおりに送信されない可能性があります。通常、表示されるエラーは、パディングが無効であり、削除できない、または*復号化するデータの長さが無効です * です。. UDP ソケットを介して暗号化された Rijndael 資格情報を送信するアプリケーションを作成しているときに、これに遭遇しました。したがって、通常、あなたが送信したものはあなたが受け取るものではなく、パディング文字が追加され (またはそれに似たもの)、rijndael アルゴリズムによって認識できないバグの暗号化されたデータになります (これは、復号化を試みているときにも発生する可能性があることに注意してください)他のタイプの暗号化と私はそれが実際にあると思います)。したがって、暗号化されたデータを送信しようとしていて、これを正しく行おうとしている人を支援することを目的として、以下に私のソリューションを提供します (これが唯一であり、これが最も適切な方法であると言っているわけではありません)。これを行いますが、これは私が機能することがわかった方法であり、私が個人的に使用した方法です):

1) ほとんどの暗号化関数はパラメーターとしてバイト配列を取り、データを送信するためのほとんどのソケット関数もそうすることに注意して、最初に暗号化された文字列をバイト配列形式で取得します。

byte[] encrypted_string = new byte[lengthofblocksize]; // where lengthofblocksize would be the length in bytes of the blocksize for the used algorithm, for rijndael that usually is 16
//getting the encrypted byte array
encrypted_string = EncryptStringToBytes(messageInPlainText, key, iv); 

ここで、messageInPlainText はプレーン テキスト メッセージ、key と iv は key と iv パラメーターです。

2) データを送信する前に ソケットを介してデータを送信する場合、base64 文字列に暗号化します。文字列を変数「textdata」に格納するとします。最初のステップは string encrypted_string_base64 = Convert.ToBase64String(textdata); です。

3) これで、base64 文字列が文字列として取得されました。必要なのは、バイト配列で取得することです。これは、UTF8Encoding GetBytes メソッドを使用して行うことができます。4) そして、ソケットを介してデータを送信する準備ができたので、反対側で適切にエンコードされたバイナリ データを取得できます。sck.Send(encrypted_string);

5) 反対側では、通常はバイナリ データで文字列を取得します。それが変数 recvdata にあるとします。あなたがする必要があるのは、バイト配列を文字列に変換することです:

 string recv_textdata = Convert.FromBase64String(recvdata);

ただし、その前に、不要なパディングとシンボルをすべてクリアするメソッドを作成する必要がある場合があります。

 UTF8Encoding enc = new UTF8ENcoding();
 string recv_textdata = FormatStringAsBase64(enc.GetString(recvdata)).Trim();
(please scroll to the bottom in order to see the full definition of the above method)

6) これで暗号化されたデータが base64 文字列になりましたが、これを復号化メソッドに渡す準備ができていません。これは、base64 からデコードしてバイト配列に変換する必要があるためです。これは、 Convert.FromBase64String メソッド (少なくとも私のものはパラメーターとしてバイト配列を使用しています) - パラメーターとして文字列を受け取り、結果としてバイト配列を返します。

byte[] recv_textdata_encrypted_ready = new byte[lengthofblocksize]; //length of blocksize is 16 for rijndael as explained
recv_textdata_encrypted_ready = Convert.FromBase64String(recv_textdata);

7) これで、復号化のために暗号化されたバイト配列を渡す準備ができました。

それでは、FormatAsBase64String の定義と、それが必要な理由に戻りましょう。UDP ソケットを介して base64 でエンコードされた文字列を渡すと、データグラムがチャンクで送信され、不要なパディングが追加される可能性があります。これは通常、ソケットを介して送信するデータの md5 ハッシュと、ソケットを介して受信するデータを比較することによってのみ確認できます。ソケット、またはそれをバイナリファイルに書き込み(これが私が使用した方法です)、UTF8エンコーディング(またはポイント5で使用した他のエンコーディング)をサポートするテキストエディタを使用して結果を開きます)。したがって、以下の方法を使用しない場合、送信するデータは受信するデータと同じではない可能性があり、その結果、不要なパディングを含む文字列が生成され、.NET 復号化ライブラリを介した復号化の対象外となり、.NET 復号化ライブラリはそれを認識しないため、または私の場合、Convert で何が起こったのか。FromBase64String メソッドはそれを有効な base64 エンコード文字列として認識せず、文字列に無効なパディングが含まれており、有効な base64 エンコード形式ではないというエラーを吐き出します。したがって、文字列を有効な base64 のみとして検証して、通常はバイナリ暗号化されたバイト配列に変換するときに問題が発生しないようにするには、次のようなものが必要になる場合があります。

    private static string FormatStringAsBase64(string message_encrypted_base64)
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < message_encrypted_base64.Length; i++)
        {
            //current char
            char currchar = message_encrypted_base64[i];

            //check if we got a valid base64 symbol
            /* From Microsoft MSDN Official Convert.ToBase64String Method (http://msdn.microsoft.com/en-us/library/dhx0d524.aspx):
             * The base-64 digits in ascending order from zero are the uppercase 
             * characters "A" to "Z", the lowercase characters "a" to "z", the numerals "0" 
             * to "9", and the symbols "+" and "/". 
             * The valueless character, "=", 
             * is used for trailing padding.
             * 
             */
            if (char.IsLetterOrDigit(currchar) || currchar == '=' || currchar == '+' || currchar == '/')
            {
                sb.Append(currchar);
            }
        }
        return sb.ToString();
    }

UDP ソケットを介して暗号化された rijndael データを送信しようとしている人のために、これを書いています。私はこれを自分で理解するのに3日を失いました(まあ、これを理解するのにそれほど長い時間を費やしたバカのように見えるかもしれませんが、何が起こっているのかを理解しようとするすべての方法で混乱したコードがある場合)読んだ後スタックオーバーフローで同様のエラーが発生したスレッド全体。まったく同じ問題も解決策もありませんでした。したがって、UDP ソケットを介して暗号化されたデータを送信しようとして、無効なパディングやパディングを削除できない、または復号化するデータの長さが無効であるなどのエラーが発生した場合、このスレッドが役立つ可能性があります。

このスレッドは、説明されている問題に遭遇した他の多くの人に役立つ可能性があり、このフォーラムで多くのユーザーの時間を節約できると信じています. モッズがスレッドが不要または不適切であると信じていない、または見つけていない場合は、それを取り除くことができます。私が遭遇した問題とその解決方法を説明するために時間を割くことにしました。他の人は、明らかに非常に簡単に修正できる問題の解決策を見つけるまで、これが本当に私を怒らせていたと信じています.

于 2013-10-11T19:45:13.533 に答える