【发布时间】:2019-11-08 22:16:49
【问题描述】:
尝试了几种在 Google 和 SOF 上找到的不同方法,但我似乎无法让它发挥作用。我正在尝试在 PHP 中加密一个字符串并在 Java(Android 活动)中解密它。在 PHP 中加密字符串,我使用 AES-256-CBC 和 sha256 哈希(在 PHP 中成功加密/解密)。问题是,无论我尝试什么,我都无法在 Java 中获得未加密的字符串。我最近的尝试以“密码函数:OPENSSL_internal:WRONG_FINAL_BLOCK_Length”的控制台错误结束。
我怀疑我的问题与 Base64 编码有关,但到目前为止我的所有尝试都失败了。或者我在 Java 端使用了错误的密码。我读过一些人需要转换为十六进制,但我不明白为什么。
请注意,iv 和 key 并不完全一样,只是为了举例,这里使用相同。
我的 PHP 代码:
$secret_key = "1111111111111111";
$secret_iv = "1111111111111111";
$encrypt_method = "AES-256-CBC";
$key = hash( 'sha256', $secret_key );
$iv = substr( hash( 'sha256', $secret_iv ), 0, 16 );
$output = base64_encode( openssl_encrypt( $string, $encrypt_method, $key, 0, $iv ) );
我的 Java 代码:
String given_iv = "1111111111111111";
String key = "1111111111111111";
IvParameterSpec iv = new IvParameterSpec(given_iv.getBytes());
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] decodedEncryptedData = Base64.decode(data.getBytes(),Base64.NO_WRAP);
byte[] original = cipher.doFinal(decodedEncryptedData);
Log.i(TAG, "Result: " + new String(original));
return new String(original);
谁能告诉我哪里出错了。谢谢。
更新 1: 正如@Peter 和@Topaco 所指出的,我在 PHP 端进行了两次 Base64 编码,这已被删除,并直接在 Java 端使用密钥/IV。不幸的是,错误仍然存在。
现在的 PHP 代码:
$secret_key = "1111111111111111";
$secret_iv = "1111111111111111";
$encrypt_method = "AES-256-CBC";
$output = openssl_encrypt( $string, $encrypt_method, $secret_key, 0, $secret_iv );
Java 代码仍然保持不变。但是我在搞乱 Cipher.getInstance("AES/CBC/PKCS5PADDING") 并将其更改为“AES/CBC/NOPADDING”,它改变了错误,我现在得到一个乱码文本的输出,但仍然没有真正的运气完全解密。
【问题讨论】:
-
openssl_encrypt 默认返回 base64 编码的密文,因此您是双重编码。删除 base64_encode 调用或设置 OPENSSL_RAW_DATA 选项。
-
与问题无关:不要重复使用 IV!使用random_bytes function 为每个加密生成一个新的。并且如果$secret_key的熵低(如密码),则使用PBKDF2、Argon2、bcrypt、scrypt等密钥推导函数来获取加密密钥。 sha256 不适合。
-
@Peter 正如建议的那样,我从 php 文件中删除了 base64_encode。现在java端给了我一个不同的错误“BadPaddingException...BAD_DECRYPT”。无论哪种方式,仍然没有成功解密。
-
除了双重 Base64 编码问题:在 PHP 代码中,您从
$secret_key/$secret_iv创建哈希值并将它们用作键 / IV。在 Java 代码中,您直接 使用值作为键/IV(即您不 使用散列值)。另请注意,在 PHP 代码中,hash方法的第三个参数对于二进制结果应该是TRUE(否则您会得到十六进制字符串的结果,我认为这不是有意的)。 -
如果您使用的是 16 字节密钥,当然您还必须使用 AES-128。在 Java 中,这是隐式发生的,因为密钥长度决定了变体,即 16 字节密钥隐式表示 AES-128。在 PHP 中,变量是显式指定的(密钥总是通过截断或通过填充 0 值扩展来隐式调整),当前变量是 AES-256:所以在 PHP 代码中,只需将
$encrypt_method从 AES- 256-CBC 到 AES-128-CBC。
标签: java android encryption