web-dev-qa-db-ja.com

AESPKCS7パディング

AES暗号化/復号化のためにBouncyCastleを学び始めたところです。 256ビットキーでAES/CBC/PKCS7PADDINGを使用しています。

BCはテキストを正常に暗号化および復号化できますが、復号化後、常にnull(0x00)のパディングがいくつかあることに気付き、ハッシュ比較に失敗します。たとえば、元の入力文字列が“1234567890”であるとすると、復号化されたバイト配列は常に次のようになります。

{0x49,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x48,0x00,0x00,0x00,0x00,0x00,0x00}

パディングが0x06,0x06,0x06,0x06,0x06,0x06ではないのはなぜですか?また、暗号化前にまったく同じ文字列を取得できるように、暗号化後にパディングの長さ(0になる可能性があります)を決定論的に伝える方法はありますか?

6
user908645

PKCS7を指定すると、[〜#〜] bc [〜#〜]は暗号化する前にデータにパディングを追加し、復号化するときにデータを再度削除します。 PKCS7 with [〜#〜] aes [〜#〜]は常に少なくとも1バイトのパディングを追加し、入力をの倍数にするのに十分なデータを追加しますAESブロックサイズ。パディングを復号化する場合はverifiedであり、PKCS7の場合は、復号化されたデータの最後のブロックのどれだけがパディングされているか、および実際のデータがどれだけあるかを示す指標としても機能します。

復号化ステップでPKCS7を指定せずに暗号化およびパディングされたデータを復号化しようとすると、パディングは復号化されたデータに残ります。

編集:

私のポイントを説明するために..ここにいくつかのJava "1234567890"をAES/CBC/PKCS7で暗号化し、PKCS7の有無にかかわらず再度復号化するコードがありますパディング:

public class BCTest {
    public static void doTest() throws Exception {
        Security.addProvider(new BouncyCastleProvider());

        byte[] clearData = "1234567890".getBytes();
        SecretKey secretKey = new SecretKeySpec("0123456789ABCDEF".getBytes(), "AES");
        AlgorithmParameterSpec IVspec = new IvParameterSpec("0123456789ABCDEF".getBytes());

        // encrypt with PKCS7 padding
        Cipher encrypterWithPad = Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC");
        encrypterWithPad.init(Cipher.ENCRYPT_MODE, secretKey, IVspec);
        byte[] encryptedData = encrypterWithPad.doFinal(clearData);
        System.out.println("Encryped data (" + encryptedData.length + " bytes): \t" + toHexString(encryptedData));

        // decrypt with PKCS7 pad
        Cipher decrypterWithPad = Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC");
        decrypterWithPad.init(Cipher.DECRYPT_MODE, secretKey, IVspec);
        byte[] buffer1 = new byte[encryptedData.length]; 
        int decryptLen1 = decrypterWithPad.doFinal(encryptedData, 0, encryptedData.length, buffer1); 
        System.out.println("Decrypted with Pad (" + decryptLen1 + " bytes):  \t" + toHexString(buffer1));

        // decrypt without PKCS7 pad
        Cipher decrypterWithoutPad = Cipher.getInstance("AES/CBC/NOPADDING", "BC");
        decrypterWithoutPad.init(Cipher.DECRYPT_MODE, secretKey, IVspec);
        byte[] buffer2 = new byte[encryptedData.length]; 
        int decryptLen2 = decrypterWithoutPad.doFinal(encryptedData, 0, encryptedData.length, buffer2); 
        System.out.println("Decrypted without Pad (" + decryptLen2 + " bytes):\t" + toHexString(buffer2));
    }

    private static String toHexString(byte[] bytes) {
        return javax.xml.bind.DatatypeConverter.printHexBinary(bytes);
    }

    public static void main(String[] args) throws Exception {
        BCTest.doTest(); 
    }
}

出力:

Encryped data (16 bytes):           602CAE14358D0AC5C96E2D46D17E58E3
Decrypted with Pad (10 bytes):      31323334353637383930000000000000
Decrypted without Pad (16 bytes):   31323334353637383930060606060606

パディングオプションを使用して復号化すると、出力からパディングが削除され、暗号化により10バイトの復号化データが示されます。残りのバッファは0で埋められます。パディングオプションを使用せずに復号化すると、パディングが復号化されたデータの一部になります。

Edit2:

元のコードを見て、私の予感を確認します。 methode GetOutputSizeは、復号化された文字列の出力サイズを返しませんが、出力バッファに必要な最大スペースのみを返します。メソッドには、BCコードに次のドキュメントがあります。

/**
* return the size of the output buffer required for an update plus a
* doFinal with an input of len bytes.
*
* @param len the length of the input.
* @return the space required to accommodate a call to update and doFinal
* with len bytes of input.
*/

DoFinalは、バッファに入れられた復号化されたデータの実際の長さを返します。

だからで

byte[] plainTextBuffer = new byte[cipher.GetOutputSize(data.Length - IV_LENGTH)];
int length = cipher.DoFinal(data, iv.Length, data.Length - iv.Length, plainTextBuffer, 0);

plainTextBufferは、実際の復号化されたデータよりもわずかに大きくなります。実際のデータの長さはlengthになります。

6

bouncycastleのc#を使用しています。私には、これはbouncycastleのバグであるか、少なくともbouncycastle c#の実装がpkcs7仕様に正確に準拠していない可能性があります。

私の解決策は、DoFinalの戻り長に含まれていない末尾のバイトを切り落とすことです。 0x00のパディングがある理由はまだよくわかりませんが、前述のように、まったく存在しないはずです。

以下はコードです。暗号化と復号化の両方にAES/CBC/PKCS7PADDINGを使用しました。

暗号化--->

        ICipherParameters keyParams = ParameterUtilities.CreateKeyParameter("AES", keyByte);
        ICipherParameters aesIVKeyParam = new ParametersWithIV(keyParams, StringToByteArray(IV_STRING));
        byte[] iv = ((ParametersWithIV) aesIVKeyParam).GetIV();

        IBufferedCipher cipher = CipherUtilities.GetCipher("AES/CBC/PKCS7PADDING");
        cipher.Init(true, aesIVKeyParam);

        byte[] cipherText = new byte[iv.Length + cipher.GetOutputSize(data.Length)];
        Array.Copy(iv, 0, cipherText, 0, iv.Length);
        int length = cipher.DoFinal(data, 0, data.Length, cipherText, iv.Length);

復号化--->

        ICipherParameters keyParams = ParameterUtilities.CreateKeyParameter("AES", keyByte);
        byte[] iv = new byte[IV_LENGTH];
        Array.Copy(data, 0, iv, 0, IV_LENGTH);
        ICipherParameters aesIVKeyParam = new ParametersWithIV(keyParams, iv);

        IBufferedCipher cipher = CipherUtilities.GetCipher("AES/CBC/PKCS7PADDING");
        cipher.Init(false, aesIVKeyParam);

        byte[] plainTextBuffer = new byte[cipher.GetOutputSize(data.Length - IV_LENGTH)];
        int length = cipher.DoFinal(data, iv.Length, data.Length - iv.Length, plainTextBuffer, 0);
0
user908645