使用不正确密钥大小的 AES 加密示例
AES encryption examples that use an incorrect key size
使用 AES 加密时,您的密钥大小需要为 128、192 或 256 位。但是在各种加密网站上,您可以使用任何长度甚至可以是 1 个字符(8 位)的密钥。
例如,在那个网站上,我可以使用任何我想要的密钥,它 encrypt/decrypt 就好了。
这是如何运作的?怎么可能使用长度不正确的密钥?
许多加密工具(和库)允许您提供 'password',它用于派生适当大小的密钥。为了避免歧义,术语 加密密钥 通常用于指代与加密算法一起使用的 N 位密钥。
如果您查看链接页面上的代码,它会计算您提供的密钥的 SHA-1 哈希值,并将前 16 个字节作为 128 位加密密钥。
PHP、OpenSSL(及其他)
许多网站专门使用 PHP mcrypt_encrypt
。 PHP mcrypt 用于接受任何大小的密钥和 IV。发生的事情是算法不支持的密钥被扩展到大于给定密钥字节的第一个可用大小。如果密钥太大,则将其缩减为最大密钥大小。
对于 PHP 这在 5.6.0 中发生了变化:
Invalid key and iv sizes are no longer accepted. mcrypt_encrypt() will now throw a warning and return FALSE if the inputs are invalid. Previously keys and IVs were padded with '[=11=]' bytes to the next valid size.
这可能会破坏很多网站。
请注意,这种 密钥扩展 绝对不是最佳实践,而且不仅会受到密码学专家的反对。相反,应该使用 密钥派生函数 或 KDF。
使用散列函数散列
使用 MD5 或 SHA-1 等加密散列的散列可以用作穷人 KDF。它不提供 PBKDF 所提供的保护(见下文)。如果需要更短的密钥,那么使用(最左边的)字节是相对安全的。如果使用散列,API 或源代码中应该清楚。
这好像是问题中例子中使用的方法
播种随机数生成器
除非从 API 中非常清楚使用了什么算法并且给定种子的 DRBG 没有与以前的种子数据混合(例如通过操作系统),否则不应使用此方法。通常使用 key/password 作为随机数生成器的种子会导致灾难性的失败。应该用一切可能的手段来对抗这种方法。随机数生成器不是 KDF。很遗憾 there are many people following bad examples.
密码加密
相反,对于基于密码的加密 (PBE),应使用 PBKDF(基于密码的密钥派生函数)。 PBKDF 的示例是 PBKDF2、bcrypt 或 scrypt。这通常在 API 中明确显示或在源代码中清晰可见。一个好的 PBKDF 使用盐,可能是胡椒(秘密值)和工作因子或迭代计数。这使得密码——通常确实包含比完整密钥更少的熵——在某种程度上更安全。不过,它无法防止真正的弱密钥。
密钥推导
如果你有一个确实包含足够熵的秘密,那么就不需要盐和工作因子(但是盐可以使你的 KDF 更加安全)。工作因素只会为您的密钥推导增加恒定的时间——如果暴力攻击由于熵的数量已经不可行,工作因素只会减慢目标用户和 CPU。可以说目前最先进的 KBKDF 是 HKDF。找到在密码库中实现的 KDF 可能很棘手。
http://aesencryption.net/ 算法采用字符串形式的密钥并将其重新映射到一个长度为 Rijndael 接受的数组。如果长于 256 位,则密钥被截断为该长度,否则用 '[=11=]'
字节填充,直到达到算法可接受的长度之一,即 128、160、192、224 或 256 位。
我通过获取密钥、将其转换为数组并最终截断/填充它来重现该站点的行为。
您可以使用下面的算法来重现站点的关键转换http://aesencryption.net
public static byte[] transformKey(String inputKey){
int keySize = Math.min(((((sessionKey.length * 8 - 128) / 32) + 1) * 32) + 128, 256) / 8;
sessionKey = Arrays.copyOf(sessionKey, keySize);
for (int i = key.getBytes().length; i < sessionKey.length; i++) {
sessionKey[i] = '[=10=]';
}
return sessionKey;
}
注意:for
循环没有用,因为 Arrays.copyOf
已经用零填充了数组。
使用 AES 加密时,您的密钥大小需要为 128、192 或 256 位。但是在各种加密网站上,您可以使用任何长度甚至可以是 1 个字符(8 位)的密钥。
例如,在那个网站上,我可以使用任何我想要的密钥,它 encrypt/decrypt 就好了。
这是如何运作的?怎么可能使用长度不正确的密钥?
许多加密工具(和库)允许您提供 'password',它用于派生适当大小的密钥。为了避免歧义,术语 加密密钥 通常用于指代与加密算法一起使用的 N 位密钥。
如果您查看链接页面上的代码,它会计算您提供的密钥的 SHA-1 哈希值,并将前 16 个字节作为 128 位加密密钥。
PHP、OpenSSL(及其他)
许多网站专门使用 PHP mcrypt_encrypt
。 PHP mcrypt 用于接受任何大小的密钥和 IV。发生的事情是算法不支持的密钥被扩展到大于给定密钥字节的第一个可用大小。如果密钥太大,则将其缩减为最大密钥大小。
对于 PHP 这在 5.6.0 中发生了变化:
Invalid key and iv sizes are no longer accepted. mcrypt_encrypt() will now throw a warning and return FALSE if the inputs are invalid. Previously keys and IVs were padded with '[=11=]' bytes to the next valid size.
这可能会破坏很多网站。
请注意,这种 密钥扩展 绝对不是最佳实践,而且不仅会受到密码学专家的反对。相反,应该使用 密钥派生函数 或 KDF。
使用散列函数散列
使用 MD5 或 SHA-1 等加密散列的散列可以用作穷人 KDF。它不提供 PBKDF 所提供的保护(见下文)。如果需要更短的密钥,那么使用(最左边的)字节是相对安全的。如果使用散列,API 或源代码中应该清楚。
这好像是问题中例子中使用的方法
播种随机数生成器
除非从 API 中非常清楚使用了什么算法并且给定种子的 DRBG 没有与以前的种子数据混合(例如通过操作系统),否则不应使用此方法。通常使用 key/password 作为随机数生成器的种子会导致灾难性的失败。应该用一切可能的手段来对抗这种方法。随机数生成器不是 KDF。很遗憾 there are many people following bad examples.
密码加密
相反,对于基于密码的加密 (PBE),应使用 PBKDF(基于密码的密钥派生函数)。 PBKDF 的示例是 PBKDF2、bcrypt 或 scrypt。这通常在 API 中明确显示或在源代码中清晰可见。一个好的 PBKDF 使用盐,可能是胡椒(秘密值)和工作因子或迭代计数。这使得密码——通常确实包含比完整密钥更少的熵——在某种程度上更安全。不过,它无法防止真正的弱密钥。
密钥推导
如果你有一个确实包含足够熵的秘密,那么就不需要盐和工作因子(但是盐可以使你的 KDF 更加安全)。工作因素只会为您的密钥推导增加恒定的时间——如果暴力攻击由于熵的数量已经不可行,工作因素只会减慢目标用户和 CPU。可以说目前最先进的 KBKDF 是 HKDF。找到在密码库中实现的 KDF 可能很棘手。
http://aesencryption.net/ 算法采用字符串形式的密钥并将其重新映射到一个长度为 Rijndael 接受的数组。如果长于 256 位,则密钥被截断为该长度,否则用 '[=11=]'
字节填充,直到达到算法可接受的长度之一,即 128、160、192、224 或 256 位。
我通过获取密钥、将其转换为数组并最终截断/填充它来重现该站点的行为。
您可以使用下面的算法来重现站点的关键转换http://aesencryption.net
public static byte[] transformKey(String inputKey){
int keySize = Math.min(((((sessionKey.length * 8 - 128) / 32) + 1) * 32) + 128, 256) / 8;
sessionKey = Arrays.copyOf(sessionKey, keySize);
for (int i = key.getBytes().length; i < sessionKey.length; i++) {
sessionKey[i] = '[=10=]';
}
return sessionKey;
}
注意:for
循环没有用,因为 Arrays.copyOf
已经用零填充了数组。