如果在加密之前已知明文的开头,是否存在漏洞?

时间:2016-05-30 16:59:33

标签: node.js encryption

假设这是我使用来自node.js的本地crypto的加密和解密函数。

var algo = 'aes-256-cbc';
var algoSecret = 'mySecret';

var encrypt = function(secret){
    var cipher = require('crypto').createCipher(algo,algoSecret);
    var crypted = cipher.update(secret,'utf8','hex')
    crypted += cipher.final('hex');
    return crypted;
}
var decrypt = function(text){
    var decipher = require('crypto').createDecipher(algo,algoSecret);
    var dec = decipher.update(text,'hex','utf8');
    dec += decipher.final('utf8');
    return dec;
}

我必须加密长度为20的数据。但是,前8个字符总是相同的并且每个人都知道。例如:始终以api-key=开头。包含或删除8个第一个字符是否会影响系统的安全性?

Ex:encrypt('api-key=askjdhaskdhaskd') vs 'api-key=' + encrypt('askjdhaskdhaskd')

1 个答案:

答案 0 :(得分:2)

  

包含或删除8个第一个字符是否会影响系统的安全性?

是的,但只是轻微的。

具有静态IV的CBC模式是确定性的,这意味着仅观察密文的攻击者可以确定之前是否发送了相同的明文前缀。由于已知前8个字节是静态的,因此第一个块的后8个字节的可能性更可能等于另一个API密钥,其不一定完全匹配。这些信息对攻击者是否有用是一个完全不同的问题。

最好总是为CBC模式生成不可预测的(读取:随机)IV,而不是依赖于从密码派生的相同IV。无论密钥大小如何,对于AES,IV总是16个字节或32个十六进制编码字符。

一些示例代码:

var crypto = require('crypto');
var algo = 'aes-256-cbc';
var algoSecret = 'mySecret';
var key = crypto.pbkdf2Sync(algoSecret, 'salt', 1000, 256, 'sha256'); 
// the key can also be stored in Hex in order to prevent PBKDF2 invocation

var encrypt = function(secret){
    var iv = crypto.randomBytes(16);
    var cipher = crypto.createCipheriv(algo, key, iv);
    var crypted = cipher.update(secret,'utf8','hex')
    crypted += cipher.final('hex');
    return iv.toString('hex') + crypted;
}
var decrypt = function(text){
    var iv = new Buffer(text.slice(0, 32), 'hex');
    text = text.slice(32);
    var decipher = crypto.createDecipheriv(algo, key, iv);
    var dec = decipher.update(text,'hex','utf8');
    dec += decipher.final('utf8');
    return dec;
}

这仍然不够,因为根据您的通信架构,此代码可能容易受到padding oracle攻击。您应该使用消息验证代码(MAC)验证密文。一个受欢迎的选择是"encrypt-then-MAC"的HMAC-SHA256。

var crypto = require('crypto');
var algo = 'aes-256-cbc';
var algoSecret = 'mySecret';
var key = crypto.pbkdf2Sync(algoSecret, 'salt', 1000, 512, 'sha512');
var keyMac = key.slice(32);
var keyEnc = key.slice(0, 32);

var encrypt = function(secret){
    var iv = crypto.randomBytes(16);
    var cipher = crypto.createCipheriv(algo, keyEnc, iv);
    var crypted = cipher.update(secret,'utf8','hex')
    crypted += cipher.final('hex');
    var ct = iv.toString('hex') + crypted;

    var hmac = crypto.createHmac('sha256', keyMac);
    hmac.update(ct);
    return ct + hmac.digest('hex');
}
var decrypt = function(text){
    var hmac = crypto.createHmac('sha256', keyMac);
    hmac.update(text.slice(0, -64));
    if (hmac.digest('hex') !== text.slice(-64)) { // TODO: contant-time comparison
        // TODO: do some decoy decryption
        return false;
    }

    var iv = new Buffer(text.slice(0, 32), 'hex');
    text = text.slice(32, -64);
    var decipher = crypto.createDecipheriv(algo, keyEnc, iv);
    var dec = decipher.update(text,'hex','utf8');
    dec += decipher.final('utf8');
    return dec;
}