Encryption of files with CryptoJs and decryption in Python (AES CBC) - javascript

I'm trying to encrypt files (trying with png) in Javascript (ReactJs) and I want to be able to decrypt the files and restore their original state in Python.
I've tried multiple approaches but the below JS code was the only one I got working properly. With the JS code, I'm able to get back the png in its original form. However, I seem to be missing something with padding as I keep getting:
Traceback (most recent call last):
File "***/encrypt_decrypt_using_aes.py", line 110, in <module>
decrypted = decrypt(encData, key, iv)
File "***/encrypt_decrypt_using_aes.py", line 103, in decrypt
return unpad(cipher.decrypt(enc),16)
File "/usr/local/lib/python3.9/site-packages/Crypto/Util/Padding.py", line 90, in unpad
raise ValueError("Padding is incorrect.")
ValueError: Padding is incorrect.
I tried playing with ZeroPadding, NoPadding, PKCS7 but nothing worked.
Any ideas on what I am missing?
JS code:
function decrypt(input) {
var file = input;
var reader = new FileReader();
reader.onload = () => {
var key = "1234567887654321";
var decrypted = CryptoJS.AES.decrypt(reader.result, key, {mode: CryptoJS.mode.CBC}); // Decryption: I: Base64 encoded string (OpenSSL-format) -> O: WordArray
var typedArray = convertWordArrayToUint8Array(decrypted); // Convert: WordArray -> typed array
var fileDec = new Blob([typedArray]); // Create blob from typed array
var a = document.createElement("a");
var url = window.URL.createObjectURL(fileDec);
var filename = file.name.substr(0, file.name.length - 4);
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
};
reader.readAsText(file);
}
function encrypt(input) {
var file = input
console.log("In Encrypt")
console.log(file)
var reader = new FileReader();
var iv = CryptoJS.enc.Utf8.parse('53509bee-8207-11');
reader.onload = () => {
var key = "1234567887654321";
var wordArray = CryptoJS.lib.WordArray.create(reader.result); // Convert: ArrayBuffer -> WordArray
var encrypted = CryptoJS.AES.encrypt(wordArray, key, {iv: iv, mode: CryptoJS.mode.CBC}).toString(); // Encryption: I: WordArray -> O: -> Base64 encoded string (OpenSSL-format)
var fileEnc = new Blob([encrypted]); // Create blob from string
// var a = document.createElement("a");
// var url = window.URL.createObjectURL(fileEnc);
var filename = input.name + ".enc";
console.log(filename)
var file = new File([fileEnc], filename);
// a.href = url;
// a.download = filename;
// a.click();
// window.URL.revokeObjectURL(url);
setFiles2State(files2State => ({
fileList2: [...files2State.fileList2, file],
}));
console.log("OUT LIST")
console.log(files2State)
};
reader.readAsArrayBuffer(file);
}
Python code (Not working):
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad
def encrypt(data,key,iv):
data= pad(data.encode(),16)
cipher = AES.new(key.encode('utf-8'),AES.MODE_CBC,iv)
return base64.b64encode(cipher.encrypt(data))
def decrypt(enc,key,iv=None):
enc = base64.b64decode(enc)
# enc = enc.encode('utf-8')
if iv == None:
cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC)
else:
cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv)
return unpad(cipher.decrypt(enc),16)
with open("demo_file.png.enc", "r+") as fileIn:
encData = fileIn.read()
key = "1234567887654321"
iv = "53509bee-8207-11".encode('utf-8')
decrypted = decrypt(encData, key, iv)
with open("demo_file.png", "wb") as fileOut:
fileOut.write(decrypted)

Related

How to download base64 which is encrypted? and Decrypt after that

