我有以下哈希算法的Java实现:
public String encrypt(@NotNull String value) {
String ret = null;
var iv = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // 16 bytes
var ivspec = new IvParameterSpec(iv);
try {
var factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
var spec = new PBEKeySpec(AUTH_SECRET.toCharArray(),
SALT.getBytes(StandardCharsets.UTF_8), ITERATION_COUNT, KEY_LENGTH);
var tmp = factory.generateSecret(spec);
var secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
var cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);
ret = Base64.getEncoder()
.encodeToString(cipher.doFinal(value.getBytes(StandardCharsets.UTF_8)));
} catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchPaddingException |
InvalidKeyException | InvalidAlgorithmParameterException | BadPaddingException |
IllegalBlockSizeException e) {
LOGGER.error("Error while encrypting value: {}", value, e);
}
return ret;
}
public String decrypt(@NotNull String value) {
String ret = null;
var iv = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
var ivspec = new IvParameterSpec(iv);
try {
var factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
var spec = new PBEKeySpec(AUTH_SECRET.toCharArray(),
SALT.getBytes(StandardCharsets.UTF_8), ITERATION_COUNT, KEY_LENGTH);
var tmp = factory.generateSecret(spec);
var secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
var cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivspec);
ret = new String(cipher.doFinal(Base64.getDecoder().decode(value)),
StandardCharsets.UTF_8);
} catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchPaddingException |
InvalidKeyException | InvalidAlgorithmParameterException | BadPaddingException |
IllegalBlockSizeException | IllegalArgumentException e) {
LOGGER.error("Error while decrypting value: {}", value, e);
}
return ret;
}
经过研究,我发现了以下可能需要使用的函数:pbkdf2 hmac
在这个主题上已经有一个线程:PHP中的"PBKDF2WithHmacSHA256“替代,但是,我的实现似乎与线程中的实现不同。例如,我使用OpenSSL生成了一个OpenSSL。我要把它作为参数传递到哪里?
到目前为止,这就是我使用PHP实现的目标:
function derivate_key($password, $salt, $iterations, $keyLengthBits): string {
return hash_pbkdf2("sha256", $password, $salt, $iterations, $keyLengthBits / 8, true);
}
function encrypt($plaintext, $key): string {
$iv = str_repeat(chr(0), 16);
$cipher = "AES-256-CBC";
$ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, 0, $iv);
$hmac = hash_hmac("sha256", $ciphertext_raw, $key, true);
return base64_encode($iv . $hmac . $ciphertext_raw);
}
不幸的是,输出值与Java实现的输出值不同
发布于 2022-07-08 11:21:21
为了使PHP代码提供与Java代码相同的密文,以下内容是必要的:
encrypt()
中,必须删除HMAC和级联的确定,因为这两种情况都不会发生在Java代码中。$ciphertext_raw
(其中$ciphertext_raw
是一个奇怪的名称,因为openssl_encrypt()
返回默认编码的密文Base64 )。encrypt()
的密钥将由derivate_key()
确定。请注意,$keyLengthBits
和算法必须是一致的,例如对于n位密钥AES-n必须指定.然后,如果对密码、salt、迭代计数和密钥大小使用相同的值,则这两种代码都将返回相同的密文。
完整代码和示例:
<?php
function derivate_key($password, $salt, $iterations, $keyLengthBits): string {
return hash_pbkdf2("sha256", $password, $salt, $iterations, $keyLengthBits / 8, true);
}
function encrypt($plaintext, $key): string {
$iv = str_repeat(chr(0), 16);
$cipher = "AES-256-CBC";
$ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, 0, $iv);
//$hmac = hash_hmac("sha256", $ciphertext_raw, $key, true);
//return base64_encode($iv . $hmac . $ciphertext_raw);
return $ciphertext_raw;
}
$salt = "This is a test salt";
$password = "This is a test Passphrase";
$iterations = 10000;
$keyLengthBits = 256;
$key = derivate_key($password, $salt, $iterations, $keyLengthBits);
$ciphertext = encrypt("The quick brown fox jumps over the lazy dog", $key);
print($ciphertext . PHP_EOL); // cqSHCSOOZlZmugahBVpTusElSEN1gKqiQQDUr1YF1AlLAO466g1/DldwcuoWOjOW
?>
它产生与Java代码相同的(Bas64编码的)密文:
cqSHCSOOZlZmugahBVpTusElSEN1gKqiQQDUr1YF1AlLAO466g1/DldwcuoWOjOW
安保:
看上去你用的是静态盐。静态盐通常是有问题的(参见这里)。因为静态IV还有一个额外的问题:由于静态salt总是为相同的密码生成相同的密钥,所以密钥/IV对被重用,这也是不安全的(对于一些算法,比如CTR或GCM,甚至是致命的)。
因此,应该为这里的每个加密(通常)生成足够长度的随机盐。随机盐生成不同的密钥,因此即使使用静态IV,也不会重用密钥/IV对。盐不是秘密的,与密文一起传递到解密端,通常是串联的。
如果你想避免IV依赖于盐的随机性,也可以随机产生IV。IV也不是秘密的,与盐和密文一起传递到解密方,也是串联的。
在解密侧,可以使用已知的盐和/或IV长度来分离各部分。
https://stackoverflow.com/questions/72894266
复制相似问题