把书拼在一起。 在《LDAP 密码加密方式初探》文章中,使用OpenSSL命令AES算法进行加密解密时使用了Key和IV参数,这些参数是如何生成的呢?
还在AES-256-CBC上开始了探索。 首先准备生成Key和IV的passphrase :
$ echo -n “drjom () ) moj rd”pass phrase
上述回文形式的passphrase来自神秘组织:)
将此passphrase传递给openssl命令以生成相应的Key和IV。
$ OpenSSL enc-AES-256-CBC-kfile pass phrase-MDM D5-p-salt
salt=51D9C4B24C759179
key=bb F4 ea0e 7a0EBD7c 60 CCE 2024 e 218 a 53 BBB 69 CCA 65 B4 d 0b 705 e 37080676 e5f5d
iv=8e5e C1 AC 2191167 d F9 b 753 ba 93 a1 e 7b 8
其中的salt是随机生成的,因此每次执行的结果都不同。 另一方面,Key和IV的生成方法参考SuperUser的一个回答进行补充验证如下。 (注意将md5sum的输出结果与上述openssl命令的输出结果进行比较)。
$ perl-e‘打印包“h”、“51d9c4b24c759179”和“salt”
$ catpassphrasesalthash1_ 128.tmp
$ md5sum hash1_128.tmp
BF4ea0e 7a0EBD7c 60 CCE 2024 e 218 a 53 hash1_ 128.tmp
$ perl-e‘打印包“h”、“bb F4 ea0e 7a0EBD7c 60 CCE 2024 e 218 a 53”、“HasH1_128”
$ cat hash1_ 128 passphrasesalthash2_ 128.tmp
$ md5sum hash2_128.tmp
BBB 69 CCA 65 B4 d0b 705 e 37080676 e5f5d hash2_ 128.tmp
$ perl-e‘打印包“h *”,“BBB 69 CCA 65 B4 d0b 705 e 37080676 e5f5d”‘hash2_ 128
$ cat hash2_ 128 passphrasesalthash3_ 128.tmp
$ md5sum hash3_128.tmp
8 e5e C1 AC 2191167 d F9 b 753 ba 93 a1 e 7b8hash3_ 128.tmp
对AES-256-CBC来说:
has h1 _ 128=MD5 (过程加速) )。
hash2_ 128=MD5 (has h1 _ 128 pass phrases alt ) ) ) ) ) ) ) )
hash3_ 128=MD5 (hash2_ 128 pass phrases alt )
Key=hash1_128 hash2_128
IV=hash3_128
Key和IV分别是AES-256-CBC的Key和IV。
即使没有salt,上述过程也成立。 首先,使用-nosalt选项生成openssl命令的Key和IV。
$ OpenSSL enc-AES-256-CBC-kfile pass phrase-MDM D5-p-nosalt
key=D5 e 483 D8 b 90 c 02 BD 470 ba 8049 E1 fa61 d 64 e B2B fa 444 CBF 9853 cdfb8b24 da 7a
iv=304 e 9e 87 db9C1 c 8101 f 605 ed4 dd0 B9 EB
分步验证如下。 请注意md5sum的输出结果与上述openssl命令的输出结果之间的比较。
$ md5sum passphrase
D5 e 483 D8 b 90 c 02 BD 4d 470 ba 8049 E1 fa6pass phrase
$ perl-e‘打印包“h”、“D5 e 483 D8 b 90 c 02 BD 4d 470 ba 8049 E1 fa 6”、“HasH1_128”
$ cat hash1_ 128 pass phrase hash2_ 128.tmp
$ md5sum hash2_128.tmp
1d 64 EB2BFA 444 CBF 9853 cdfb8b24 da 7a hash2_ 128.tmp
$ perl-e‘打印包“h”、“1d 64 EB2BFA 444 CBF 9853 cdfb8b24 da 7a”、“hash2_128”
$ cat hash2_128 passphrase has
h3_128.tmp$ md5sum hash3_128.tmp
304e9e87db9c1c8101f605ed4dd0b9eb hash3_128.tmp
也就是说:
hash1_128 = MD5(Passphrase)
hash2_128 = MD5(hash1_128 + Passphrase)
hash3_128 = MD5(hash2_128 + Passphrase)
Key = hash1_128 + hash2_128
IV = hash3_128
在此基础上,看看 AES-128-CBC 生成的 Key 和 IV 是什么样子的:
$ openssl enc -aes-128-cbc -kfile passphrase -md md5 -P -nosalt
key=D5E483D8B90C02BD4D470BA8049E1FA6
iv =1D64EB2BFA444CBF9853CDFB8B24DA7A
对比 AES-256-CBC 可以看出,AES-128-CBC 的 Key 和 IV 生成方法进一步简化(以下为没有 salt 时的情况):
hash1_128 = MD5(Passphrase)
hash2_128 = MD5(hash1_128 + Passphrase)
Key = hash1_128
IV = hash2_128
在上述验证过程中使用到 openssl 命令时,都用 -md 选项将生成 Key 和 IV 的 hash 函数指定为 md5。那么假如不指定的话,默认的 hash 函数是什么呢?
由此问答可知,从 1.1 版本开始,默认的 hash 函数由 MD5 变为 SHA256(可使用 openssl version 命令查看当前版本号),另外也可以通过修改 /etc/ssl/openssl.cnf 配置文件中的 default_md 字段指定默认的 hash 函数。
要知道,MD5 生成的 hash 是 128bit 的,而 SHA256 生成的 hash 是 256bit 的,上述 256bit Key 生成时的拼接操作是否有必要呢?
继续验证,仍然回到 AES-256-CBC 并使用 SHA256 作为 hash 函数:
$ openssl enc -aes-256-cbc -kfile passphrase -md sha256 -P -nosalt
key=53A8968B0F53CAA2D21F2694B19EDD0676AF034D4D570651B3689C7827EC84C2
iv =ED889267E14BA02167ED96E226153158
分步看看:
$ sha256sum passphrase
53a8968b0f53caa2d21f2694b19edd0676af034d4d570651b3689c7827ec84c2 passphrase
$ perl -e ‘print pack “H*”, “53a8968b0f53caa2d21f2694b19edd0676af034d4d570651b3689c7827ec84c2″‘ > hash1_256
$ cat hash1_256 passphrase > hash2_256.tmp
$ sha256sum hash2_256.tmp
ed889267e14ba02167ed96e226153158373dbeff2b1177c12906ab786dd1ebd8 hash2_256.tmp
可以看到,对 passphrase 做一次 SHA256 运算就已经是 256bit Key 了,对 Key 和 passphrase 拼接后再次做 SHA256 运算,截取前 128bit 作为 IV 的值。也就是说:
hash1_256 = SHA256(Passphrase)
hash2_256 = SHA256(hash1_256 + Passphrase)
Key = hash1_256
IV = First128bit(hash2_256)
再看看 AES-128-CBC 的情况:
$ openssl enc -aes-128-cbc -kfile passphrase -md sha256 -P -nosalt
key=53A8968B0F53CAA2D21F2694B19EDD06
iv =76AF034D4D570651B3689C7827EC84C2
对 passphrase 做一次 SHA256 运算之后,前 128bit 作为 AES-128-CBC 的 Key 值,后 128bit 作为其 IV 值。写成等式是:
hash1_256 = SHA256(Passphrase)
Key = First128bit(hash1_256)
IV = Second128bit(hash1_256)
至此,可以看出 AES 算法 Key 和 IV 的生成规律了:将 hash 结果(第一次 hash 运算时为空)、passphrase 和 salt(nosalt 时为空)拼接后循环做 hash 运算,再根据 AES 所需的 Key 和 IV 的 bit 数取值。
更进一步的,从上述生成过程可见,只要生成了足够 bit 位的值,hash 运算就停止了,这称为一个迭代,这正是 OpenSSL 为人所诟病的不足。而 GnuPG 使用了多次迭代。
小结
OpenSSL AES 算法使用的 Key 和 IV 生成规律:将 hash 结果(第一次 hash 运算时为空)、passphrase 和 salt(nosalt 时为空)拼接后循环做 hash 运算,再根据 AES 所需的 Key 和 IV 的 bit 数取值。
默认的 hash 函数,从 OpenSSL 1.1 开始由 MD5 变为 SHA256。
可以通过 /etc/ssl/openssl.cnf 的 default_md 字段修改默认的 hash 函数。
OpenSSL AES 生成 Key 和 IV 时只做一次迭代,GnuPG 使用多次迭代。
以上。