使用 BouncyCastle SSL 通过 keyFile 进行 Java AES 解密

2023-12-10

我正在尝试编写 Java 代码,使用与 OpenSSL 解密兼容的 BouncyCastle 来解密使用 AES256 加密的文件。

s_key 是提供的文件,其中包含将用于加密和解密的密钥

需要完成的步骤: 1 - 读取密钥文件 2 - 使用提供的密钥解密文件 inputfilename

到目前为止我已经使用过下面的内容,但出现错误:

import java.io.*;
import java.nio.charset.StandardCharsets;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import org.apache.commons.io.FileUtils;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator;
import org.bouncycastle.crypto.io.CipherOutputStream;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.ParametersWithIV;

import javax.crypto.NoSuchPaddingException;


public class test5_encrypt {

    public static void main(String[] args) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException {


        File file = new File("/home/roxane/key");
        String passwordStr = FileUtils.readFileToString(file, "UTF-8");

        String outputPath = "/home/roxane/test1";
        String inputPath = "/home/roxane/test";


        SecureRandom random = new SecureRandom();
        byte salt[] = new byte[8];
        random.nextBytes(salt);

// Derive 32 bytes key (AES_256) and 16 bytes IV
        byte[] password = passwordStr.getBytes(StandardCharsets.UTF_8);
        OpenSSLPBEParametersGenerator pbeGenerator = new OpenSSLPBEParametersGenerator(new MD5Digest()); // SHA256 as of v1.1.0 (if in OpenSSL the default digest is applied)
        pbeGenerator.init(password, salt);
        ParametersWithIV parameters = (ParametersWithIV) pbeGenerator.generateDerivedParameters(256, 128);// keySize, ivSize in bits
        System.out.println(parameters.getIV());


// Decrypt with AES-256
        try (FileOutputStream fos = new FileOutputStream(outputPath)) {

           // Encrypt chunkwise (for large data)
            PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
            cipher.init(false, parameters);
            try (FileInputStream fis = new FileInputStream(inputPath);
                 CipherOutputStream cos = new CipherOutputStream(fos, cipher)) {
                int bytesRead = -1;
                byte[] buffer = new byte[64 * 1024 * 1024];
                while ((bytesRead = fis.read(buffer)) != -1) {
                    cos.write(buffer, 0, bytesRead);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);

        }


    }
}

Error:

Exception in thread "main" java.lang.RuntimeException: org.bouncycastle.crypto.io.InvalidCipherTextIOException: Error finalising cipher data
    at decrypt.test5_encrypt.main(test5_encrypt.java:61)
Caused by: org.bouncycastle.crypto.io.InvalidCipherTextIOException: Error finalising cipher data
    at org.bouncycastle.crypto.io.CipherOutputStream.close(Unknown Source)
    at decrypt.test5_encrypt.main(test5_encrypt.java:59)
Caused by: org.bouncycastle.crypto.InvalidCipherTextException: pad block corrupted

当使用密码时,OpenSSL 以特定格式存储密文,即 ASCII 编码Salted__,后面是 8 个字节的 salt,然后是实际的密文。

在解密过程中,salt 一定不能是随机生成的(就像在发布的代码中所做的那样),否则将得出错误的密钥和 IV。相反,盐必须根据密文的元数据来确定。还必须修复流类的使用:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator;
import org.bouncycastle.crypto.io.CipherInputStream;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.ParametersWithIV; 

...

String inputPath = "...";   // path to enc file
String outputPath = "...";  // path to dec file
String passwordStr = "...";

// Decrypt with AES-256, CBC using streams
try (FileInputStream fis = new FileInputStream(inputPath)){

    // Determine salt from OpenSSL format
    fis.readNBytes(8); // Skip prefix Salted__
    byte[] salt = fis.readNBytes(8); // Read salt
    
    // Derive 32 bytes key (AES_256) and 16 bytes IV via EVP_BytesToKey()
    byte[] password = passwordStr.getBytes(StandardCharsets.UTF_8);
    OpenSSLPBEParametersGenerator pbeGenerator = new OpenSSLPBEParametersGenerator(new MD5Digest()); // SHA256 as of v1.1.0 (if in OpenSSL the default digest is applied)
    pbeGenerator.init(password, salt);
    ParametersWithIV parameters = (ParametersWithIV) pbeGenerator.generateDerivedParameters(256, 128); // keySize, ivSize in bits
    
    // Decrypt chunkwise (for large data)
    PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
    cipher.init(false, parameters);
    try (CipherInputStream cis = new CipherInputStream(fis, cipher);
         FileOutputStream fos = new FileOutputStream(outputPath)) {
        int bytesRead = -1;
        byte[] buffer = new byte[64 * 1024 * 1024]; // chunksize, e.g. 64 MiB
        while ((bytesRead = cis.read(buffer)) != -1) {
            fos.write(buffer, 0, bytesRead);
        }               
    }
}

这在功能上等同于 OpenSSL 语句:

openssl enc -d -aes256 -k <passpharse> -in <enc file> -out <dec file>

请注意,OpenSSL 在早期版本中默认应用 MD5 作为摘要,自 v.1.1.0 起使用 SHA256。代码和 OpenSSL 语句必须使用相同的摘要以实现兼容性。
在代码中,摘要是显式指定的,在 OpenSSL 语句中,可以通过以下方式显式设置:-md选项,以便双方都可以匹配。
请记住EVP_BytesToKey()OpenSSL 默认使用它来派生密钥,如今被认为是不安全的。


关于 Java 8 的补充:对于 Java 8,例如盐的测定可采用下列方法:

int i = 0;
byte[] firstBlock = new byte[16]; 
while (i < firstBlock.length) {
    i += fis.read(firstBlock, i, firstBlock.length - i);
}
byte[] salt = Arrays.copyOfRange(firstBlock, 8, 16);

循环是必要的,因为read(byte[],int,int),不像readNBytes(int), does 不保证缓冲区是完全充满(这里考虑非 EOF 和非错误情况)。

如果省略循环(这意味着使用等效的read(byte[])),对于那些也完全填满缓冲区的 JVM,代码仍将运行。由于这适用于最常见的小缓冲区大小的 JVM,因此代码大部分都可以工作,请参阅 dave_thompson_085 的评论。但是,这不能保证anyJVM,因此不太健壮(尽管可能不是很多)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 BouncyCastle SSL 通过 keyFile 进行 Java AES 解密 的相关文章

随机推荐

