代码之家  ›  专栏  ›  技术社区  ›  millimoose Tomasz Nurkiewicz

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

  •  0
  • millimoose Tomasz Nurkiewicz  · 技术社区  · 5 年前

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

    0 回复  |  直到 5 年前
        1
  •  5
  •   t.m.adam    5 年前

    密码不应直接用作密钥,但可以用于生成具有KDF的密钥。这是因为一个密钥应该有一定的大小,而且密码很弱——它们只使用有限的字节集,而且通常包含单词。这使得他们容易受到暴力和字典攻击。KDFs不仅生成长而统一的密钥,而且还引入了一个工作因素,使得暴力攻击不切实际。

    createCipheriv() 不修改密钥内容或大小。文档中没有提到这一点,但遵循源代码(从 createCipheriv : source ,到 Cipheriv : source ,到 createCipherWithIV : source ,到 prepareSecretKey : source )我们看到钥匙是按原样使用的。因此,密钥的大小应该是正确的,并且应该具有足够的复杂性。

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

    使用创建密钥 scrypt :

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

    使用创建密钥 pbkdf2 :

    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包提供。

        2
  •  0
  •   bato3    5 年前

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

    密钥扩展不会增加安全性,因为您在开始时仍然拥有相同的短密码。为了您的安全,请立即假设您有一个闯入和应用程序代码泄漏。也就是说,所有标准和自定义算法都是公共的。

    无论如何,您必须使用密码扩展,因为大多数算法都需要具有特定长度的密码。


    早前的回答,有点离题。

    最好的方法是将密码处理成二进制缓冲区(字符串)。

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

    您的编程语言、框架或库无关紧要。

    一些差异是在打包加密消息的阶段。您可以接收一条原始消息,也可以接收一条格式优美的消息,其中您在base64中添加了IV和内容。

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

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

    它们不一定是秘密的:IVs通常只是添加到 未加密的密文消息。

    总而言之:你不需要影响加密过程本身,你需要检查电子所需要的数据。

    例如,我的解密过程(在节点中,来自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 encrypt:

    
        $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,
           ),
    

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