如何在java中使用libsodium正确解密文件(chacha20poly1305)?

问题描述 投票:0回答:1

我想创建可用于解密文件的 jar 文件。代码如下所示:

package org.example;

import com.goterl.lazysodium.LazySodium;
import com.goterl.lazysodium.LazySodiumJava;
import com.goterl.lazysodium.SodiumJava;
import com.goterl.lazysodium.interfaces.AEAD;
import com.goterl.lazysodium.utils.Key;

import javax.crypto.AEADBadTagException;
import javax.xml.bind.DatatypeConverter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Paths;

public class Main {

    public static void main(String[] args) {
        if (args.length != 4) {
            System.out.println("Usage: java -jar YourJarFileName.jar cryptoContentFilePath nonceHex keyHex additionalHex");
            System.exit(1);
        }

        String cryptoContentFilePath = args[0];
        String nonceHex = args[1];
        String keyHex = args[2];
        String additionalHex = args[3];

        byte[] nonceBytes = DatatypeConverter.parseHexBinary(nonceHex);
        byte[] keyBytes = DatatypeConverter.parseHexBinary(keyHex);

        Key key = Key.fromBytes(keyBytes);

        LazySodium sodium = new LazySodiumJava(new SodiumJava());

        try (FileInputStream fis = new FileInputStream(cryptoContentFilePath)) {
            byte[] cryptoContentBytes = new byte[fis.available()];
            fis.read(cryptoContentBytes);

            String rawData = sodium.decrypt(new String(cryptoContentBytes), additionalHex, nonceBytes, key, AEAD.Method.CHACHA20_POLY1305);
            System.out.println(rawData);

            // Write decrypted content to a text file
            writeToFile("decrypted_output.jpg", rawData);

        } catch (IOException | AEADBadTagException e) {
            throw new RuntimeException(e);
        }
    }

    private static void writeToFile(String filePath, String content) {
        try (FileOutputStream fos = new FileOutputStream(filePath)) {
            fos.write(content.getBytes());
        } catch (IOException e) {
            throw new RuntimeException("Error writing to file", e);
        }
    }
}

我将使用以下命令来运行解密过程:

java -jar decrypt.jar encrypted.jpg nonceHex keyHex additionalHex"

使用适当的随机数、密钥和附加值时,解密过程似乎可以正常工作。然而,当我尝试打开已经解密的文件时,我遇到了困难,根本无法打开它。该文件似乎已损坏,解密文件和原始文件之间的文件大小不同表明了这一点。

使用 VS Code 检查两个文件的字节文本后,我发现它们包含完全相同的内容。这表明解密逻辑运行正常。该问题似乎与在解密文件中保留元数据有关。

我的解密文件似乎缺少元数据。例如,在解密音频文件时,生成的解密文件不包含原始文件中存在的持续时间或采样率等信息。

我做错了什么?如何使用 libsodium 正确解密文件。谢谢你。

java encryption libsodium
1个回答
0
投票

我终于通过理解 read(byte[] b) 不能保证读取 b.length 字节来解决了我的问题。感谢@Topaco 的评论,现在它成功解密了整个文件。

首先,我还需要自己修改libsodium,因为libsodium的解密函数不支持接收[]byte,所以我fork了官方的libsodium并自己修改了解密函数,这样就可以直接接收[]byte。然后我编译为 .jar 文件并将其放入我的应用程序 gradle 中。你可以在这里看到我的叉子。

这是我的最终代码:

package org.example;

import com.goterl.lazysodium.LazySodium;
import com.goterl.lazysodium.LazySodiumJava;
import com.goterl.lazysodium.SodiumJava;
import com.goterl.lazysodium.interfaces.AEAD;
import com.goterl.lazysodium.utils.Key;

import javax.crypto.AEADBadTagException;
import javax.xml.bind.DatatypeConverter;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        if (args.length != 5) {
            System.out.println("Usage: java -jar YourJarFileName.jar cryptoContentFilePath decryptedOutputPath nonceHex keyHex additionalHex");
            System.exit(1);
        }

        String cryptoContentFilePath = args[0];
        String decryptedOutputPath = args[1];
        String nonceHex = args[2];
        String keyHex = args[3];
        String additionalHex = args[4];

        byte[] nonceBytes = DatatypeConverter.parseHexBinary(nonceHex);
        byte[] keyBytes = DatatypeConverter.parseHexBinary(keyHex);

        Key key = Key.fromBytes(keyBytes);

        LazySodium sodium = new LazySodiumJava(new SodiumJava());

        try (FileInputStream fis = new FileInputStream(cryptoContentFilePath)) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[4096];  // You can adjust the buffer size as needed

            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                baos.write(buffer, 0, bytesRead);
            }

            byte[] rawData = baos.toByteArray();
            int divideSize = 1024*1024;  // You can adjust the divideSize as needed
            int appendSize = divideSize + AEAD.CHACHA20POLY1305_ABYTES;

            int totalCount = (rawData.length / appendSize) + ((rawData.length % appendSize) > 0 ? 1 : 0);
            byte[] finalData = new byte[rawData.length - (totalCount * AEAD.CHACHA20POLY1305_ABYTES)];

            for (int i = 0; i < totalCount; i++) {
                int tempSize = (i == totalCount - 1) ? rawData.length - (i * appendSize) : appendSize;
                byte[] tempData = new byte[tempSize];

                System.arraycopy(rawData, i * appendSize, tempData, 0, tempSize);

                byte[] endData = sodium.decrypt(tempData, additionalHex, nonceBytes, key, AEAD.Method.CHACHA20_POLY1305);

                System.arraycopy(endData, 0, finalData, i * divideSize, endData.length);
            }

            // Write decrypted content to a text file
            writeToFile(decryptedOutputPath, finalData);
            System.out.println("File successfully decrypted: " + decryptedOutputPath);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static void writeToFile(String filePath, byte[] content) {
        try (FileOutputStream fos = new FileOutputStream(filePath)) {
            fos.write(content);
        } catch (IOException e) {
            throw new RuntimeException("Error writing to file", e);
        }
    }

}
© www.soinside.com 2019 - 2024. All rights reserved.