How to use the Web Crypto API to decrypt a file created with OpenSSL?

雨燕双飞 提交于 2019-12-05 10:33:29
pedrofb

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

In fact working with CMS EnvelopedData (encrypted messages) is not a trivial task. And in order to make it easier to work with such complex data always better would be to use already made and tested libraries.

At the moment only one such lib is PKIjs (and new, ES6 version of PKIjs is here). There are a lot of live examples here. For your specific question there are two examples:

  1. How To Encrypt CMS via certificate
  2. How To Encrypt CMS via password

Hope it will help you to use WebCrypto in a correct way, as it should be :)

I've created a small library to do just that. Thanks to @pedrofb for his answer.

Embed WebCrypto.js (Minified) in your document.

Use it like this:

// Initialize the library
initWebCrypto();

var encrypted = "wl2v/1oY7NqV58jpSGkNKmKNu6cdNDz7QCSmKk61k9gyG2Exxh3MxXf9kuSk/ESr6MGNdtQEAhSjHZ9b+Vc4Uw==";
var key = "6f0f1c6f0e56afd327ff07b7b63a2d8ae91ab0a2f0c8cd6889c0fc1d624ac1b8";
var iv = "92c9d2c07a9f2e0a0d20710270047ea2";

// Decrypt your stuff
WebCrypto.decrypt({
    data: encrypted,
    key: key,
    iv: iv,
    callback: function(response){
        if( !response.error ){
            console.log(atob(response.result));
        }else{
            console.error(response.error);
        }
    }
});

See https://github.com/etienne-martin/WebCrypto.swift/blob/master/www/index.html for more examples.

Source code: https://github.com/etienne-martin/WebCrypto.swift/blob/master/source.js

Hope this helps!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!