Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Encryption and Decryption between Java and Javascript won't work

EDIT 1

In the decryptFile method, the decrypt part will not output anything..

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
});


EDIT 2 The link that has been given in the comments section partially solves the problem. It does encrypt and decrypt cross platform, but it is rather slow due to PBKDF2 with SHA256 hashing. I cannot find a way to only use the AES part and not the PKBDF2 part.


ORIGINAL TEXT

I am using the same key and IV for both the Java and Javascript version. I cannot decrypt a file in Javascript that has been encrypted in Java, nor can I decrypt a file in Java that has been encrypted in Javascript. I need those two to be compatible with eachother, but I cannot figure out how I am trying to decrypt a file in Javascript that was previously encrypted in Java. I have succesfully implemented decrypting and encrypting text between the two, but when I would like to for example decrypt a file that is encrypted in Java, it just won't work.

Encrypt / decrypt a file in 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();
        }
    }
}

Encrypt / decrypt in 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>    

I also made a Fiddle in case you'd like that more :), and the Java source can be downloaded here.

In the Java source I used a cactus.jpg as file, but any file could be used :). The cactus can be found here.

How can I decrypt the file that has been encrypted in Java? I have tried converting the blob contents to String, retrieving the data as ArrayBuffer and convert it to String, receive it as text and pass it to the decryption method, but nothing seems to work.

The library I am using in Javascript is CryptoJS, and in Java the standard Crypto libraries.

I found other similar (1,2) questions. However, I think they differ too much, as the answer of those questions does not concern this issue, but rather a small mistake.

If I've forgotten any data, please tell me.

like image 864
Jason Avatar asked May 20 '18 21:05

Jason


2 Answers

The problem is that you interpret the decryption result as a UTF8 string. That isn't how it works. Files are just arbitrary bytes, they don't necessarily make up a UTF8 string. The result of your decryption is correct if you just don't try to interpret it as UTF8.

like image 50
Luke Joshua Park Avatar answered Oct 02 '22 13:10

Luke Joshua Park


Firstly, try sending simple encrypted text from java to javascript or vice versa and test if the code is working.

If the code is working for Simple text, i.e. , you are able to send a encrypted String from Java and successfully decrypt it in JavaScript or vice versa, then what you can do is Base64 encode the encrypted bytes/file and then transfer the text and then decode and decrypt it on other end.

If the code is not working for simple text, then you can try encrypting a simple text in javascript and java independently and check if the result are same. If not, there is some mismatch in encryption/decryption logic between java and javascript.

EDIT:

As you mentioned that the code is working for String, I have show below an example to convert file into Base64 String using apache common codec library in java.

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);
}

Now you encrypt this String and send it to javascript. In javascript first decrypt the String and then convert it into a file object.

Eg.

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

}   

//Usage example:
base64toFile('aGVsbG8gd29ybGQ=', 'hello.txt', 'text/plain');
like image 22
Shubham Kadlag Avatar answered Oct 02 '22 13:10

Shubham Kadlag