JAVA如何解密用PHP加密的base64编码和RIJNDAEL 256的数据?
Posted
技术标签:
【中文标题】JAVA如何解密用PHP加密的base64编码和RIJNDAEL 256的数据?【英文标题】:How can JAVA decrypt data which was base64 encoded and RIJNDAEL_256 encryped by PHP? 【发布时间】:2020-08-19 06:31:26 【问题描述】:我需要将数据从 php 迁移到 JAVA 应用程序。
数据已使用此 PHP 函数加密:
function encrypt($text, $encryptionKey)
return utf8_encode(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $encryptionKey, $text, MCRYPT_MODE_CBC, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0")));
...使用此函数可以成功解密:
function decrypt($text, $encryptionKey)
return utf8_decode(rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $encryptionKey, base64_decode($text), MCRYPT_MODE_CBC, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0")));
它存储在 mysql-InnoDB 中,collation=latin1_swedish_ci
在一个表中,charset=utf8
在 VARCHAR(255) 列中。
在 JAVA 中,数据通过使用 Hibernate 的 JPA 存储库检索,然后呈现给此 基于Rijndael 256 encryption with Java & Bouncy Castle 的方法:
protected String decryptRijndael256_(String encryptedBase64Text)
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedBase64Text.getBytes(StandardCharsets.UTF_8));
byte[] key = encryptionKey.getBytes(StandardCharsets.UTF_8);
RijndaelEngine rijndaelEngine = new RijndaelEngine(256);
KeyParameter keyParam = new KeyParameter(key);
rijndaelEngine.init(false, keyParam);
PaddedBufferedBlockCipher bufferedBlock = new PaddedBufferedBlockCipher(rijndaelEngine, new ZeroBytePadding());
byte[] decryptedBytes = new byte[bufferedBlock.getOutputSize(encryptedBytes.length)];
int processed = bufferedBlock.processBytes(encryptedBytes, 0, encryptedBytes.length, decryptedBytes, 0);
processed += bufferedBlock.doFinal(decryptedBytes, processed);
decryptedBytes = Arrays.copyOfRange(decryptedBytes, 0, processed);
return new String(decryptedBytes, StandardCharsets.UTF_8);
虽然这在加密文本如下所示的大多数情况下都有效:
5VTv/x2f41Aj2iES7B9lRUi8Q9gH3MYnSR3xc4X1di4=
=> account.name@gmail.com
...如果文本很长并且看起来像这样,则会失败:
p77KGdWlexQXLGPZzkAqk2OK6oC9r7TDfMfaDhofu0et7RaPcA0hUCq0mBnY4oakjZpIrBeMadwhYonVKwJlGw==
=> very.long.account.name@gmail.c���ե.c��@*�c�ꀽ���|���G
所以它大部分被正确解码,但最后一个块似乎是问题所在。我怀疑问题出在字符编码或MCRYPT_MODE_CBC
及其IV (\0\0...) 上。
我还没有找到任何方法将模式和 IV 添加到 java 实现中。
关于 Base64 加密。我从java.util.Base64
、org.apache.commons.codec.binary.Base64
和org.bouncycastle.util.encoders.Base64
尝试了任何可能的方法。
结果要么如上,要么:
org.bouncycastle.crypto.DataLengthException: last block incomplete in decryption
at org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher.doFinal(Unknown Source)
我的猜测是问题与 PHP 如何进行填充有关。
问题:
PHP 和 JAVA 处理解密的方式有何不同?如何正确解码所有值?
(PHP:5.6.40,JAVA:11.0.7_10 Amazon Corretto)
【问题讨论】:
仅供参考:MCrypt 扩展已被弃用了 很长 时间,并在 PHP7 中被删除。此外,Rijndael 密码有时几乎是 AES,但实际上并非如此,如果您将数据迁移到实际的 AES,您的生活会更轻松。 @Sammitch 以及它的遗留代码,因此我们正在迁移 - 使用一种我有点过敏的语言 - 但是现在看起来我可能还需要使用一些 PHP 进行迁移,除非我得到答案在这里 【参考方案1】:该问题是由于在发布的代码中使用 ECB 模式而不是 CBC 模式引起的。应用 CBC 模式替换:
RijndaelEngine rijndaelEngine = new RijndaelEngine(256);
KeyParameter keyParam = new KeyParameter(key);
rijndaelEngine.init(false, keyParam);
PaddedBufferedBlockCipher bufferedBlock = new PaddedBufferedBlockCipher(rijndaelEngine, new ZeroBytePadding());
与:
byte[] iv = new byte[32]; // 0-IV, analogous to PHP code
PaddedBufferedBlockCipher bufferedBlock = new PaddedBufferedBlockCipher(new CBCBlockCipher(new RijndaelEngine(256)), new ZeroBytePadding());
CipherParameters keyAndIV = new ParametersWithIV(new KeyParameter(key), iv);
bufferedBlock.init(false, keyAndIV);
使用发布代码中的key
。 iv
是一个包含 IV 的 byte[]
,其大小对应于块大小(32 字节)。类似于 PHP 代码,必须使用 0-IV(解密使用 PHP 代码加密的数据)。但是请注意,出于安全原因,密钥/IV 对只能使用一次。因此,如果密钥是固定的,最好使用一个 IV,它是为每次加密随机生成的。
另外,代码:
decryptedBytes = Arrays.copyOfRange(decryptedBytes, 0, processed);
return new String(decryptedBytes, StandardCharsets.UTF_8);
可以简化为:
return new String(decryptedBytes, 0, processed, StandardCharsets.UTF_8);
【讨论】:
你是救命稻草,我一直在查看BlockCipherPadding
的实现,但不知何故错过了查看另一个接口 BlockCipher
及其实现
我似乎并不清楚这一点,我需要对数据进行解码,以便在新应用程序中以更好的方式对其进行加密,而不仅仅是按原样阅读,但你的笔记是非常感谢 - IV 有点像我理解的散列时应该使用的盐,所以应该以类似的方式处理
@Holly - 关于新实现:除了IV 应遵循的规则外,还应彻底重新考虑算法、模式和填充,例如AES-GCM 和Pkcs7-Padding 是更可靠的替代方案。
澄清:AES-GCM 和 Pkcs7-Padding 在某种程度上具有误导性,因为 GCM 不使用填充。我的真正意思是:对于需要填充的模式(例如像 CBC),Pkcs7 比零填充更可靠。以上是关于JAVA如何解密用PHP加密的base64编码和RIJNDAEL 256的数据?的主要内容,如果未能解决你的问题,请参考以下文章