离线使用的 AES 加密选项

AES encryption option for offline usage

我的objective是在移动应用程序中对某些数据(例如个人信息等)在本地执行数据encryption/decryption,而不是密码。偶然发现了这个library,现在考虑我可以有的两个选择

选项 1 使用用户密码作为 secret passphrase,而不是硬编码 passphrase,加密和解密密钥是“自定义的”

var encrypted = CryptoJS.AES.encrypt("Message", "Secret Passphrase");
​
var decrypted = CryptoJS.AES.decrypt(encrypted, "Secret Passphrase");

选项 2 根据用户密码生成密钥,如下所示

const CryptoJS = require("crypto-js");
const salt = CryptoJS.lib.WordArray.random(128 / 8);
const key = CryptoJS.PBKDF2("password", salt, {
  keySize: 512 / 32,
  iterations: 10000,
});

var encrypted = CryptoJS.AES.encrypt("Message", key.toString());
var decrypted = CryptoJS.AES.decrypt(encrypted, key.toString());
console.log(decrypted.toString(CryptoJS.enc.Utf8));

鉴于我的用例,我想知道一个选项是否比另一个选项有任何优势?

您永远不应该对密码短语进行硬编码,它会将密码学转化为编码。

最好使用散列而不是标准的人类可读文本字符串,尤其是因为它添加了我认为是迭代散列的内容,这会增加抵御暴力攻击的工作量。 1000 次迭代会增加相当多的时间,10k 甚至更多,但所有消耗 CPU 周期的东西都需要与性能相平衡。

还请确保在显式设置 reading/writing 数据时使用的任何编码(如 Utf8),以免混用和匹配类型,否则会导致一些问题。

这两个选项都不推荐:

  • 绝对不应使用第一个选项,除非出于兼容性原因强制这样做。

    主要原因是内置密钥派生应用了已弃用的密钥派生函数EVP_BytesToKey(),它派生了一个 32 字节的密钥 (AES-256) 和一个 16 字节的 IV。
    EVP_BytesToKey() 和用于密钥派生的参数(MD5,迭代计数为 1)在今天被认为是不安全的,s。例如here.
    此外,正如评论中已经提到的,EVP_BytesToKey() 是 OpenSSL 的专有和非标准实现,因此在许多平台上不可用,这在跨平台架构中可能是一个问题。

    显然更安全的替代方法是应用标准化密钥派生函数(例如 PBKDF2)来派生密钥,然后用于 encryption/decryption。

  • 虽然第二个选项使用 PBKDF2 作为密钥派生函数,但是使用 key.toString() 对密钥执行十六进制编码,即将密钥转换为字符串,具有深远的影响:

    CryptoJS 只是使用数据类型将第二个参数解释为密码或密钥。在字符串的情况下,数据被解释为密码,并且使用 EVP_BytesTokey() 执行密钥推导(对于第一个选项),在 WordArray 的情况下,数据被解释为密钥 直接应用。

    当前的实现将密钥作为字符串传递,即使用 PBKDF2 派生的密钥被解释为密码,并且不必要地执行使用 EVP_BytesTokey() 的内置密钥派生 此外 到 PBKDF2 的密钥派生。

正确做法:

更改第二个选项,使密钥作为 WordArray 传递,即 key 而不是 key.toString()
此更改的结果是 salt 和 IV 必须 显式处理 (与使用 EVP_BytesToKey() 的内置密钥派生相反,这种情况发生在幕后):

  • 当前代码已经生成随机盐,完全正确。同样,必须生成随机 IV(CryptoJS 默认使用 CBC)并传递(s. here)。
    作为使用 random() 显式生成的替代方法,IV 可以通过 PBKDF2.
  • 与密钥一起导出
  • 由于解密需要salt和IV,不是secret,所以通常两者都与密文串联:salt|IV|ciphertext(如果IV和key一起导出,当然只需要salt串联)。在解密端,可以根据已知的salt和IV长度将部分分开。

关于评论中提到的密钥大小:今天所有 AES 变体都被认为是安全的,即使是最小的密钥大小 AES-128。
应使用 AES-128、AES-192 还是 AES-256 取决于各自的要求。如果需要尽可能高的安全性,例如对于未来的量子计算机,AES-256 可能是更好的选择。但是,此要求可能并不总是存在。参见例如还有 post Why most people use 256 bit encryption instead of 128 bit?.