I am trying to decrypt a file that was created using the OpenSSL command-line interface. This file was created with:
openssl aes-256-cbc -a -in file.txt -out file_encrypted.txt
And can be decrypted with:
openssl aes-256-cbc -d -a -in file_encrypted.txt
By using the -p
flag I can retrieve the actual value, salt and IV which will be required by the WebCrypto API:
> openssl aes-256-cbc -d -a -p -in file_encrypted.txt
salt=F57F1CC0CD384326
key=0E971326890959386F1CFB91F185CFE109203DCEBC81DCAD4EE642F34C538E5B
iv=A884549B66400EB198879F8A09148D4E
secret text
My current attempt looks like this:
function getKey (password) {
return crypto.subtle.digest({name: "SHA-256"}, convertStringToArrayBufferView(password)).then(function(result){
return crypto.subtle.importKey("raw", result, {name: "AES-CBC"}, false, ["encrypt", "decrypt"]);
});
}
function decrypt(key, data, iv) {
return crypto.subtle.decrypt({ name: "AES-CBC", iv: iv }, key, data).then(function(result){
var decrypted_data = new Uint8Array(result);
return convertArrayBufferViewtoString(decrypted_data);
}, fail);
}
var encrypted = Uint8Array.from('0E971326890959386F1CFB91F185CFE109203DCEBC81DCAD4EE642F34C538E5B'.match(/\w\w/g));
var IV = Uint8Array.from('A884549B66400EB198879F8A09148D4E'.match(/\w\w/g));
getKey(prompt('Enter decryption password:')).then(function (key) {
decrypt(key, encrypted, IV).then(result => {
console.log(`password: ${result}`)
});
}, fail);
(array-to-buffer methods ommited for brevity - taken from http://qnimate.com/passphrase-based-encryption-using-web-cryptography-api/)
This fails with an unspecified DOMException
though and I have no idea what to do next.
OpenSSL applies a salted key derivation algorithm to your password using some random bytes generated when encrypting and stored in the header of the encrypted file.
In this post is very well explained
OpenSSL uses a salted key derivation algorithm. The salt is a piece of random bytes which are generated when encrypting, and stored in the file header; upon decryption, the salt is retrieved from the header, and the key and IV are recomputed from the provided password and the salt value.
The encryption format used by OpenSSL is non-standard: it is "what OpenSSL does", and if all versions of OpenSSL tend to agree with each other, there is still no reference document which describes this format except OpenSSL source code.
Hence a fixed 16-byte header, beginning with the ASCII encoding of the string "Salted__", followed by the salt itself.
To make your code works is needed:
Load the key generated by OpenSSL (or derive the key from password using the provided salt with the openssl algorithm. The derivation algorithm is undocumented in the openssl encryption page, but in this post is said that is propietary, so it is not available in webcrypto)
decode from HEX to ArrayBuffer using hex2a
and convertStringToArrayBufferView
var IV = convertStringToArrayBufferView (hex2a ('A884549B66400EB198879F8A09148D4E'));
Load the encrypted file: decode from base64 (you used -a
option) and remove the first 16 bytes of the salt
This a simplified javascript example with data generated with the same openssl command
openssl aes-256-cbc -d -a -p -in file_encrypted.txt
enter aes-256-cbc decryption password:
salt=886DBE2C626D6112
key=0DA435C43BE722BB5BF09912E11E3E25BE826C35A674EC4284CD1C49AFBCC78E
iv =7F9608BF748309A2C7DAA63600AB3825
this is the secret value of the fiile
Javascript code
//The content of file_encrypted.txt. It is encoded in base64
var opensslEncryptedData = atob('U2FsdGVkX1+Ibb4sYm1hEp/MYnmmcteeebZ1jdQ8GhzaYlrgDfHFfirVmaR3Yor5C9th02S2wLptpJC6IYKiCg==');
//Encrypted data removing salt and converted to arraybuffer
var encryptedData = convertStringToArrayBufferView(opensslEncryptedData.substr(16,opensslEncryptedData.length););
//key and IV. salt would be needed to derive key from password
var IV = convertStringToArrayBufferView (hex2a ('7F9608BF748309A2C7DAA63600AB3825'));
var key = convertStringToArrayBufferView (hex2a ('0DA435C43BE722BB5BF09912E11E3E25BE826C35A674EC4284CD1C49AFBCC78E'));
//var salt = convertStringToArrayBufferView (hex2a ('886DBE2C626D6112'));
crypto.subtle.importKey("raw", key, {name: "AES-CBC"}, false, ["encrypt", "decrypt"]). then (function (cryptokey){
return crypto.subtle.decrypt({ name: "AES-CBC", iv: IV }, cryptokey, encryptedData).then(function(result){
var decrypted_data = new Uint8Array(result);
var res = convertArrayBufferViewtoString(decrypted_data);
console.log(res);
}).catch (function (err){
console.log(err);
});
}).catch (function (err){
console.log(err);
});
Utility functions
function hex2a(hexx) {
var hex = hexx.toString();//force conversion
var str = '';
for (var i = 0; i < hex.length; i += 2)
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
return str;
}
function convertStringToArrayBufferView(str){
var bytes = new Uint8Array(str.length);
for (var iii = 0; iii < str.length; iii++) {
bytes[iii] = str.charCodeAt(iii);
}
return bytes;
}
function convertArrayBufferViewtoString(buffer){
var str = "";
for (var iii = 0; iii < buffer.byteLength; iii++) {
str += String.fromCharCode(buffer[iii]);
}
return str;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With