Java和Javascript之间的加密和解密将不起作用

2022-09-03 15:34:13

编辑 1

在 decryptFile 方法中,解密部分不会输出任何内容。

let decrypted = CryptoJS.AES.decrypt(e.target.result, CryptoJS.enc.Utf8.parse(key), {
    iv: CryptoJS.enc.Utf8.parse(iv),
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
});


编辑 2评论部分中给出的链接部分解决了该问题。它确实跨平台加密和解密,但由于PBKDF2具有SHA256哈希,因此速度相当慢。我找不到一种方法来只使用AES部分而不是PKBDF2部分。


原文

我对Java和Javascript版本使用相同的密钥和IV。我无法解密在Javascript中加密的文件,也无法解密在Javascript中加密的文件。我需要这两者相互兼容,但我无法弄清楚我如何尝试在Javascript中解密以前在Java中加密的文件。我已经成功地在两者之间实现了解密和加密文本,但是当我想解密在Java中加密的文件时,它就不起作用了。

加密/解密Java中的文件:

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class Test {
    private SecretKey secretKey;
    private IvParameterSpec ivParameterSpec;

    private String key = "ThisIsMyGreatKey";
    private byte[] ivKey = "ABCDEFGHabcdefgh".getBytes();

    public static void main(String[] args) {
        try {
            new Test().run();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void run() {
        ivParameterSpec = new IvParameterSpec(ivKey);
        secretKey = new SecretKeySpec(key.getBytes(), "AES");
        encryptOrDecryptFile(Cipher.ENCRYPT_MODE,
            new File("src/cactus.jpg"), new File("src/cactus-encrypted.jpg"));
    }

    private void encryptOrDecryptFile(int mode, File inputFile, File outputFile) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(mode, secretKey, ivParameterSpec);

            // Read input
            byte[] input = new byte[(int) inputFile.length()];
            FileInputStream inputStream = new FileInputStream(inputFile);
            inputStream.read(input);

            // Encrypt and write to output
            byte[] output = cipher.doFinal(input);
            FileOutputStream outputStream = new FileOutputStream(outputFile);
            outputStream.write(output);

            inputStream.close();
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在Javascript中加密/解密

<input type="file" id="file-input" onchange="handleFile(this)">
<button onclick="useEncryptionForFile()" id="encrypt-file">Encrypt File</button>
<button onclick="useDecryptionForFile()" id="decrypt-file">Decrypt File</button>
<textarea id="output"></textarea>
<img id="example">

<script>
    let key = "ThisIsMyGreatKey";
    let iv = "ABCDEFGHabcdefgh";
    let useEncryption, useDecryption;

    let input = document.getElementById("file-input");
    let output = document.getElementById("output");
    let example = document.getElementById("example");

    function handleFile(element) {
        if (element.files && element.files[0]) {
            let file = element.files[0];
            if (useDecryption) {
                decryptFile(file);
            } else {
                encryptFile(file);
            }
        }
    }

    function encryptFile(file) {
        let reader = new FileReader();

        reader.onload = function (e) {
            let encrypted = CryptoJS.AES.encrypt(e.target.result, CryptoJS.enc.Utf8.parse(key), {
                iv: CryptoJS.enc.Utf8.parse(iv),
                mode: CryptoJS.mode.CBC,
                padding: CryptoJS.pad.Pkcs7
            });

            output.textContent = encrypted;

            let a = document.createElement("a");
            a.setAttribute('href', 'data:application/octet-stream,' + encrypted);
            a.setAttribute('download', file.name + '.encrypted');
            a.click();
        };

        reader.readAsDataURL(file);
    }

    function decryptFile(file) {
        let reader = new FileReader();
        reader.onload = function (e) {
            let decrypted = CryptoJS.AES.decrypt(e.target.result, CryptoJS.enc.Utf8.parse(key), {
                iv: CryptoJS.enc.Utf8.parse(iv),
                mode: CryptoJS.mode.CBC,
                padding: CryptoJS.pad.Pkcs7
            });

             // Decrypted is emtpy    
            output.textContent = decrypted;

            // Desperate try to get something working
            example.src = "data:image/png;base64," + btoa(decrypted);

            let a = document.createElement("a");
            a.setAttribute('href', decrypted);
            a.setAttribute('download', file.name.replace('encrypted', 'decrypted'));
            a.click();
        };

        reader.readAsText(file);
    }

    function useEncryptionForFile() {
        document.getElementById("encrypt-file").style.backgroundColor = "#757575";
        document.getElementById("decrypt-file").style.backgroundColor = "#FFFFFF";
        useEncryption = true;
      useDecryption = false;
    }

    function useDecryptionForFile() {
        document.getElementById("encrypt-file").style.backgroundColor = "#FFFFFF";
        document.getElementById("decrypt-file").style.backgroundColor = "#757575";
        useDecryption = true;
      useEncryption = false;
    }
</script>    

我还制作了一个Fiddle,以防你想要更:),Java源代码可以在这里下载

在Java源代码中,我使用仙人掌.jpg作为文件,但任何文件都可以:)使用。仙人掌可以在这里找到。

如何解密已在Java中加密的文件?我尝试将blob内容转换为String,以ArrayBuffer的形式检索数据并将其转换为String,将其作为文本接收并将其传递给解密方法,但似乎没有任何效果。

我在Javascript中使用的库是CryptoJS,在Java中是标准的Crypto库。

我发现了其他类似的(12)问题。然而,我认为它们之间的差异太大,因为这些问题的答案与这个问题无关,而是一个小错误。

如果我忘记了任何数据,请告诉我。


答案 1

问题是您将解密结果解释为 UTF8 字符串。这不是它的工作方式。文件只是任意字节,它们不一定构成 UTF8 字符串。解密的结果是正确的,如果你只是不尝试将其解释为UTF8。


答案 2

首先,尝试将简单的加密文本从java发送到javascript,反之亦然,并测试代码是否正常工作。

如果代码适用于简单文本,即,您可以从Java发送加密的字符串并以JavaScript成功解密它,反之亦然,那么您可以做的是Base64编码加密的字节/文件,然后传输文本,然后在另一端对其进行解码和解密。

如果代码不适用于简单文本,那么您可以尝试在javascript和java中独立加密简单文本,并检查结果是否相同。如果不是,则java和javascript之间的加密/解密逻辑存在一些不匹配。

编辑:

正如您提到的代码适用于String,我在下面展示了一个示例,使用java中的apache通用编解码器库将文件转换为Base64 String。

private static String encodeFileToBase64Binary(String fileName) throws IOException {
    File file = new File(fileName);
    byte[] encoded = Base64.encodeBase64(FileUtils.readFileToByteArray(file));
    return new String(encoded, StandardCharsets.US_ASCII);
}

现在你加密这个字符串并将其发送到javascript。在javascript中,首先解密String,然后将其转换为文件对象。

例如。

 function base64toFile(encodedstring,filename,mimeType){
   return new File([encodedstring.arrayBuffer()],filename, {type:mimeType});

}   

//Usage example:
base64toFile('aGVsbG8gd29ybGQ=', 'hello.txt', 'text/plain');