繁体   English   中英

我如何在 PHP 和 JAVA 之间加密和解密使用 AES/GCM/

[英]how do i encrypt and decrypt use AES/GCM/ between PHP and JAVA

我用PHP加密用AES/GCM和JAVA通信,但是不行。这是代码。不知道哪里错了?

<?php

$key = "123456789012345678901234567890";
$plaintext = "aaaaaaa";
$encryptStr = aesGcmEncrypt($plaintext, $key);
echo "加密后:" . $encryptStr;


function aesGcmEncrypt($plaintext, $key)
{

    $ivlen = openssl_cipher_iv_length($cipher = "aes-128-gcm");
    $iv = openssl_random_pseudo_bytes($ivlen);
    $ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, OPENSSL_NO_PADDING, $iv, $tag);
    $ciphertext = base64_encode($iv . $ciphertext_raw . $tag);
    return $ciphertext;
}

function decrypt($str, $key)
{
    $encrypt = base64_decode($str);
    $ivlen = openssl_cipher_iv_length($cipher = "aes-128-gcm");
    $tag_length = 16;
    $iv = substr($encrypt, 0, $ivlen);
    $tag = substr($encrypt, -$tag_length);
    $ciphertext = substr($encrypt, $ivlen, -$tag_length);

    $ciphertext_raw = openssl_decrypt($ciphertext, $cipher, $key, OPENSSL_NO_PADDING, $iv, $tag);
    return $ciphertext_raw;
}

这是 java 代码。

private static String aesGcmEncrypt(String content, byte[] key) {
        try {
            System.out.println(content);
            System.out.println(content.getBytes(UTF_8).length);
            // 根据指定算法ALGORITHM自成密码器
            Cipher cipher = Cipher.getInstance("AES/GCM/PKCS5Padding");
            SecretKeySpec skey = new SecretKeySpec(key, "AES");
            cipher.init(Cipher.ENCRYPT_MODE, skey);
            //获取向量
            byte[] ivb = cipher.getIV();
            byte[] encodedByteArray = cipher.doFinal(content.getBytes(UTF_8));

            byte[] message = new byte[ivb.length + encodedByteArray.length];

            System.arraycopy(ivb, 0, message, 0, ivb.length);
            System.arraycopy(encodedByteArray, 0, message, ivb.length, encodedByteArray.length);
            String ss = Base64.getEncoder().encodeToString(message);
            return ss;
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException
                | BadPaddingException e) {
            return null;
        }
    }

JAVA代码不能修改,因为不是我的,必须适配java代码。

两种代码的行为可能与您预期的不同。

在 Java 代码中根本没有使用填充,尽管指定了PKCS5Padding (在 Java 中,PKCS7Padding 的同义词)。 SunJCE Provider 为 GCM 禁用指定的PKCS5Padding并应用NoPadding 这很有用,因为GCM是一种不需要填充的 stream 密码模式。
应该提到的是,该行为取决于版本。 只有早期的 JDK 版本(例如 8、11、12)接受 GCM 的PKCS5Padding并将其作为NoPadding运行。 相比之下,更高的 JDK 版本(例如 14、15)会引发异常。 此外,其他提供者的行为可能有所不同。

在 PHP 代码中,密文由openssl_encrypt作为原始数据返回,因此只有 Base64 编码一次(即在与 IV 和标签连接之后),因为它应该是。 因此,代码的行为就像设置了标志OPENSSL_RAW_DATA 这是因为使用了OPENSSL_NO_PADDING (值为 3),它实际上只是为非对称加密定义的,而不是为对称加密定义的,因此根本不应该在这里应用。 对称加密的标志是OPENSSL_RAW_DATA (值为 1)和OPENSSL_ZERO_PADDING (值为 2),因此OPENSSL_NO_PADDING等价于OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING ,因此隐式设置OPENSSL_RAW_DATA
请注意, OPENSSL_ZERO_PADDING不启用零填充,但禁用默认的 PKCS7 填充。 与 SunJCE Provider 类似,openssl 也隐式禁用 GCM 的默认 PKCS7 填充,即无论是否设置OPENSSL_ZERO_PADDING ,GCM 都不使用填充。

总之,可以说填充和标志被排除为错误的原因:在两个代码中都没有应用填充,在OPENSSL_RAW_DATA代码中设置了 OPENSSL_RAW_DATA。

不幸的是,您没有描述错误,所以只能猜测。 问题可能是由不兼容的密钥引起的,因为这两个代码都可以在我的机器上运行并且是兼容的。
正如评论中已经提到的,对于 AES-128/192/256,必须使用 16/24/32 字节的密钥。 在 Java 代码中,密钥的长度决定了 AES 变体,即,例如 16 字节的长度意味着 AES-128。 在 PHP 代码中,必须明确指定 AES 变体,例如aes-128-gcm 太长的键被简单地切断,太短的键用 0 值填充。
例如,如果在 Java 代码中使用 16 字节密钥进行加密(AES-128),在aes-128-gcm代码中使用相同的密钥进行解密,则解密成功。

如果有进一步的问题,请发布使用过的 Java 版本、错误信息以及完整的示例数据,即密钥、明文和密文。


样本数据:

Plaintext (UTF8):   The quick brown fox jumps over the lazy dog
Key (UTF8):         0123456789012345

Java 代码提供(在 JDK 11 下)以下密文(由于随机生成的 IV,这对于每个加密当然是不同的):

Ciphertext (Base64): 8DcD/QwKeFG1u2N1ve3mtsX1Lq7js33ESTigT2GH6Lrqrckh5I4qzkJMG3rnuJ9CSFZ1jai8LTChe3tuIJSMLmMTbUQ9mB0=
IV (hex):            f03703fd0c0a7851b5bb6375

这个密文可以通过 PHP decrypt方法使用aes-128-cbc和上面的密钥来解密:

print(decrypt("8DcD/QwKeFG1u2N1ve3mtsX1Lq7js33ESTigT2GH6Lrqrckh5I4qzkJMG3rnuJ9CSFZ1jai8LTChe3tuIJSMLmMTbUQ9mB0=", "0123456789012345") . "\n");

因此,如果aes-128-cbc 、上述密钥和相同的 IV 被使用, aesGcmEncrypt返回相同的密文(对于后者,PHP 代码中随机生成的 IV 必须替换为 Java 代码中生成的 IV,当然只有对于这个测试):

$iv = hex2bin('f03703fd0c0a7851b5bb6375'); // IV to use in aesGcmEncrypt
print(aesGcmEncrypt("The quick brown fox jumps over the lazy dog", "0123456789012345") . "\n");

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM