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になる可能性があります)を決定論的に伝える方法はありますか?
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
になります。
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);