首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >PBKDF2WithHmacSHA256在Java中的实现

PBKDF2WithHmacSHA256在Java中的实现
EN

Stack Overflow用户
提问于 2022-07-07 08:01:59
回答 1查看 163关注 0票数 0

我有以下哈希算法的Java实现:

代码语言:javascript
运行
复制
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实现的目标:

代码语言:javascript
运行
复制
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实现的输出值不同

EN

回答 1

Stack Overflow用户

发布于 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、迭代计数和密钥大小使用相同的值,则这两种代码都将返回相同的密文。

完整代码和示例:

代码语言:javascript
运行
复制
<?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编码的)密文:

代码语言:javascript
运行
复制
cqSHCSOOZlZmugahBVpTusElSEN1gKqiQQDUr1YF1AlLAO466g1/DldwcuoWOjOW

安保:

看上去你用的是静态盐。静态盐通常是有问题的(参见这里)。因为静态IV还有一个额外的问题:由于静态salt总是为相同的密码生成相同的密钥,所以密钥/IV对被重用,这也是不安全的(对于一些算法,比如CTR或GCM,甚至是致命的)。

因此,应该为这里的每个加密(通常)生成足够长度的随机盐。随机盐生成不同的密钥,因此即使使用静态IV,也不会重用密钥/IV对。盐不是秘密的,与密文一起传递到解密端,通常是串联的。

如果你想避免IV依赖于盐的随机性,也可以随机产生IV。IV也不是秘密的,与盐和密文一起传递到解密方,也是串联的。

在解密侧,可以使用已知的盐和/或IV长度来分离各部分。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72894266

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档