为了账号安全,请及时绑定邮箱和手机立即绑定

使用 MCRYPT_RIJNDAEL_256 密码的 mcrypt_encrypt PHP

使用 MCRYPT_RIJNDAEL_256 密码的 mcrypt_encrypt PHP

PHP
ABOUTYOU 2023-08-11 16:31:47
我有一个用于加密字符串(令牌)的 PHP 代码。我需要生成此令牌并调用旧后端 API。后端 API 会解密并验证它,然后才允许访问 API。我们正在用 Java 构建客户端应用程序。所以这段代码需要用Java来实现。$stringToEncode="****String which needs to be encrypted. This string length is multiple of 32********************";$key="32ByteKey-asdcxzasdsadasdasdasda";    echo base64_encode(        mcrypt_encrypt(                    MCRYPT_RIJNDAEL_256,                    $key,                    $stringToEncode,                    MCRYPT_MODE_CBC,                    $key                )            );这里 IV 与 key 相同。我尝试使用以下 Java 代码使用“org.bouncycastle.crypto”private static void encrypt(String key, String data) throws InvalidCipherTextException {                    byte[] givenKey = key.getBytes(Charset.forName("ASCII"));                final int keysize = 256;        byte[] keyData = new byte[keysize / Byte.SIZE];        System.arraycopy(givenKey, 0, keyData, 0, Math.min(givenKey.length, keyData.length));        KeyParameter keyParameter = new KeyParameter(keyData);        BlockCipher rijndael = new RijndaelEngine(256);        ZeroBytePadding c = new ZeroBytePadding();        PaddedBufferedBlockCipher pbbc = new PaddedBufferedBlockCipher(rijndael, c);        CipherParameters ivAndKey = new ParametersWithIV(keyParameter, key.getBytes(Charset.forName("ASCII")));        pbbc.init(true, ivAndKey);        byte[] plaintext = data.getBytes(Charset.forName("UTF8"));        byte[] ciphertext = new byte[pbbc.getOutputSize(plaintext.length)];        int offset = 0;        offset += pbbc.processBytes(plaintext, 0, plaintext.length, ciphertext, offset);        offset += pbbc.doFinal(ciphertext, offset);        System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(ciphertext));    }但低于例外 -Exception in thread "main" java.lang.IllegalArgumentException: invalid parameter passed to Rijndael init - org.bouncycastle.crypto.params.ParametersWithIV
查看完整描述

1 回答

?
动漫人物

TA贡献1815条经验 获得超10个赞

PHP 代码使用块大小为 256 位的 Rijndael。SunJCE Provider 不支持此功能,仅支持 AES(它是 Rindael 的子集,块大小为 128 位)。因此,第二个 Java 代码片段也不起作用。因此必须使用像 BouncyCastle (BC) 这样的第三方提供商。

Java/BC代码中有两个bug:

  • 与 PHP 代码相反,没有应用 CBC。必须使用 来更改此设置CBCBlockCipher

  • BC 的零填充变体与 mcrypt 变体不同。BC 变体总是填充,即即使明文的长度已经对应于块大小的整数倍(此处为 32 字节),mcrypt 变体也不会(即后者仅在明文长度不填充的情况下填充)对应于块大小的整数倍)。
    由于根据描述,明文已经对应于块大小的整数倍,因此 mcrypt 变体不会填充。在 Java/BC 代码中,如果根本不使用填充,就可以实现这一点。
    但要小心:如果明文的长度不对应于块大小的整数倍,那么当然必须使用填充(考虑到与 PHP 代码中使用的零填充变体的兼容性)。

这两个问题都可以通过更换线路来解决

ZeroBytePadding c = new ZeroBytePadding();
PaddedBufferedBlockCipher pbbc = new PaddedBufferedBlockCipher(rijndael, c);

经过

BufferedBlockCipher pbbc = new BufferedBlockCipher(new CBCBlockCipher(rijndael));

那么两个代码产生的密文是相同的。

一些附加说明:

  • 使用密钥作为 IV 通常没有用。由于出于安全原因,IV 对于给定密钥只能使用一次,因此每次加密都必须使用新密钥。
    通常在实践中,每次加密都会生成一个新的、随机的 IV。

  • 使用标准(即 AES)通常更安全,而不是使用块大小为 256 位的 Rijndael,后者不是标准。

  • 零填充并不可靠(此外,正如您所经历的,有几种变体可能会导致问题)。更可靠的是 PKCS7 填充。


查看完整回答
反对 回复 2023-08-11
  • 1 回答
  • 0 关注
  • 144 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信