You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java解密从OpenSSL(>1.1.1 -md sha256)迁移到(<1.1.1 --pbkdf2)失败求助

Java适配OpenSSL 1.1.1+ PBKDF2加解密报错问题解决

问题背景

原Java代码适配OpenSSL 1.1.1以下版本的命令:

echo -n "password" | openssl enc -aes-256-cbc -a -k secretKey -md sha256

升级到OpenSSL 1.1.1+后改用-pbkdf2参数:

echo -n "password" | openssl enc -aes-256-cbc -a -k secretKey -pbkdf2

修改Java代码使用PBKDF2withHmacSHA256生成密钥后,解密时抛出错误:

Exception in thread "main" modified.EncryptionDecryptionException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
Caused by: javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.

核心错误原因

  1. 密钥推导的密码输入错误:getNewKeyAndIvThatSupportPBKDF方法错误传入了密文的char数组作为PBKDF2的密码参数,而OpenSSL的-k secretKey是用secretKey作为密码推导密钥,与密文无关。
  2. 算法名称大小写问题:Java中PBKDF2标准算法名称为PBKDF2WithHmacSHA256(首字母大写),原代码中的PBKDF2withHmacSHA256可能导致部分JDK环境无法识别。
  3. 参数传递逻辑混乱:getCipher方法保留了旧逻辑的冗余变量,干扰了新密钥推导流程的正确性。

修复步骤

  1. 修正PBKDF2密码输入:调用密钥推导方法时,传入SECRET_KEY的char数组(即OpenSSL命令中的-k参数值),而非密文内容。
  2. 统一算法名称:使用标准大写的PBKDF2WithHmacSHA256算法名称。
  3. 清理冗余代码:移除未使用的passAndSalt等变量,简化密钥推导流程。
  4. 对齐OpenSSL参数:确保PBKDF2迭代次数与OpenSSL默认值(10000)一致。

修正后的完整代码

import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.Base64;
import java.util.Base64.Decoder;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

public class AESWithPBKDF2 {

    private static final String SECRET_KEY = "secretKey";
    private static final String SALT_KEY = "Salted__";
    private static final int KEY_LEN = 32;
    private static final int IV_LEN = 16;
    // OpenSSL -pbkdf2 默认迭代次数为10000
    private static final int PBKDF2_ITERATIONS = 10000;

    public static void main(String[] args) throws Exception {
        // 测试OpenSSL命令生成的密文
        String example = "U2FsdGVkX1+Z9ZkKZ8e6eQ=="; // 替换为实际生成的密文
        String decryptedText = decryptPassword(example.toCharArray());
        System.out.println("解密结果: " + decryptedText);

        // 测试加密后解密的一致性
        String plainText = "password";
        String encrypted = encryptPassword(plainText.toCharArray());
        System.out.println("加密结果: " + encrypted);
        String decrypted = decryptPassword(encrypted.toCharArray());
        System.out.println("解密验证: " + decrypted);
    }

    private AESWithPBKDF2() {
        throw new IllegalStateException("Utility class");
    }

    public static String decryptPassword(char[] source) throws EncryptionDecryptionException {
        return decrypt(source, SECRET_KEY);
    }

    public static String encryptPassword(char[] source) throws EncryptionDecryptionException {
        return encrypt(source, SECRET_KEY);
    }

    private static String decrypt(char[] source, String sk) throws EncryptionDecryptionException {
        try {
            final byte[] magic = SALT_KEY.getBytes(StandardCharsets.UTF_8);
            final Decoder decoder = Base64.getDecoder();
            final byte[] inBytes = decoder.decode(String.valueOf(source));

            checkMagic(magic, inBytes);
            byte[] salt = Arrays.copyOfRange(inBytes, magic.length, magic.length + 8);

            final Cipher cipher = getCipher(Cipher.DECRYPT_MODE, sk.toCharArray(), salt);
            final byte[] clear = cipher.doFinal(inBytes, 16, inBytes.length - 16);

            return new String(clear, StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
                | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
            throw new EncryptionDecryptionException(e.getMessage(), e);
        }
    }

    private static String encrypt(char[] source, String sk) throws EncryptionDecryptionException {
        try {
            final byte[] magic = SALT_KEY.getBytes(StandardCharsets.UTF_8);
            final byte[] inBytes = String.valueOf(source).getBytes(StandardCharsets.UTF_8);
            byte[] salt = new SecureRandom().generateSeed(8);

            final Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, sk.toCharArray(), salt);

            byte[] data = cipher.doFinal(inBytes);
            data = concat(concat(magic, salt), data);

            return Base64.getEncoder().encodeToString(data);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
                | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
            throw new EncryptionDecryptionException(e.getMessage(), e);
        }
    }

    private static Cipher getCipher(int cipherMode, char[] password, byte[] salt)
            throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
        byte[] keyAndIv = getPBKDF2KeyAndIv(password, salt);

        final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, KEY_LEN);
        final byte[] iv = Arrays.copyOfRange(keyAndIv, KEY_LEN, KEY_LEN + IV_LEN);

        final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
        final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(cipherMode, key, new IvParameterSpec(iv));

        return cipher;
    }

    private static byte[] concat(final byte[] a, final byte[] b) {
        final byte[] c = new byte[a.length + b.length];
        System.arraycopy(a, 0, c, 0, a.length);
        System.arraycopy(b, 0, c, a.length, b.length);
        return c;
    }

    private static void checkMagic(final byte[] magic, final byte[] inBytes) {
        final byte[] shouldBeMagic = Arrays.copyOfRange(inBytes, 0, magic.length);
        if (!Arrays.equals(shouldBeMagic, magic)) {
            throw new EncryptionDecryptionException("解密异常: 无效的魔术头");
        }
    }

    private static byte[] getPBKDF2KeyAndIv(char[] password, byte[] salt) throws EncryptionDecryptionException {
        try {
            PBEKeySpec spec = new PBEKeySpec(password, salt, PBKDF2_ITERATIONS, (KEY_LEN + IV_LEN) * 8);
            return SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
                    .generateSecret(spec)
                    .getEncoded();
        } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
            throw new EncryptionDecryptionException(e.getMessage(), e);
        }
    }

    // 自定义异常类
    public static class EncryptionDecryptionException extends Exception {
        public EncryptionDecryptionException(String message, Throwable cause) {
            super(message, cause);
        }
    }
}

内容的提问来源于stack exchange,提问作者Illidan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 08:19:54