首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在Java 8中使用CBC模式的Rijndael-128

在Java 8中使用CBC模式的Rijndael-128
EN

Stack Overflow用户
提问于 2022-07-15 08:46:08
回答 1查看 167关注 0票数 1

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

我尝试使用JCA和显式使用赏金城堡库用Java编写加密方法,但对于这两种方法,我都得到了密钥长度的错误。在PHP中可以使用144位键吗?如果是,请建议如何在Java中使用相同的方法。请查找下面提到的所使用的代码和遇到的错误:

php

代码语言:javascript
运行
复制
$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

代码语言:javascript
运行
复制
    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));
  }

误差

代码语言:javascript
运行
复制
Exception in thread "main" java.lang.IllegalArgumentException: Key length not 128/160/192/224/256 bits.

PS:前面提到的用于Java的代码使用赏金城堡,使用JCA的类似代码导致类似的密钥长度错误。

EN

回答 1

Stack Overflow用户

发布于 2022-07-15 13:17:41

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

  • 有效密钥大小分别为16 ( AES -128)、24 (AES-192)和32字节(AES-256),即18个字节不是有效的密钥大小。PHP代码通过填充0x00值,隐式地将18个字节密钥扩展到下一个有效的AES密钥大小,即24个字节(即AES-192)。键扩展必须在Java代码中显式执行。这是缺少的,也是出现异常的原因。

  • pkcs5_pad()代码中的PKCS#7实现了PKCS#7填充(在Java中有时被称为PKCS#5填充)。在Java方面,不需要自定义实现,因为BouncyCastle支持PKCS#7填充。注意,当使用自定义变量时,必须完全禁用填充。此外,不能使用零填充,因为与mcrypt变体不同的是,BouncyCastle变量总是pad(即即使最后一个块已完全填充),因此这两个变体都不是等价的。

此外,应该使用更具体的

  • 而不是RijndaelEngine。密钥大小为16、24和32字节的块大小为128位的Rijndael与AES对应。最后一点不是错误,而是出于效率原因而实现的。

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

代码语言:javascript
运行
复制
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/PKCS5Padd)在Java 8下也是可能的,使用车载方式(即通过SunJCE提供程序),即实际上不需要BouncyCastle。

测试:

PHP代码使用相同的输入参数返回:

代码语言:javascript
运行
复制
$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

相同的密文显示这两个实现在功能上是等价的。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72991396

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档