node.js 加密中的密钥或 iv 长度无效
Invalid length key or iv in node.js crypto
我知道这个主题有几个答案,但我对详细信息密钥长度有疑问。
我想用 AES 算法用 CTR 加密数据。加密长度为 256 位。
如果我用密钥长度 256 和 IV 长度 16 加密数据,我得到一个错误 Invalid key length
。我认为 IV 必须是相同的密钥长度。我将 IV 的长度更改为 256,但出现错误 Invalid iv length
。我发现 IV 必须是 16 位长。
只有当我的密钥是 16 位并且我的 IV 是 16 位长时,我的代码才有效。所以我的代码没有用 256 位的长度加密。
我的代码
const crypto = require('crypto');
let key = crypto.randomBytes(16).toString('hex'); // Key is static
let iv = crypto.randomBytes(16);
let cipher = crypto.createCipheriv('aes-256-ctr', process.env.KEY, iv);
let encrypte = cipher.update("Example_data", 'utf-8', 'hex');
encrypte += cipher.final('hex');
如何使用256位密钥长度加密数据?
首先:AES使用一个16字节的IV(对应AES块大小)和一个16、24或32字节的密钥。在发布的代码中指定了 aes-256-ctr
,因此需要一个 256 位 = 32 字节的密钥。
这正是所应用密钥的大小,而不是您假设的 16 个字节,这就是首先执行代码的原因(至少如果未定义的 process.env.KEY
被替换为 key
): crypto.randomBytes(16)
创建一个 16 字节的缓冲区并 toString('hex')
将其转换为十六进制字符串, 将大小加倍 为 32 字节(因为 1 字节在buffer 在字符串中由 2 个字符(因此 2 个字节)表示)。所以从技术上讲,使用 UTF-8 编码(crypto.createCipheriv()
的默认编码),这会产生一个 32 字节的密钥,可用于 aes-256-ctr
。但是这个key中每个字节只能取16个值(对应数字0-9和a-f),对应1632 = 25616 = 2128 可能的值,因此不是 256,而是只有 128 位安全强度。
您通过直接密钥的二进制数据来防止安全强度的降低,因此crypto.randomBytes(32)
。这里每个字节可以取 256 个不同的值,对于 32 字节的密钥对应于 25632 = 2256 可能的值,因此 256 位安全力量。如果密钥是十六进制编码,则一定不是UTF8编码,而是十六进制解码。
如果密钥是导出的,例如使用密钥派生函数,十六进制编码可能会导致跨平台的另一个问题。如果一个平台将大写字母用于十六进制编码,而将其他小写字母用于十六进制编码,则会产生 不同的 键。
长话短说:避免将十六进制字符串的 UTF8 编码数据用作密钥(或至少了解其中的含义)。
我知道这个主题有几个答案,但我对详细信息密钥长度有疑问。
我想用 AES 算法用 CTR 加密数据。加密长度为 256 位。
如果我用密钥长度 256 和 IV 长度 16 加密数据,我得到一个错误 Invalid key length
。我认为 IV 必须是相同的密钥长度。我将 IV 的长度更改为 256,但出现错误 Invalid iv length
。我发现 IV 必须是 16 位长。
只有当我的密钥是 16 位并且我的 IV 是 16 位长时,我的代码才有效。所以我的代码没有用 256 位的长度加密。
我的代码
const crypto = require('crypto');
let key = crypto.randomBytes(16).toString('hex'); // Key is static
let iv = crypto.randomBytes(16);
let cipher = crypto.createCipheriv('aes-256-ctr', process.env.KEY, iv);
let encrypte = cipher.update("Example_data", 'utf-8', 'hex');
encrypte += cipher.final('hex');
如何使用256位密钥长度加密数据?
首先:AES使用一个16字节的IV(对应AES块大小)和一个16、24或32字节的密钥。在发布的代码中指定了 aes-256-ctr
,因此需要一个 256 位 = 32 字节的密钥。
这正是所应用密钥的大小,而不是您假设的 16 个字节,这就是首先执行代码的原因(至少如果未定义的 process.env.KEY
被替换为 key
): crypto.randomBytes(16)
创建一个 16 字节的缓冲区并 toString('hex')
将其转换为十六进制字符串, 将大小加倍 为 32 字节(因为 1 字节在buffer 在字符串中由 2 个字符(因此 2 个字节)表示)。所以从技术上讲,使用 UTF-8 编码(crypto.createCipheriv()
的默认编码),这会产生一个 32 字节的密钥,可用于 aes-256-ctr
。但是这个key中每个字节只能取16个值(对应数字0-9和a-f),对应1632 = 25616 = 2128 可能的值,因此不是 256,而是只有 128 位安全强度。
您通过直接密钥的二进制数据来防止安全强度的降低,因此crypto.randomBytes(32)
。这里每个字节可以取 256 个不同的值,对于 32 字节的密钥对应于 25632 = 2256 可能的值,因此 256 位安全力量。如果密钥是十六进制编码,则一定不是UTF8编码,而是十六进制解码。
如果密钥是导出的,例如使用密钥派生函数,十六进制编码可能会导致跨平台的另一个问题。如果一个平台将大写字母用于十六进制编码,而将其他小写字母用于十六进制编码,则会产生 不同的 键。
长话短说:避免将十六进制字符串的 UTF8 编码数据用作密钥(或至少了解其中的含义)。