简体   繁体   English

如何使用Web Crypto API解密使用OpenSSL创建的文件?

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

I am trying to decrypt a file that was created using the OpenSSL command-line interface. 我正在尝试解密使用OpenSSL命令行界面创建的文件。 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: 通过使用-p标志,我可以检索WebCrypto API所需的实际值,salt和IV:

> 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/ ) (为简洁起见,省略了数组到缓冲区的方法-摘自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. 这会失败,但未指定DOMException ,我也不知道下一步该怎么做。

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. OpenSSL使用加密时生成的一些随机字节并将其存储在加密文件的标头中,将盐化密钥派生算法应用于您的密码。

In this post is very well explained 这篇文章中很好的解释了

OpenSSL uses a salted key derivation algorithm. OpenSSL使用盐键派生算法。 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. 解密后,从报头中检索盐,并根据提供的密码和盐值重新计算密钥和IV。

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. OpenSSL所使用的加密格式是非标准的:它是“ OpenSSL所做的事”,并且如果所有版本的OpenSSL都倾向于彼此一致,那么除OpenSSL源代码外,仍然没有参考文档描述此格式。

Hence a fixed 16-byte header, beginning with the ASCII encoding of the string "Salted__", followed by the salt itself. 因此,一个固定的16字节标头以字符串“ Salted__”的ASCII编码开始,然后是salt本身。

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) 加载由OpenSSL的生成密钥 (或使用提供的盐与OpenSSL的算法得出的密码的关键。推导算法在无证的OpenSSL加密页面 ,但在这个帖子中说,这就是propietary,所以它不是在webcrypto可用)

  • decode from HEX to ArrayBuffer using hex2a and convertStringToArrayBufferView 使用hex2aconvertStringToArrayBufferView从HEX解码到ArrayBuffer

    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 加载加密的文件 :从base64解码(您使用了-a选项)并删除了salt的前16个字节

This a simplified javascript example with data generated with the same openssl command 这是一个简化的javascript示例,其数据使用相同的openssl命令生成

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 JavaScript代码

//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. 实际上,使用CMS EnvelopedData(加密消息)并不是一件容易的事。 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 ). 目前只有一个这样的库是PKIjs这里PKIjs的 ES6新版本)。 There are a lot of live examples here . 有很多的活生生的实例在这里 For your specific question there are two examples: 对于您的特定问题,有两个示例:

  1. How To Encrypt CMS via certificate 如何通过证书加密CMS
  2. How To Encrypt CMS via password 如何通过密码加密CMS

Hope it will help you to use WebCrypto in a correct way, as it should be :) 希望它可以帮助您正确使用WebCrypto,因为它应该是:)

I've created a small library to do just that. 我已经创建了一个小型库来执行此操作。 Thanks to @pedrofb for his answer. 感谢@pedrofb的回答。

Embed WebCrypto.js (Minified) in your document. WebCrypto.js (最小化)嵌入到文档中。

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. 有关更多示例,请参见https://github.com/etienne-martin/WebCrypto.swift/blob/master/www/index.html

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

Hope this helps! 希望这可以帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM