使用rijndael - 128与CBC模式在Java 8



我正在Java 8中重写一些PHP代码,其中Rijndael-128密码(AES)用于加密和解密。使用的IV长度为16个字符(128位),而使用的密钥长度为18个字符(144位)。

我试图使用JCA和显式使用bouncycastle库在Java中编写加密方法,但对于两者,我都得到了密钥长度的错误。有可能在PHP中使用144位密钥吗?如果是,请建议如何在Java中使用相同的。请查看下面提到的使用代码和遇到的错误:

php

$cipherText = bin2hex($this->mcryptVal($this->pkcs5_pad($plainText), $key, $iv));
private function mcryptVal($text, $key, $iv, $encrypt = true) {
$td = mcrypt_module_open('rijndael-128', '', 'cbc', $iv);
mcrypt_generic_init($td, $key, $iv);
$result = ($encrypt) ? mcrypt_generic($td, $text) : mdecrypt_generic($td, $text);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $result;
}
private function pkcs5_pad($text) {
$blocksize = 16;
$pad = $blocksize - (strlen($text) % $blocksize);
return $text . str_repeat(chr($pad), $pad);
}

java

String plainText = "Hello World";
String key = ***; //144 bit (18 characters)
String iv = ***; //128 bit
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(new RijndaelEngine(128)), new ZeroBytePadding());
CipherParameters ivAndKey =
new ParametersWithIV(new KeyParameter(key.getBytes()), iv.getBytes());
cipher.init(true, ivAndKey);
byte[] out = new byte[cipher.getOutputSize(pkcs5Pad(plainText).getBytes().length)];
int l1 = cipher.processBytes(pkcs5Pad(plainText).getBytes(), 0,
pkcs5Pad(plainText).getBytes().length, out, 0);
int l2 = cipher.doFinal(out, l1);
byte[] encrypted = new byte[l1 + l2];
for (int i = 0; i < l1 + l2; i++) {
encrypted[i] = out[i];
}
String encryptedText = Hex.encodeHexString(encrypted);

public static String pkcs5Pad(String plainText) {
int blockSize = 16;
int pad = blockSize - (plainText.length() % blockSize);
return plainText.concat(StringUtils.repeat((char) pad, pad));
}

误差

Exception in thread "main" java.lang.IllegalArgumentException: Key length not 128/160/192/224/256 bits.

p: Java中提到的代码是使用bouncycastle,使用JCA的类似代码会导致密钥长度

的类似错误

在Java代码中有三个问题需要更改:

  • AES的有效密钥大小为16 (AES-128), 24 (AES-192)和32字节(AES-256),即18字节不是有效的密钥大小。PHP代码隐式地将18字节密钥扩展到下一个有效AES密钥大小,即24字节(即AES-192),填充0x00值。密钥扩展必须在Java代码中显式地完成。这是缺失的,这就是异常的原因。

  • PHP代码中的
  • pkcs5_pad()实现pkcs# 7填充(在Java世界中有时历史上称为pkcs# 5填充)。在Java端,不需要自定义实现,因为BouncyCastle支持pkcs# 7填充。注意,当使用自定义变体时,必须完全禁用填充。也不能使用零填充,因为BouncyCastle变体,不像mcrypt变体,总是填充(即即使最后一个块被完全填充),因此两个变体是不相等的。

  • 此外,应使用更具特异性的AESEngine代替RijndaelEngine。Rijndael的块大小为128位,密钥大小为16,24和32字节,每个定义对应于AES。最后一点不是bug,但出于效率的考虑应该实现。

因此,您的Java实现稍微更改为:

import java.nio.charset.StandardCharsets;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.util.encoders.Hex;
...
byte[] plainText = "The quick brown fox jumps over the lazy dog".getBytes(StandardCharsets.UTF_8);
byte[] keyInvalid = "012345678901234567".getBytes(StandardCharsets.UTF_8); // 18 bytes key, no valid AES key size
byte[] key = new byte[24]; // next valid AES key size after 18 is 24 bytes (valid AES key sizes: 16, 24, 32 bytes)
System.arraycopy(keyInvalid, 0, key, 0, keyInvalid.length);
byte[] iv = "0123456789012345".getBytes(StandardCharsets.UTF_8);
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()), new PKCS7Padding());
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv);
cipher.init(true, ivAndKey);
byte[] ciphertext = new byte[cipher.getOutputSize(plainText.length)];
int length = cipher.processBytes(plainText, 0, plainText.length, ciphertext, 0);
cipher.doFinal(ciphertext, length);
String ciphertextHex = Hex.toHexString(ciphertext);
System.out.println(ciphertextHex); // 4e4d648e1af730b6c7571cbd033a43c478f423a958f70bdf5929418e978d3126a7f7bbbeaea06e6cf12e99b922918917

请注意,这里执行的加密(AES/CBC/PKCS5Padding)也可以在Java 8下使用板上手段(即通过SunJCE提供程序),即BouncyCastle实际上是不必要的。

测试:

PHP代码返回相同的输入参数:
$key = '012345678901234567'; // 18 bytes key
$iv = "0123456789012345";    // 16 bytes IV
$plainText = 'The quick brown fox jumps over the lazy dog';
$cipherText = bin2hex(mcryptVal(pkcs5_pad($plainText), $key, $iv));
print($cipherText . PHP_EOL); // 4e4d648e1af730b6c7571cbd033a43c478f423a958f70bdf5929418e978d3126a7f7bbbeaea06e6cf12e99b922918917

相同的密文,表明两种实现在功能上是等效的

相关内容

  • 没有找到相关文章

最新更新