  • 无法删除 matplotlib.animation.FuncAnimation 对象

    编辑 TL 博士 看起来好像有一个matplotlib backends backend qt4 TimerQT包含对我的 FuncAnimation 对象的引用的对象 如何删除它以释放 FuncAnimation 对象 1 一些背景知识
  • Beautiful Soup Find - 只获取文字

    我有这段代码仅以字符串形式输出价格 125 01 但我必须更改一些内容 因为现在它会打印带有 html 标签和所有内容的整行 我怎样才能让它只打印出文本 而不使用正则表达式 import requests from bs4 import B
  • WP7 GestureListener 上图像宽度/高度的约束

    我添加了GestureListener对于一个图像 我试图像这样的 Stack Overflow 答案进行缩放 如何在WP7中放大和缩小图像 问题是图像永远不会停止缩放 并覆盖页面上的其他控件 这涵盖了页面上的一些重要按钮 此外 它允许图像
  • 找不到 Graphviz 的可执行文件(Python 3.4)

    我在 Windows 7 上运行 Python3 4 我尝试使用 graphviz 的 Python 接口 这是我打算运行的脚本 from graphviz import Digraph import pydotplus dot Digra
  • 通过命令行替换 csv 中的特定文本

    我有 csv 格式的数据 可以从以下位置获取输出SQL Server 数据有一些NULL and N A 写出的值构成列字符类型 否则它只会由整数组成 是否可以通过批处理文件语句查找这些值并将其替换为数字 例如 1NULL和 2 为N A
  • PYSPARK:如何可视化 GraphFrame?

    假设我创建了下图 我的问题是如何可视化它 Create a Vertex DataFrame with unique ID column id v sqlContext createDataFrame a Alice 34 b Bob 36
  • 如何使用 openssl lib pem_read 从字符串读取公钥/私钥

