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

如何在Java应用中解密通过OpenSSL AES-256-CBC加密的文件?

没问题,我帮你搞定这个Java解密OpenSSL AES-256-CBC加密文件的需求!先给你理清楚你用的那个OpenSSL命令的细节(顺便提一句,命令里-out后面多了个横杠,正确写法应该是openssl enc -e -aes-256-cbc -in file.xml -out file.xml.aes -pass pass:password,不过这不影响解密逻辑),然后直接上可运行的代码~

解密前必须知道的OpenSSL默认行为

你用的enc命令没写全参数,但OpenSSL有几个默认规则,解密时必须严格对应:

  • 加密后的文件开头会有8字节的固定魔数Salted__,紧接着是8字节的盐值(salt)
  • 密钥和IV是通过EVP_BytesToKey算法从密码和盐派生的,默认用MD5哈希,迭代次数为1
  • 填充方式是PKCS#7(和Java里的PKCS5Padding兼容,因为PKCS#5是PKCS#7的子集)

完整Java解密代码

这段代码直接实现了对应OpenSSL逻辑的解密,不需要额外依赖,用Java自带的加密库即可:

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.MessageDigest;
import java.util.Arrays;

public class OpenSSLAesDecryptor {

    public static void decrypt(String encryptedFilePath, String decryptedFilePath, String password) throws Exception {
        try (FileInputStream fis = new FileInputStream(encryptedFilePath);
             FileOutputStream fos = new FileOutputStream(decryptedFilePath)) {

            // 第一步:读取OpenSSL加密文件的魔数和盐值
            byte[] magicBytes = new byte[8];
            fis.read(magicBytes);
            if (!Arrays.equals(magicBytes, "Salted__".getBytes())) {
                throw new IllegalArgumentException("输入的文件不是OpenSSL enc命令加密的文件");
            }

            byte[] salt = new byte[8];
            fis.read(salt);

            // 第二步:用EVP_BytesToKey算法生成AES密钥和IV
            byte[] keyAndIv = evpBytesToKey(32, 16, MessageDigest.getInstance("MD5"), salt, password.getBytes());
            byte[] keyBytes = Arrays.copyOfRange(keyAndIv, 0, 32); // AES-256需要32字节密钥
            byte[] ivBytes = Arrays.copyOfRange(keyAndIv, 32, 48); // CBC模式需要16字节初始化向量

            // 第三步:初始化AES解密器
            SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
            IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);

            // 第四步:分块读取加密文件,解密后写入输出文件
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                byte[] decryptedChunk = cipher.update(buffer, 0, bytesRead);
                if (decryptedChunk != null) {
                    fos.write(decryptedChunk);
                }
            }
            // 处理最后一块(可能包含填充)
            byte[] finalChunk = cipher.doFinal();
            if (finalChunk != null) {
                fos.write(finalChunk);
            }
        }
    }

    // 实现OpenSSL的EVP_BytesToKey密钥派生算法
    private static byte[] evpBytesToKey(int keyLength, int ivLength, MessageDigest md, byte[] salt, byte[] password) {
        byte[] result = new byte[keyLength + ivLength];
        int resultPos = 0;
        byte[] currentHash = new byte[0];

        while (resultPos < result.length) {
            // 拼接之前的哈希、密码、盐值作为新的哈希输入
            byte[] hashInput = new byte[currentHash.length + password.length + salt.length];
            System.arraycopy(currentHash, 0, hashInput, 0, currentHash.length);
            System.arraycopy(password, 0, hashInput, currentHash.length, password.length);
            System.arraycopy(salt, 0, hashInput, currentHash.length + password.length, salt.length);

            currentHash = md.digest(hashInput);
            int copySize = Math.min(currentHash.length, result.length - resultPos);
            System.arraycopy(currentHash, 0, result, resultPos, copySize);
            resultPos += copySize;

            // 重置MessageDigest实例,避免残留状态影响下一次哈希计算
            md.reset();
        }
        return result;
    }

    // 测试用主方法
    public static void main(String[] args) {
        try {
            // 替换成你的加密文件路径、输出文件路径和密码
            decrypt("file.xml.aes", "decrypted_file.xml", "password");
            System.out.println("解密完成!解密后的文件已生成");
        } catch (Exception e) {
            System.err.println("解密失败:" + e.getMessage());
            e.printStackTrace();
        }
    }
}

关键注意事项

  • AES-256权限问题:如果运行时遇到InvalidKeyException,可能是因为Java默认的加密策略限制。Java 8及以下需要安装JCE无限制权限策略文件,Java 9+则默认支持,不需要额外操作。
  • 密码编码一致性:代码中用password.getBytes()默认采用UTF-8编码,OpenSSL的pass参数默认也是UTF-8,所以如果你的密码包含特殊字符,确保两边编码一致。
  • 异常处理:示例代码里的异常处理比较简单,实际生产环境中可以细化,比如捕获文件不存在、密码错误等具体场景,给出更友好的提示。
  • 资源管理:代码用了try-with-resources语法,会自动关闭输入输出流,避免资源泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:12:16