JAVA 如何解密 base64 编码和 RIJNDAEL_256 由 PHP 加密的数据?
How can JAVA decrypt data which was base64 encoded and RIJNDAEL_256 encryped by PHP?
我需要将数据从 PHP 迁移到 JAVA 应用程序。
数据已使用此 PHP 函数加密:
function encrypt($text, $encryptionKey) {
return utf8_encode(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $encryptionKey, $text, MCRYPT_MODE_CBC, "[=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=]")));
}
...并且可以使用这个函数成功解密:
function decrypt($text, $encryptionKey) {
return utf8_decode(rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $encryptionKey, base64_decode($text), MCRYPT_MODE_CBC, "[=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=]")));
}
它存储在 MySql-InnoDB 中,collation=latin1_swedish_ci
在 table 中,charset=utf8
在 VARCHAR(255) 列中。
在 JAVA 中,数据通过使用 Hibernate 的 JPA 存储库检索,然后呈现给此
基于 Rijndael 256 加密和 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 ([=73=][=73=]...)。
我还没有找到任何方法来将模式和 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 亚马逊 Corretto)
问题是由于在发布的代码中使用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 代码加密的数据)。但是请注意,出于安全原因,key/IV 对只能使用一次。因此,如果密钥是固定的,最好使用每次加密时随机生成的 IV。
另外,代码:
decryptedBytes = Arrays.copyOfRange(decryptedBytes, 0, processed);
return new String(decryptedBytes, StandardCharsets.UTF_8);
可以简化为:
return new String(decryptedBytes, 0, processed, StandardCharsets.UTF_8);
我需要将数据从 PHP 迁移到 JAVA 应用程序。
数据已使用此 PHP 函数加密:
function encrypt($text, $encryptionKey) {
return utf8_encode(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $encryptionKey, $text, MCRYPT_MODE_CBC, "[=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=][=13=]")));
}
...并且可以使用这个函数成功解密:
function decrypt($text, $encryptionKey) {
return utf8_decode(rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $encryptionKey, base64_decode($text), MCRYPT_MODE_CBC, "[=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=][=14=]")));
}
它存储在 MySql-InnoDB 中,collation=latin1_swedish_ci
在 table 中,charset=utf8
在 VARCHAR(255) 列中。
在 JAVA 中,数据通过使用 Hibernate 的 JPA 存储库检索,然后呈现给此 基于 Rijndael 256 加密和 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 ([=73=][=73=]...)。
我还没有找到任何方法来将模式和 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 亚马逊 Corretto)
问题是由于在发布的代码中使用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 代码加密的数据)。但是请注意,出于安全原因,key/IV 对只能使用一次。因此,如果密钥是固定的,最好使用每次加密时随机生成的 IV。
另外,代码:
decryptedBytes = Arrays.copyOfRange(decryptedBytes, 0, processed);
return new String(decryptedBytes, StandardCharsets.UTF_8);
可以简化为:
return new String(decryptedBytes, 0, processed, StandardCharsets.UTF_8);