    我在C 中使用openssl 将PrivateKey和PublicKey文件内容复制到两个const char 但我看到api的演示只是从pem文件中获取pem公钥 私钥 那么如何从字符串中获取公钥 私钥呢 尝试这个 char mKey B
  • 缩放 UIPageControl 的当前点并保持其居中

    我对 UIPageControl 进行了子类化 以便使其当前的点更大 class CustomPageControl UIPageControl override var currentPage Int didSet updateDots
  • 在 Python 中将数据文件列拆分为单独的数组

    我是 python 新手 一整天都在试图解决这个问题 我有一个数据文件 如下所示 time I R stkb Step Information Temp 0 Run 1 11 0 000000000000000e 000 0 000000e
  • 在 PHPMailer 上使用 OAuth 2.0 的 Microsoft Office 授权问题

    我在通过 PHPMail 发送 SMTP 邮件期间遇到 OAuth 2 0 授权问题 基本上 我已经尝试了下面列出的所有范围 email openid profile https graph microsoft com Mail Send
  • 用 div 替换文本框,jquery 不起作用

    它只工作了几秒钟 然后 div 再次消失 document ready function done click function txtname replaceWith function return div this val div 你也
  • 如何使用太阳黑子实现通配符搜索

    随时欢迎任何帮助 我将 sunspot 与 solr 一起使用 但无法找到任何好的解决方案来说明如何使用 sunspot 执行通配符搜索 如果我搜索 8088 它应该返回以 8088 但不是 228088560 开头的所有数字 在 solr
  • 从 libgdx 中的集合中检测触摸对象(移动)的最佳方法

    这是我第一次尝试游戏开发 我刚刚开始尝试 libgdx 并了解游戏编程的不同方面 我看了示例项目 我可以了解libgdx游戏的整体架构 但为了掌握游戏动力学的基础知识 我开始玩低级的东西 比如如何绘制简单的形状 如何移动它们 如何处理此类碰
  • 从 C# 应用程序中启动 SFC

    我一直在寻找 但似乎无法完成这项工作 我正在尝试从 C 应用程序中的按钮启动 SFC 我知道这需要权利提升 并且在我试图做的范围内是我想要的行为 我努力了 以管理员身份运行 cmd 和命令 使用来自 C 的参数以管理员身份运行 CMD an
  • 如何在 firebase 中实现基于角色的访问控制

    这是我第一次涉足 Firebase 和 nosql 我有 SQL 背景 使用简单登录安全电子邮件 密码 如何限制对 Firebase 中数据的访问 例如 某些用户有权创建业务对象 用户 客户 类别等 而其他用户则无权 有没有办法将权限列表附
  • 具有动态行和列的 angular2 表

    如何在 angular2 中创建具有动态列和行的表格 我的数据来自休息服务并在此可观察中捕获 this physicalDataService getPhysicalData subscribe res gt this data res 我
  • 使用查询字符串和锚点标签形成正确的 URL

    当查询字符串和锚标记 哈希标记 在 URL 中可见时 它们的正确显示顺序是什么 http www whatever com var val anchor or http www whatever com anchor var val 有这方
  • 组合 ASP.NET 属性中的字符串

    我正在尝试在属性内连接一个字符串 我收到一个错误 我认为这与我的Eval 是否有正确的方法来连接字符串 或者这是不可能的 我认为问题在于我设置 NavigateUrl 的位置
  • 循环优化。寄存器重命名如何打破依赖关系?什么是执行端口容量?

    我正在分析 Agner Fog 的 optimization assemble 中的循环示例 我指的是12 9章 代码是 我简化了一下 L1 vmulpd ymm1 ymm2 rsi rax vaddpd ymm1 ymm1 rdi rax
  • 使用 BouncyCastle SSL 通过 keyFile 进行 Java AES 解密

    我正在尝试编写 Java 代码 使用与 OpenSSL 解密兼容的 BouncyCastle 来解密使用 AES256 加密的文件 s key 是提供的文件 其中包含将用于加密和解密的密钥 需要完成的步骤 1 读取密钥文件 2 使用提供的密