java - openssl_encrypt 不带 vector 的 AES 加密

标签 java php aes

我需要一个 token 来访问某些源。源代码提供者给了我一个 java 示例,向我展示了如何创建它。以下是它如何加密文本:

private static final String ALGO = "AES";                   
public static String encrypt(byte[] keyValue, String token_text) throws Exception {                 
    Key key = new SecretKeySpec(keyValue, ALGO);
    Cipher c = Cipher.getInstance(ALGO);
    c.init(Cipher.ENCRYPT_MODE, key);

    byte[] bytes = c.doFinal(token_text.getBytes());
    byte[] buf = Base64.encodeBase64(bytes);

    return new String(buf); 
}

我想将这段java程序翻译成php。所以,这是我的代码:

public static function generate_token($token_text, $key) {
        $iv = self::hex_to_str("00000000000000000000000000000000");
        $token = openssl_encrypt($token_text, "AES-128-CBC", $key, 0, $iv);
        return $token;
    }

    private static function hex_to_str($hex)
    {
            $string='';
            for ($i=0; $i < strlen($hex)-1; $i+=2)
            {
                $string .= chr(hexdec($hex[$i].$hex[$i+1]));
            }
            return $string;
    }

但我无法得到相同的结果。我四处搜寻。它看起来像 cipher.init 没有 vector 调用。如果我对 openssl_encrypt 做同样的事情,它会给我一个错误:

openssl_encrypt(): Using an empty Initialization Vector (iv) is potentially insecure and not recommended

Some one说Cipher的默认 vector 是0。我尝试了一下,仍然无法得到相同的结果,但非常接近:

java: v8GhW0lu8DzNyqsfQTg4g7H6pwXCAAgy9vqFdz5OmXY=

php:v8GhW0lu8DzNyqsfQTg4g6If77f+8YVCcq8VcQGNe68=

我被困在这里一整天了。如果有人可以提供帮助,我将非常感激。

# java -version
openjdk version "1.8.0_101"
OpenJDK Runtime Environment (build 1.8.0_101-b13)
OpenJDK 64-Bit Server VM (build 25.101-b13, mixed mode)

# php -v
PHP 5.6.24 (cli) (built: Jul 21 2016 07:42:08) 
Copyright (c) 1997-2016 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies

最佳答案

假设 Java 端使用 SunJCE-Provider,默认模式是 ECB 模式,默认填充是 PKCS5Padding,即 Cipher.getInstance("AES")Cipher.getInstance("AES/ECB/PKCS5Padding") 参见例如https://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html#ciphertrans

ECB 模式不使用 IV。因此,您必须在 PHP 中替换generate_token-method

$iv = self::hex_to_str("00000000000000000000000000000000");
$token = openssl_encrypt($token_text, "AES-128-CBC", $key, 0, $iv);

$token = openssl_encrypt($token_text, "AES-128-ECB", $key, 0);

示例:

encrypt("This is the key!".getBytes(), "This is a plain text that needs to be encrypted...");

提供

fLSh/HoQkrsIVBtZJVnuIRqcz4ztUBDkDG9Pi3xe49Q9hh9zDzWZDRHEO70ixfLf2WbWYSeDOQ/ONFTWHW9i0Q==

这与 PHP 代码的结果相同(对于相同的 key 和纯文本)。

通常,如果您有多个区 block ,则 ECB 模式并不安全(从您的示例中我推断您的代币至少由两个区 block 组成)。那么,更好的选择是 CBC 或 GCM 模式,请参见https://crypto.stackexchange.com/questions/20941/why-shouldnt-i-use-ecb-encryptionhttps://security.stackexchange.com/questions/6500/aes-ecb-mode-for-single-block-random-data-encryption 。但由于 Java 加密方法似乎是您的引用,因此可能无法更改它。

编辑:

Java 密码类根据 key 大小自动确定是否必须使用 AES-128、AES-192 或 AES-256。因此,您还必须知道 Java 代码上下文中键的大小。如果 key 大小为 16 字节,则选择 AES-128-ECB 是正确的,否则您必须相应地调整 PHP 代码(例如,对于 24 字节或 32 字节 key 大小,选择 AES-192-ECB 或 AES-256-ECB,分别)。

关于java - openssl_encrypt 不带 vector 的 AES 加密,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53339751/

相关文章:

javascript - Laravel 5.5 - 从 JSON 对象数组为数据表创建列标题

php - 提取位标志的最有效方法

javax.crypto.BadPaddingException - 使用 Salt 和 IV 通过 AES256 进行解密

java - 使用 AES/ECB/NoPadding 与 PKCS5Padding 进行加密

java - 如何在基于代理的Spring bean中设置模拟对象?

java - 在 Java 中使用构建器模式将元素添加到 ArrayList

java - 初始化后向 fragment 发送数据

java - 退出数组 for 循环 Java

php - Laravel 5.5 API 资源收集使用数组

python - 用python编写AES-CTR解密例程