【问题标题】:How to use the Web Crypto API to decrypt a file created with OpenSSL?如何使用 Web Crypto API 解密使用 OpenSSL 创建的文件?
【发布时间】:2017-05-11 15:07:33
【问题描述】:

我正在尝试解密使用 OpenSSL 命令行界面创建的文件。此文件是使用以下内容创建的:

openssl aes-256-cbc -a -in file.txt -out file_encrypted.txt

并且可以通过以下方式解密:

openssl aes-256-cbc -d -a -in file_encrypted.txt

通过使用 -p 标志,我可以检索 WebCrypto API 所需的实际值、盐和 IV:

> openssl aes-256-cbc -d -a -p -in file_encrypted.txt
salt=F57F1CC0CD384326
key=0E971326890959386F1CFB91F185CFE109203DCEBC81DCAD4EE642F34C538E5B
iv=A884549B66400EB198879F8A09148D4E
secret text

我目前的尝试是这样的:

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

(为简洁起见省略了数组到缓冲区的方法 - 取自http://qnimate.com/passphrase-based-encryption-using-web-cryptography-api/

虽然没有指定DOMException,但我不知道下一步该做什么。

【问题讨论】:

  • 好问题。 WebCrypto 准备好使用了吗? WbCrypto Working Group 似乎已经停滞不前了。
  • 自两年前以来,所有现代浏览器桌面和移动设备都或多或少地实现了 WebCrypto。见caniuse.com/#feat=cryptography。我可以看到该小组本月发布了提议的建议。下一步应该是 Key Discovery Api,但在电子邮件组中查看活动我想我可能会死

标签: javascript openssl cryptography webcrypto-api


【解决方案1】:

OpenSSL 使用加密时生成并存储在加密文件标头中的一些随机字节对您的密码应用加盐密钥派生算法。

this post里解释的很好

OpenSSL 使用加盐密钥派生算法。 salt是加密时产生的一段随机字节,存放在文件头中;解密时,从标头中检索盐,并根据提供的密码和盐值重新计算密钥和 IV。

OpenSSL 使用的加密格式是非标准的:它是“OpenSSL 所做的”,如果所有版本的 OpenSSL 都趋于一致,那么除了 OpenSSL 源代码之外,仍然没有描述这种格式的参考文档。

因此有一个固定的 16 字节标头,以字符串“Salted__”的 ASCII 编码开头,然后是盐本身。

需要使您的代码正常工作:

  • 加载 OpenSSL 生成的密钥(或使用提供的 salt 和 openssl 算法从密码中派生密钥。派生算法在 openssl encryption page 中未记录,但在此 post 中是说这是专有的,所以它在 webcrypto 中不可用)

  • 使用 hex2aconvertStringToArrayBufferView 从 HEX 解码到 ArrayBuffer

    var IV = convertStringToArrayBufferView (hex2a ('A884549B66400EB198879F8A09148D4E'));

  • 加载加密文件:从 base64 解码(您使用了-a 选项)并删除 salt 的前 16 个字节

这是一个简化的 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 代码

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

实用功能

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

【讨论】:

    【解决方案2】:

    事实上,使用 CMS EnvelopedData(加密消息)并非易事。为了更容易处理这些复杂的数据,最好使用已经制作和测试过的库。

    目前只有一个这样的库是 PKIjs(以及新的 ES6 版本的 PKIjs is here)。有很多活生生的例子here。对于您的具体问题,有两个示例:

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

    希望它能帮助您以正确的方式使用 WebCrypto,因为它应该是 :)

    【讨论】:

      【解决方案3】:

      为此,我创建了一个小型库。感谢@pedrofb 的回答。

      在您的文档中嵌入WebCrypto.js(缩小版)。

      像这样使用它:

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

      有关更多示例,请参阅https://github.com/etienne-martin/WebCrypto.swift/blob/master/www/index.html

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

      希望这会有所帮助!

      【讨论】:

        猜你喜欢
        • 2022-10-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-14
        相关资源
        最近更新 更多