如何使用nodejs解密cookie

时间:2016-01-19 17:00:48

标签: node.js cookies encryption machinekey

我正在努力制作run this

function hex2a(hex) {
    var str = '';
    for (var i = 0; i < hex.length; i += 2)
        str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
    return str;
}

//Raw cookie
var cookie = "B417B464CA63FE780584563D2DA4709B03F6195189044C26A29770F3203881DD90B1428139088D945CF6807CA408F201DABBADD59CE1D740F853A894692273F1CA83EC3F26493744E3D25D720374E03393F71E21BE2D96B6110CB7AC12E44447FFBD810D3D57FBACA8DF5249EB503C3DFD255692409F084650EFED205388DD8C08BF7B941E1AC1B3B70B9A8E09118D756BEAFF25834E72357FD40E80E76458091224FAE8";

//decryptionKey from issuers <machineKey>
var deckey = "FFA87B82D4A1BEAA15C06F6434A7EB2251976A838784E134900E6629B9F954B7";


var crypto = require('crypto');

var ivc = cookie, iv, cipherText, ivSize = 16, res = "";

ivc = new Buffer(ivc, 'hex');
iv = new Buffer(ivSize);
cipherText = new Buffer(ivc.length - ivSize);
ivc.copy(iv, 0, 0, ivSize);
ivc.copy(cipherText, 0, ivSize);

c = crypto.createDecipheriv('aes-256-cbc', hex2a(deckey), iv.toString('binary'));
res = c.update(cipherText, "binary", "utf8");
res += c.final('utf8');


console.log(res);

在这个Q&A中,它提到了有关节点js版本的差异,我试过应用那个,但没有成功:

res = c.update(cipherText, "binary", "utf8");

行结果这样的结果

�sJ舸=�X7D������G����}x���T

res += c.final('utf8'); 

发出此错误

0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length

nodejs version:4.1.2和crypto version 0.0.3

如何使用此算法正确解密cookie,或者您可以推荐任何其他?

1 个答案:

答案 0 :(得分:4)

[假设您正在尝试解密.NET框架cookie]:

(注意:这个答案完全重写了,因为事情并不像看起来那么简单)

加密模式描述为here,引用了有趣的部分:

  

验证+解密数据(fEncrypt = false,signData = true)

     
      
  • 输入:buf表示要解密的密文,修饰符表示要从明文末尾删除的数据(因为它不是真正的纯文本数据)
  •   
  • 输入(buf):E(iv + m +修饰符)+ HMAC(E(iv + m +修饰符))
  •   
  • 输出:m
  •   
           
      
  • 以上描述中的'iv'不是实际的IV。相反,如果ivType =&gt; IVType.Random,我们将随机字节('iv')添加到明文,然后再将其提供给加密算法。在算法的早期引入随机性可以防止用户检查两个密文以查看明文是否相关。如果ivType = IVType.None,则'iv'只是一个空字符串。如果ivType = IVType.Hash,我们使用明文的非键控哈希。

  •   
  • 以上描述中的“修饰符”是一段元数据,应与明文一起加密,但实际上并不是明文本身的一部分。它可用于存储诸如生成此明文的用户名,生成明文的页面等内容。在解密时,将修饰符参数与存储在加密流中的修饰符进行比较,并将其从返回纯文本之前的消息。

  •   

使用以下脚本实现(希望):

// Input
var cookie = "B417B464CA63FE780584563D2DA4709B03F6195189044C26A29770F3203881DD90B1428139088D945CF6807CA408F201DABBADD59CE1D740F853A894692273F1CA83EC3F26493744E3D25D720374E03393F71E21BE2D96B6110CB7AC12E44447FFBD810D3D57FBACA8DF5249EB503C3DFD255692409F084650EFED205388DD8C08BF7B941E1AC1B3B70B9A8E09118D756BEAFF25834E72357FD40E80E76458091224FAE8";
var decryptionKey = "FFA87B82D4A1BEAA15C06F6434A7EB2251976A838784E134900E6629B9F954B7";
var validationKey = "A5326FFC9D3B74527AECE124D0B7BE5D85D58AFB12AAB3D76319B27EE57608A5A7BCAB5E34C7F1305ECE5AC78DB1FFEC0A9435C316884AB4C83D2008B533CFD9";

// Parameters
var hmacSize=20

// Make buffers for input
var cookieBuffer = new Buffer(cookie, 'hex');
var decryptionKeyBuffer = new Buffer(decryptionKey, 'hex');
var validationKeyBuffer = new Buffer(validationKey, 'hex');

// Parse cookie
var curOffset=0;
var cipherText = new Buffer(cookieBuffer.length - hmacSize);
curOffset+=cookieBuffer.copy(cipherText, 0, curOffset, curOffset+cipherText.length);
var hmac = new Buffer(hmacSize);
curOffset+=cookieBuffer.copy(hmac, 0, curOffset, curOffset+hmac.length);

// Verify HMAC
var crypto = require('crypto');
var h = crypto.createHmac('sha1', validationKeyBuffer);
h.update(cipherText);
var expectedHmac = h.digest();
console.log('Expected HMAC: ' + expectedHmac.toString('hex'));
console.log('Actual   HMAC: ' + hmac.toString('hex'));
//if(!expectedHmac.equals(hmac)) { // Note: Requires nodejs v0.11.13
//    throw 'Cookie integrity error';
//}

// Decrypt
var zeroIv = new Buffer("00000000000000000000000000000000", 'hex');
var c = crypto.createDecipheriv('aes-256-cbc', decryptionKeyBuffer, zeroIv);
var plaintext = Buffer.concat([c.update(cipherText), c.final()]);

// Strip IV (which is the same length as decryption key -- see notes below)
var res = new Buffer(plaintext.length-decryptionKeyBuffer.length);
plaintext.copy(res, 0, decryptionKeyBuffer.length, plaintext.length);

// Output
console.log('HEX: ' + res.toString('hex'));
console.log('UTF-8: ' + res.toString('utf8'));

给出结果:

Expected HMAC: 88e332b9a27b8f6f8d805ae718c562c1c8b721ed
Actual   HMAC: 6beaff25834e72357fd40e80e76458091224fae8
HEX: 010112ea9a47b2f2ce08fe121e7d78b6f2ce0801085400650073007400550073006500720016540065007300740020007400650073007400730073006f006e002c00200072006f006c0066007a006f007200012f00ff1d892908d9c497bd804f5f22eab043ff6368702c
UTF-8: ��G���}x�TestUserTest testsson, rolfzor/���ė��O_"��C�chp,

关于此代码的一些(随机)说明:

  • 它假定AES用于加密,HMAC-SHA1用于身份验证

  • 由于使用的身份验证密钥未知,因此注释了完整性检查条件,并使用了this very related question的验证密钥(这是身份验证标记不匹配的原因)

  • 用于AES加密的填充是PKCS#7

  • 'modifier'字段假定为空。如果不是这种情况,则必须检查并将其从明文中删除

  • 对于生产环境,你肯定应该检查身份验证标记(否则你会暴露自己讨厌的攻击)

  • 为了避免更严重的攻击,应该在恒定时间内测试身份验证标记是否相等(在nodejs中实现可能很棘手)。请注意,注释掉的代码很可能容易受到时间攻击。

  • IV长度等于密钥长度(原因见here

免责声明:我没有彻底研究原始的.NET代码,也不是加密专家,所以请确认我的想法

祝你好运!