OpenSSL AES 算法中 Key 和 IV 是如何生成的?(hash1_256能對上,但hash2_256對不上)


書接上回。在《LDAP 密碼加密方式初探》一文中,使用 OpenSSL 命令 AES 算法加密解密時,都用到了 Key 和 IV 參數,那么這兩個參數是如何生成的呢?

仍然以 AES-256-CBC 開始探索。先准備好生成 Key 和 IV 的 passphrase:

$ echo -n "drjom(&)(&)MOJRD" > passphrase

上述回文形式的 passphrase 來自一個神秘的組織:)

將此 passphrase 傳入 openssl 命令生成對應的 Key 和 IV:

$ openssl enc -aes-256-cbc -kfile passphrase -md md5 -P -salt
salt=51D9C4B24C759179
key=BBF4EA0E7A0EBD7C60CCE2024E218A53BBB69CCA65B4D0B705E37080676E5F5D
iv =8E5EC1AC2191167DF9B753BA93A1E7B8

其中的 salt 是隨機生成的,因此每次執行的結果並不相同。而 Key 和 IV 的生成方法參考 SuperUser 的一個回答補充及驗證如下(注意 md5sum 輸出的結果與上述 openssl 命令的輸出結果比較):

$ perl -e 'print pack "H_", "51D9C4B24C759179"' > salt
$ cat passphrase salt > hash1_128.tmp
$ md5sum hash1_128.tmp
bbf4ea0e7a0ebd7c60cce2024e218a53  hash1_128.tmp
$ perl -e 'print pack "H_", "bbf4ea0e7a0ebd7c60cce2024e218a53"' > hash1_128
$ cat hash1_128 passphrase salt > hash2_128.tmp
$ md5sum hash2_128.tmp 
bbb69cca65b4d0b705e37080676e5f5d  hash2_128.tmp
$ perl -e 'print pack "H*", "bbb69cca65b4d0b705e37080676e5f5d"' > hash2_128
$ cat hash2_128 passphrase salt > hash3_128.tmp
$ md5sum hash3_128.tmp 
8e5ec1ac2191167df9b753ba93a1e7b8  hash3_128.tmp

可以看出,對於 AES-256-CBC 來說:

hash1_128 = MD5(Passphrase + Salt)
hash2_128 = MD5(hash1_128 + Passphrase + Salt)
hash3_128 = MD5(hash2_128 + Passphrase + Salt)
Key = hash1_128 + hash2_128
IV  = hash3_128

Key 和 IV 分別就是 AES-256-CBC 的 Key 和 IV。

當沒有 salt 時,上述過程仍然成立。先使用 openssl 命令帶 -nosalt 選項生成 Key 和 IV:

$ openssl enc -aes-256-cbc -kfile passphrase -md md5 -P -nosalt
key=D5E483D8B90C02BD4D470BA8049E1FA61D64EB2BFA444CBF9853CDFB8B24DA7A
iv =304E9E87DB9C1C8101F605ED4DD0B9EB

分步驗證如下(注意 md5sum 輸出的結果與上述 openssl 命令的輸出結果比較):

$ md5sum passphrase
d5e483d8b90c02bd4d470ba8049e1fa6  passphrase
$ perl -e 'print pack "H_", "d5e483d8b90c02bd4d470ba8049e1fa6"' > hash1_128
$ cat hash1_128 passphrase > hash2_128.tmp
$ md5sum hash2_128.tmp
1d64eb2bfa444cbf9853cdfb8b24da7a  hash2_128.tmp
$ perl -e 'print pack "H_", "1d64eb2bfa444cbf9853cdfb8b24da7a"' > hash2_128
$ cat hash2_128 passphrase > hash3_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 使用多次迭代。

以上。

https://blog.lancitou.net/how-to-generate-key-and-iv-in-openssl-aes/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM