4

I'm using crypto-js and the following code to encrypt files. It seems to encrypt them and I can see the string in the alert().

I now need to be able to save the encrypted file somewhere on my computer. I run this file locally on my computer.

I can't seem to find any working solution for this scenario!

This is my full code:

<!DOCTYPE html>
<html>
<head>
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
    <title>Get Directory</title>
    <!-- Update your jQuery version??? -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>


      <!--
  https://cdnjs.com/libraries/crypto-js
  -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>


  <!--[if lt IE 9]>
    <script src="https://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
  <![endif]-->

    <script> // type="text/javascript" is unnecessary in html5

    // Short version of doing `$(document).ready(function(){`
    // and safer naming conflicts with $
    jQuery(function($) { 

        $('#file-input').on('change', function() {

            // You can't use the same reader for all the files
            // var reader = new FileReader

            $.each(this.files, function(i, file) {

                // Uses different reader for all files
                var reader = new FileReader

                reader.onload = function() {
                    // reader.result refer to dataUrl
                    // theFile is the blob... CryptoJS wants a string...
                    var encrypted = CryptoJS.AES.encrypt(reader.result, '12334');
                    ecr = encrypted.toString();
                    alert(encrypted);



                }

                reader.readAsDataURL(file)
                $('#thelist').append('FILES: ' + file.name + '<br>')
            })
        })
    });

    </script>
</head>
<body>
    <input type="file" id="file-input">
    <div id="thelist"></div>


    <input type="button" id="button" value="Save" />
</body>
</html>

Can someone please advice on this?

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
David Hope
  • 1,426
  • 4
  • 21
  • 50
  • I think you need a HTML form with a file input, because of security reasons you cannot access the local file system from JS directly. – xander Aug 21 '17 at 11:39
  • @xander, I do have a file input.. i just don't have a form. – David Hope Aug 21 '17 at 11:40
  • You can't save files to a local file system from client-side javascript, not even if you open the html file directly from the file system (which is not a good idea). What you could do is generate a link that, when clicked, downloads a file, or you could build a browser plugin or even standalone executable (for example with Node Webkit) that will give you local file access. – Kokodoko Aug 21 '17 at 11:40

1 Answers1

3

If you do not care about IE9 (or older) you can create a Blob to contain encrypted text:

var blob = new Blob([encrypted], { type: 'text/plain' });

Note that if you do not have plain text you can still easily use a Blob to accommodate an octet stream. From MDN (see also Blob ctor on MDN):

var typedArray = GetTheTypedArraySomehow();
var blob = new Blob([typedArray], {type: 'application/octet-binary'});

Create a link <a> element with embedded data from that blob:

var link = document.createElement('a');
link.download = 'Encrypted document';
link.href = URL.createObjectURL(blob);

Add the link to your document and simulate a user click then remove it:

document.body.appendChild(link);
link.click();
document.body.removeChild(link);

If you have to support IE8 then you have to do it by hand, Wikipedia has some nice examples and there is a standard answer here on SO: Create a file in memory for user to download, not through server.

Adriano Repetti
  • 65,416
  • 20
  • 137
  • 208
  • 1
    Ciphertexts aren't text files. How would you download arbitrary bytes and how would you transform the CryptoJS format of representing binary data to your possible proposed solution? – Artjom B. Aug 21 '17 at 11:43
  • I never used Ciphertexts but, according to OP example, `encrypted` **is** plain text – Adriano Repetti Aug 21 '17 at 11:44
  • That's true, but that does mean that there is a 33% overhead due to Base64 encoding. Also, why answer when there is a perfectly fine duplicate target that you yourself found? – Artjom B. Aug 21 '17 at 11:47
  • Agree, just added a note about that. With `Blob` is still pretty use with binary data (not sure about the format used by Ciphertexts BTW). Answered because I thought it may be useful to add code example instead of final result only. – Adriano Repetti Aug 21 '17 at 11:47
  • Thank you for the answer. The purpose of this exercise is to Encrypt an Audio file (i.e. MP3 player) and then Decrypt it via an Audio Player of some sort so it can be played. any suggestion on this? – David Hope Aug 22 '17 at 15:33
  • Too few information, of course you can encrypt an audio file and build your own player to decode it. – Adriano Repetti Aug 22 '17 at 16:51
  • @AdrianoRepetti, yes but am I in the right direction using my current code combined with yours? Obviously your code saves the encrypted file as a text file.... is that text file good enough to be Decrypted in a player and played? – David Hope Aug 22 '17 at 17:48
  • Of you write your own decoder then you can do what you want with the format. However text consumes space and you'd better go with binary data,see links about blob for more examples on that. Right direction? Maybe but...why a web project for this? – Adriano Repetti Aug 22 '17 at 17:59
  • not really a web project. this is a start of an app. something similar to spotify.. and the audio files can be downloaded on the users device but cannot be shared since they are encrypted. – David Hope Aug 22 '17 at 20:37
  • Hmmmmm I can't really give you an opinion without a concrete example but I think you're doing things on the wrong side.If user uploads files from his own machine then they should go to the server, no need to get a local encoded version of them. If encoding is done server side (because files are stored on the server...) then you do not need to create a Blob client side, just download the binary encrypted file from server. About security...be ready, it will be a VERY hard task.] – Adriano Repetti Aug 23 '17 at 07:21
  • @AdrianoRepetti, i think you were correct. I am indeed doing things wrong. I think the way to do this is to convert the audio files to base64 and then encrypt/decrypt the string and use that. However, base64 is huge in comparison to the original audio file. is there anything can be done to reduce the size of the base64? – David Hope Aug 24 '17 at 07:06
  • @DavidHope why do you need to convert to base64? You can _simply_ upload the unencrypted binary file, store it on the server and - when requested first time - encrypt the binary file and save a copy (or overwrite the unencrypted one). Clients will receive an encrypted binary file (if available for download through a web page you will have something like `` where `12345` is the unique ID of that file). No need to send the base64 encoded version to the client and no need to encrypt/decrypt anything with JavaScript. – Adriano Repetti Aug 24 '17 at 07:22
  • Note that if you want to make the file available for local download then you must also write a desktop application (doing this with JavaScript...well it is really too easy to be hacked). This is the difficult part, your desktop app will need to know the key to decrypt the binary file and this is the weak point in your security (because simply decompiling the app will be enough to get the key/logic and to bypass your security all together). – Adriano Repetti Aug 24 '17 at 07:25
  • @AdrianoRepetti, you totally lost me. Let me give you an exact example of what this app is.. Note, this is not a web app or desktop app. its just a mobile application and its sort of like the spotify... The users can simply 'Stream' MP3 audio files inside the app. they will need an internet connection to for streaming the audio files. But they will also have an option to download the MP3 files on their device and listen to them offline (without any internet connection). But I will need to encrypt the audio files so they cannot be downloaded and shared. they can only be played inside my app. – David Hope Aug 24 '17 at 13:49
  • I understand but...think you want to copy that file (without paying). You download the encrypted version but you know it can be played off-line then then you inspect the JavaScript code. You see the decryption key and et voila. "Protection" bypassed. Search "remove DRM" and you will see what I mean. More it's easy and more you will make a casual cracker happy. – Adriano Repetti Aug 25 '17 at 12:12