I want to make image ecrypt and decrypt in client browser.
I have create upload image an encrypt base64. But, i don't know how to download this encrypted image (On format image not text).
after that i want to upload this encrypted image for decrypt.
Thanks for advance, i'm sorry for my bad english.
$(document).ready(function() {
$("#form-encrypt").on('submit', function(e){
e.preventDefault();
disabledButton('btn-submit-encrypt');
let filesSelected = document.getElementById("inputFileToLoad").files;
let fileExtension = filesSelected[0].name.split('.').pop()
const password = $('#password').val();
if (filesSelected.length > 0) {
let fileToLoad = filesSelected[0];
let fileReader = new FileReader();
fileReader.onload = function(fileLoadedEvent) {
let base64value = fileLoadedEvent.target.result;
let encrypt = CryptoJS.AES.encrypt(base64value, password).toString();
};
fileReader.readAsDataURL(fileToLoad);
}
});
$("#form-decrypt").on('submit', function(e){
e.preventDefault();
const ciphertext = document.querySelector('#encrypt-text').value;
const pass = document.querySelector('#password-decrypt').value;
try {
let bytes = CryptoJS.AES.decrypt(ciphertext, pass);
const originalText = bytes.toString(CryptoJS.enc.Utf8);
const preview = document.getElementById('preview');
preview.setAttribute('src', originalText);
$("#preview").show();
} catch (error) {
alert('Wrong Password, or Encrypt Text Not Right');
}
});
});
function downloadTxt(filename, data, mimeType) {
let element = document.createElement('a');
element.setAttribute('download', filename);
mimeType = mimeType || 'text/plain';
element.setAttribute('href', 'data:' + mimeType + ';charset=utf-8,' + [enter image description here][1]encodeURIComponent(data));
element.click();
}
function disabledButton(elm) {
const element = document.getElementById(elm);
element.setAttribute('disabled', true);
element.innerHTML = 'Loading..';
}
yeah, i already know.
I wrong because i encrypt all result in fileReader, you must split a base64 value.
example :
data:image/png;base64,iVBOblablabla
split with ( , ) and you get array
data[0] = data:image/png;base64
data[1] = iVBOblablabla
and you should just encrypt data[1]

Is there a workaround to create a custom download link for safari 14?

The code above creates a temporal element <a> and download a file.
function downloadFile(file) {
var a = document.createElement("a");
a.href = file.FileData;
a.download = file.FileName;
a.click();
}
It works in Safari 13 but it stopped working in Safari 14 (tested in 14.0.1).
It seems the browser try to open a new tab with the file, a pdf in my case, but it gets prevented.
Not allowed to navigate top frame to data URL
'data:application/pdf;base64,JVBERi0xLjU...'
Did you find any workaround for this issue?
SOLUTION
Generating a Bob file instead solved my issue.
public async downloadFile() : Promise<void> {
const aux: string = 'data:' + file.ContentType + ';base64,';
let arraybuffer = this._base64ToArrayBuffer(file.FileData.substr(aux.length));
// create the blob object with content-type "application/XXX"
let blob = new Blob( [arraybuffer], { type: file.ContentType });
let url = URL.createObjectURL(blob);
let a = document.createElement("a");
a.href = url;
a.download = file.FileName;
a.click();
}
}
}
/**
* It transform a string in base64 to an ArrayBuffer.
* #param base64 string
*/
public _base64ToArrayBuffer(base64: string) : ArrayBuffer {
// decode base64 string, remove space for IE compatibility
var binary_string = window.atob(base64.replace(/\s/g, ''));
var len = binary_string.length;
var bytes = new Uint8Array(len);
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes.buffer;
}

How to convert base64 byte data into downloadable pdf file?

I have an encoded base64 data from an API response and stored in a variable encodedBase64.
let encodedBase64 = 'some base64 encoded long data';
function base64ToArrayBuffer(base64) {
var binaryString = window.atob(base64);
console.log('binaryString ', binaryString);
var binaryLen = binaryString.length;
var bytes = new Uint8Array(binaryLen);
for (var i = 0; i < binaryLen; i++) {
var ascii = binaryString.charCodeAt(i);
bytes[i] = ascii;
}
return bytes;
}
function saveByteArray(reportName, byte) {
var blob = new Blob([byte], {type: "application/pdf"});
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
var fileName = reportName;
link.download = fileName;
link.click();
};
var sampleArr = base64ToArrayBuffer(encodedBase64);
saveByteArray("Sample Report", sampleArr);
after executing this code i am able to download pdf file names SampleReport.pdf but when i open this it is showing Failed to load PDF document. error
what is the wrong in my code ?
difficult to get it done using front end side
but it can be done easily using Nodejs using the following code.
fs.writeFile('pdfFileName.pdf', base64DataString, {encoding: 'base64'}, error => {
if (error) {
throw error;
} else {
console.log('buffer saved!');
}
});

Read file slice as bytes and send to server?

