我用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代码。
1条答案
按热度按时间q9yhzks01#
这两种代码的行为可能与您预期的不同。
在java代码中,虽然
PKCS5Padding
(在java中,指定了pkcs7padding的同义词)。sunjce提供程序禁用指定的PKCS5Padding
适用于gcmNoPadding
. 这很有用,因为gcm是一种不需要填充的流密码模式。应该提到的是,行为依赖于版本。只有早期的jdk版本(如8、11、12)才接受
PKCS5Padding
用于gcm并作为NoPadding
. 相比之下,更高版本的jdk(例如14、15)会引发一个异常。另外,其他提供者的行为也可能不同。在php代码中,密文由
openssl_encrypt
作为原始数据,因此只对base64编码一次(即在与iv和tag串联之后),这是应该的。因此,代码的行为就像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
不启用零填充,但禁用默认的pkcs7填充。与sunjce提供程序类似,openssl也隐式地禁用gcm的默认pkcs7填充,即无论OPENSSL_ZERO_PADDING
是否设置,gcm不使用填充。总之,可以说,填充和标志被排除在错误的原因之外:在这两个代码中,在php代码中没有应用填充
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)和php代码中
aes-128-gcm
并且应用相同的密钥进行解密,则解密成功。如果有进一步的问题,请张贴使用的java版本,错误信息和完整的样本数据,即密钥,明文和密文。
样本数据:
java代码(在jdk 11下)提供了以下密文(当然,由于随机生成的iv,每个加密都不同):
这个密文可以由php解密
decrypt
方法使用aes-128-cbc
以及上面的键:相应地
aesGcmEncrypt
返回相同的密文,如果aes-128-cbc
,使用上述键和相同的iv(对于后者,php代码中随机生成的iv必须替换为java代码中生成的iv,当然仅用于此测试):