使用Node的“ crypto”模块时是否需要使用密钥派生?

时间:2019-03-05 02:07:42

标签: javascript node.js encryption

我需要在NodeJS应用程序中使用用户密码来实现对称加密。使用crypto.createCipheriv()时,我是否需要对密码短语执行某种密钥派生以获得key参数的值,或者仅按原样传递用户的密码短语就足够了,这是由实施来照顾?

2 个答案:

答案 0 :(得分:2)

密码不应直接用作密钥,但可以将其用于产生带有KDF的密钥。这是因为期望密钥具有一定的大小,并且因为密码弱-它们仅使用有限的一组字节,并且通常包含单词。这使得它们容易受到暴力攻击和字典攻击。 KDF不仅产生强密钥,而且还引入了使蛮力攻击不切实际的工作因素。

createCipheriv()不会修改键的内容或大小。文档中未提及,但遵循源代码(从createCipherivsourceCipherivsource,到createCipherWithIV:{{3 }},到prepareSecretKeysource),我们看到密钥是按原样使用的。因此,密钥应该具有正确的大小,并且应该具有足够的复杂性。

Crypto提供了两个基于密码的KDF,即scrypt和PBKDF2。最好使用scrypt,因为它在CPU和内存资源方面非常昂贵,并且可以针对并行处理进行调整,而PBKDF2仅消耗CPU资源。两个KDF都需要加盐,盐应该长而随机。

使用source创建密钥:

const keySize = 16; // for AES-128
const salt = crypto.randomBytes(16);
const key = crypto.scryptSync('password', salt, keySize);

使用scrypt创建密钥:

const keySize = 16; // for AES-128
const salt = crypto.randomBytes(16);
const key = crypto.pbkdf2Sync('password', salt, 10000, keySize, 'sha256');

其中“ sha256”是基础哈希,而10000是建议的最小迭代次数,它确定了工作因子。在scrypt中,常规工作因子是一个可选参数,默认值为16384(2 ^ 14),可以在options['cost']中进行设置,在这里我们还可以设置块大小和并行化。根据操作系统的不同,这些值可以增加很多。每次操作大约需要100毫秒。

最后,Argon2被认为是非常好的KDF,像scrypt一样,可以针对CPU和内存消耗以及并行处理进行调整。尽管Argon2在crypto中不可用,但它是由其他Node.js软件包提供的。

答案 1 :(得分:0)

破解密码的难度更多地取决于所使用的算法,而不是密码的长度。 (省略暴力攻击)

密钥扩展不能提高安全性,因为开始时您仍然具有相同的短密码。为了安全起见,请立即假设您有侵入程序,并且应用程序代码已泄漏。也就是说,所有标准算法和自定义算法都是公开的。

而且您仍然必须使用密码扩展名,因为大多数算法都要求密码具有特定的长度。


一个更早的答案,有点题外话。

TL; DR:最好的方法是将密码处理到二进制缓冲区(字符串)中。

对称加密正是基于这样一个事实,即拥有秘密就可以执行反向操作。 对称表示操作是可逆的。

您的编程语言,框架或库都没关系。

在打包加密邮件的阶段有些差异。在base64中添加IV和内容的地方,您可能会收到原始消息或格式精美的消息。

您还必须以相同方式处理密钥。但这是关于编码big endianlittle endian以及字符编码,例如:utf-8latin2

IV是一项附加保护,旨在为相同的传入消息和机密生成不同的加密消息。但是,正如您在本节的最后一段中所写的那样:

  

它们不必是秘密的:IV通常只是添加到   密文消息未加密。

总结:您无需影响加密过程本身,而需要检查Electron需要哪些数据。

例如我的解密过程(在节点中,来自PHP的数据):

let crypto = {
  key: Buffer.from('secret in hex', 'hex'),
  cipher: 'aes-128-cbc',
  iv_size: 16
}
    let bData = Buffer.from(data.replace(/ /g,'+'), 'base64');
    let iv = bData.slice(0, crypto.iv_size);
    let text = bData.slice(crypto.iv_size);
    var decipher = crypt.createDecipheriv(crypto.cipher, crypto.key, iv);
    decipher.setAutoPadding(false);
    var decrypted = decipher.update(text,'hex','hex');
    decrypted += decipher.final('hex');

对于PHP加密:


    $data = mcrypt_encrypt($this->_cipher, $this->_key, $data, $this->_mode, $iv);

    // Use base64 encoding to convert to a string
    return base64_encode($iv.$data);

//where
array(
        'key'=> urldecode('secret in urlencode'),
        'cipher' => MCRYPT_RIJNDAEL_128,
        'mode'   => MCRYPT_MODE_CBC,
       ),

如您所见,我必须找到在两个平台上实现的加密算法,并提供一种在两个平台上提供相同密钥的方法。