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

如何让Java的java.crypto兼容OpenSSL的AES-256密码加密逻辑?

问题根源

你碰到的问题核心在于:OpenSSL的enc命令并不是直接将输入的密码作为AES密钥使用,而是通过EVP_BytesToKey这个密钥派生函数,把普通字符串密码转换成符合AES算法要求的密钥(以及IV,当你没有手动指定时)。而你直接用secret.getBytes()作为密钥,"some-password"的字节长度只有12,远达不到AES-256需要的32字节,所以抛出了InvalidKeyException。

具体来说,你用到的openssl enc -nosalt参数对应的默认行为是:

  • 使用MD5作为摘要算法(EVP_BytesToKey的默认配置)
  • 盐(salt)为空字节数组
  • 为AES-256-CBC生成32字节密钥 + 16字节IV,总计48字节,会通过多次哈希密码来凑够所需长度
Java实现对应逻辑

下面是完整的Java代码,完美复现OpenSSL的这个密钥派生逻辑,并且能正确解密你提供的示例数据:

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;

public class OpenSSLAesHandler {

    // 实现OpenSSL的EVP_BytesToKey逻辑,适配-nosalt模式、默认MD5摘要
    private static byte[] evpBytesToKey(byte[] password, int keyLength, int ivLength) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] combined = new byte[keyLength + ivLength];
        int offset = 0;
        byte[] currentHash = new byte[0];

        while (offset < combined.length) {
            md.reset();
            // 第一次哈希仅用密码;后续循环用上次的哈希结果+密码再次哈希
            if (offset > 0) {
                md.update(currentHash);
            }
            md.update(password);
            currentHash = md.digest();

            // 将哈希结果复制到最终的密钥+IV数组中,直到填满
            int copySize = Math.min(currentHash.length, combined.length - offset);
            System.arraycopy(currentHash, 0, combined, offset, copySize);
            offset += copySize;
        }
        return combined;
    }

    public static String decrypt(String encryptedBase64, String password) throws Exception {
        // 1. 解码Base64格式的加密数据
        byte[] encryptedData = Base64.getDecoder().decode(encryptedBase64);

        // 2. 生成符合OpenSSL要求的密钥和IV:AES-256需32字节密钥,CBC需16字节IV
        byte[] keyIvPair = evpBytesToKey(password.getBytes(StandardCharsets.UTF_8), 32, 16);
        byte[] aesKey = new byte[32];
        byte[] iv = new byte[16];
        System.arraycopy(keyIvPair, 0, aesKey, 0, 32);
        System.arraycopy(keyIvPair, 32, iv, 0, 16);

        // 3. 初始化Cipher:必须指定完整的算法模式和填充,OpenSSL默认用PKCS#5填充
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(aesKey, "AES"), new IvParameterSpec(iv));

        // 4. 解密并返回明文
        byte[] decryptedBytes = cipher.doFinal(encryptedData);
        return new String(decryptedBytes, StandardCharsets.UTF_8);
    }

    // 测试你的示例
    public static void main(String[] args) throws Exception {
        String encryptedStr = "luYWG/C5uryBVtPHilO6Pg==";
        String password = "some-password";
        System.out.println(decrypt(encryptedStr, password)); // 输出: some-data
    }
}
关键细节提醒
  • 填充方式:OpenSSL的enc默认使用PKCS#5填充(和PKCS#7兼容),所以Java里必须明确指定"AES/CBC/PKCS5Padding",不能只写"AES"(否则会用JVM默认的模式和填充,可能和OpenSSL不匹配)。
  • 字符编码:用StandardCharsets.UTF_8确保密码和明文的编码和OpenSSL一致(OpenSSL默认用UTF-8处理字符串)。
  • IV的必要性:CBC模式必须使用IV,当你没给OpenSSL指定-iv参数时,它会通过EVP_BytesToKey一起生成IV,所以Java代码里不能跳过这一步。
  • 自定义摘要:如果后续你用OpenSSL的-md参数指定了其他摘要算法(比如SHA256),只需要把MessageDigest.getInstance("MD5")改成对应的算法即可。

内容的提问来源于stack exchange,提问作者William Añez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:24:57