【问题标题】:Encryption from PHP to Java (Android): How to Fix Error "Wrong Final Block Length"?从 PHP 到 Java (Android) 的加密:如何修复错误“错误的最终块长度”?
【发布时间】: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


【解决方案1】:

感谢@Topaco,终于找到了可行的解决方案。正如他们所指出的,我不仅是双重编码(Base64),而且我应该使用 AES-128-CBC 而不是 AES-256-CBC。正如我发现的那样,当 IV 的长度无效时,php 中的 open_ssl 似乎不会引发错误,而是会根据需要填充或删除字符。 AES-128-CBC 正确使用 16 位 IV,一切都解决了。

对于那些将来可能会偶然发现这篇文章的人,最终的解决方案是更改 PHP 端如下:

$secret_key = "1111111111111111";
$secret_iv = "1111111111111111";

$encrypt_method = "AES-128-CBC"; 
$output = openssl_encrypt( $string, $encrypt_method, $secret_key, 0, $secret_iv );

【讨论】:

    猜你喜欢
    • 2014-05-31
    • 1970-01-01
    • 1970-01-01
    • 2015-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-13
    相关资源
    最近更新 更多