I want to read a slice of a file using a FileReader, and then send it to a server. This is what I have so far:
const createReader = onRead => {
const reader = new FileReader();
reader.onloadend = function(evt) {
if (evt.target.readyState === FileReader.DONE) {
const arrayBuffer = evt.target.result;
const bytes = new Uint8Array(arrayBuffer);
console.log("BYTES: ", bytes);
onRead(evt.target.result);
}
};
return reader;
};
const reader = createReader(fileSlice => {
console.log("BYTES: ", fileSlice);
// send to server
});
reader.readAsArrayBuffer(blob);
Here's what it printed when I uploaded a simple .txt file:
However, it returns an array, and I'm not sure how to convert this into a format that I can send over to my server through HTTP.
Does anyone know what I need to do in order to convert that byte array into a format that I can eventually turn back into the original file?
You can POST the Uint8Array to the server. You can convert a Uint8Array to a string using TextDecoder
Read the file chunk, make sure you use readAsArrayBuffer
var readChunck = function (offset, length, file) {
var r = new FileReader();
var chunck = file.slice(offset, length + offset);
r.onload = readEventHandler;
r.readAsArrayBuffer(chunck)
}
In the vent handler (readEventHandler), convert the ArrayBuffer to Base64String and post to the server:
var readEventHandler = function (evt) {
var data = {
data: self.arrayBufferToBase64String(evt.target.result)
}
self.httpClient.post(url, data)
.subscribe(data=> {
// do stuff
});
// ...
}
And here is the conversion function:
private arrayBufferToBase64String(buffer: ArrayBuffer) {
let binaryString = ''
var bytes = new Uint8Array(buffer);
for (var i = 0; i < bytes.byteLength; i++) {
binaryString += String.fromCharCode(bytes[i]);
}
return window.btoa(binaryString);
}
On the server side (ASP Core Web API in this case):
[HttpPost, DisableRequestSizeLimit]
[Route("{blobId}/uploads/{uploadId}/parts/{partNumber}")]
public async Task<IActionResult> UploadPart(
[FromRoute] string blobId,
[FromRoute] string uploadId,
[FromRoute] int partNumber,
[FromBody] FilePartModel part)
{
if (!GuidValidator.IsValidGuid(blobId)) throw new ArgumentException("Invalid BLOB Id");
if (!GuidValidator.IsValidGuid(uploadId)) throw new ArgumentException("Invalid Upload Id");
var bytes = Convert.FromBase64String(part.Data);
using (var stream = new MemoryStream(bytes))
{
var etag = await _blobsService.UploadPartAsync(Guid.Parse(blobId), Guid.Parse(uploadId), partNumber, stream);
return Ok(new FilePart { ETag = etag, PartNumber = partNumber });
}
}
FilePartModel is just a simple model class:
public class FilePartModel
{
public string Data { get; set; }
}

Extract uploaded file using adm zip

I am trying to get the list of files in a uploaded file using adm-zip. As we cant get the path of the uploaded file , iam trying to convert the zip into a Buffer and pass it to the adm-zip. But zip.getEntries(); its not giving me the list of files.
checkZipFiles(){
var AdmZip = require("adm-zip");
var filesInput = document.querySelector('input[type="file"]').files[0];
var res;
var zip = new AdmZip(filesInput);
console.log(' zip is '+JSON.stringify(zip));
var zipEntries = zip.getEntries();
console.log(' zipEntries is '+zipEntries);
zipEntries.forEach(function(zipEntry) {
console.log(zipEntry.toString()); // outputs zip entries information
if (zipEntry.entryName == "my_file.txt") {
console.log(zipEntry.data.toString('utf8'));
}
});
var reader = new FileReader();
reader.readAsArrayBuffer(filesInput);
var bytes,buf ;
reader.onloadend = function(e){
var readLen=e.target.result.byteLength;
var arrayBuffer = reader.result
bytes = new Uint8Array(arrayBuffer);
console.log('insidebytes is '+bytes);
buf = new Buffer(e.target.result.byteLength);
for (var i = 0; i < buf.length; ++i) {
buf[i] = bytes[i];
}
console.log(e.target.result.byteLength+' length is '+bytes.byteLength);
var myZip = e.target.result;
console.log('bytes is '+bytes);
console.log('buf is '+buf);
zip = new AdmZip(buf);
// console.log(zip+' reader ');
var zipEntries = zip.getEntries();
console.log(' zip is '+zipEntries);
}
}
When i print the buffer it prints something like below , with the file name inside the zip.
121832 length is 121832
}+����Y���L�]��%:����_����ld� ��c{\��h7���L��e33�\"ԅտ׉��v�˕3�-��^�'�Ҁ霗�^�p�q�W��������vްR�����akny���Egr����G�%1/���Wer����\d���A�upR�L����up�jemF���� ��k9y��^Ն;h�1:ca delete.txt
�-�-F1p[
But AdmZip gives below error. Is the issue with the buffer or AdmZip not able to read the buffer?
Uncaught Invalid END header (bad signature) mainHeader.js?4281:35

Categories

Resources