java 如何正确使用“PBEWithHmacSHA512AndAES_256”算法?

qxsslcnc  于 2023-01-19  发布在  Java
关注(0)|答案(3)|浏览(463)

我正在进行一些Java加密,无法找到正确使用PBEWithHmacSHA512AndAES_256算法的方法。
加密似乎工作正常,但我无法正确初始化解密密码。
下面是一个简短的程序来演示这个问题。特别是,请参见“问题”注解。
注意:我已经看过this very helpful answer,我可以使用该方案来工作,但我很想知道我在这里做错了什么。

import java.nio.charset.StandardCharsets;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

public final class CryptQuestion {

    private static final String ALGORITHM = "PBEWithHmacSHA512AndAES_256";
    private static final int ITERATIONS = 1000; // Aside: not sure what is a good number, here.

    public static void main(final String[] args) throws Exception {
        final String message = "This is the secret message... BOO!";
        System.out.println("Original : " + message);
        final byte[] messageBytes = message.getBytes(StandardCharsets.US_ASCII);

        final String password = "some password";
        final byte[] salt = "would be random".getBytes(StandardCharsets.US_ASCII);

        // Create the Key
        final SecretKeyFactory factory = SecretKeyFactory.getInstance(ALGORITHM);
        final PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS);
        SecretKey key = factory.generateSecret(keySpec);

        // Build the encryption cipher.
        final Cipher cipherEncrypt = Cipher.getInstance(ALGORITHM);
        cipherEncrypt.init(Cipher.ENCRYPT_MODE, key);

        // Encrypt!
        final byte[] ciphertext = cipherEncrypt.doFinal(messageBytes);
        final byte[] iv = cipherEncrypt.getIV();

        // Now for decryption... The receiving end will have as input:
        // * ciphertext
        // * IV
        // * password
        // * salt

        // We just re-use 'key' from above, since it will be identical.

        final PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, ITERATIONS);
        final IvParameterSpec ivParamSpec = new IvParameterSpec(iv);

        // Build the decryption cipher.
        final Cipher cipherDecrypt = Cipher.getInstance(ALGORITHM);
        // PROBLEM: If I pass "ivParamSpec", I get "java.security.InvalidAlgorithmParameterException: Wrong parameter type: PBE expected"
        // Whereas if I pass pbeParamSpec, I get "java.security.InvalidAlgorithmParameterException: Missing parameter type: IV expected"
        // What to do?
        cipherDecrypt.init(
                Cipher.DECRYPT_MODE,
                key,
                ivParamSpec
                //pbeParamSpec
                );

        final String decrypted = new String(
                cipherDecrypt.doFinal(ciphertext),
                StandardCharsets.US_ASCII);
        System.out.println("Decrypted: " + decrypted);
    }
}
zour9fqk

zour9fqk1#

// PROBLEM: If I pass "ivParamSpec", I get "java.security.InvalidAlgorithmParameterException: Wrong parameter type: PBE expected"
// Whereas if I pass pbeParamSpec, I get "java.security.InvalidAlgorithmParameterException: Missing parameter type: IV expected"
// What to do?
cipherDecrypt.init(
    Cipher.DECRYPT_MODE,
    key,
    ivParamSpec
    //pbeParamSpec
    );

使用加密Cipher中的AlgorithmParameters

cipherDecrypt.init(
    Cipher.DECRYPT_MODE,
    key,
    cipherEncrypt.getParameters()
    );

如果你想要一个更干净的方法,在解密站点不涉及cipherEncrypt,保存算法参数为一个字节,并将它们与密钥数据沿着传输:

byte[]  algorithmParametersEncoded = cipherEncrypt.getParameters().getEncoded();

并且在解密站点重构它们,从而:

AlgorithmParameters algorithmParameters = AlgorithmParameters.getInstance(ALGORITHM);
algorithmParameters.init(algorithmParametersEncoded);

并使用algorithmParameters作为上面Cipher.init()parameters参数。

jfewjypa

jfewjypa2#

对于那些从谷歌跳到这里的人,还有另一种方法可以在java中为PBEwithHmacSHA512AndAES_256密码指定IV。IvParameterSpec可以像这样添加到构造函数中的PBEParameterSpec:

byte[] iv = ...;
byte[] salt = ...;
int iterations = 200000;

IvParameterSpec ivSpec = new IvParameterSpec(iv);
PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, iterations, ivSpec);

这样的pbeSpec就可以用来初始化密码:

cipher.init(
    Cipher.DECRYPT_MODE,
    key,
    pbeSpec
);

无论如何,公认的答案提供了一个更干净的解决方案-使用cipherEncrypt.getParameters().getEncoded()一次性序列化所有密码参数。结果字节数组一次性包括IV、salt和迭代计数。至少对于PBEwithHmacSHA512AndAES_256算法,在加密消息旁边传递此数据是完全可以的

sg2wtvxw

sg2wtvxw3#

我增强了来自jasypt的StandardPBeByteEncryptor以支持“PBEWithHmacSHA512AndAES_256”算法。请参见代码here
参见此处的单元测试PBEEncryptorTest

相关问题