Aes

是否有 OpenSSL 可互操作的 AES 加密標準?

  • December 26, 2020

許多 AES 加密的東西(文件、字元串、數據庫條目等)以"Salted__""U2FsdGVkX1"在 base64 中)開頭。我聽說這是某種 OpenSSL 互操作性的東西:a b c

某處是否有一些標準參考(可能是 RFC?)來解釋這種 OpenSSL 可互操作的 AES 加密的東西是如何產生並隨後解密的?

理想情況下,答案將連結到整個過程的標準參考,或者可能是步驟的簡短摘要列表,其中包含指向每個步驟的標準參考的連結,例如:

要以已知密碼開頭"U2FsdGVkX1"並使用已知密碼解密這樣的東西,

  • 首先進行base64解碼 - 參見Wikipedia: base64。結果將以"Salted__". 注意不要使用 C 字元串,因為結果可能包含幾個0x00字節。
  • (我猜想一些關於pickle和靜脈注射的東西?)
  • (我猜這裡有關於 CBC 或 CTR 的內容?) - 參見Wikipedia: block cipher mode of operation
  • (也許這裡有關於消息身份驗證的內容?)
  • 使用 AES 解密每個塊decrypt_one_AES_block( key, block_of_128_bits )- 請參閱維基百科:AES 文章高級加密標準 (AES) 簡筆圖指南
  • 保存 each 的結果decrypt_one_AES_block(),將它們連接在一起,這就是你的明文。如果原始文件是人類可讀的文本或 HTML 文件,則可以將結果儲存在 C 字元串中;但其他種類的東西可能包括幾個0x00與 C 字元串不兼容的字節。

這聽起來像是我打算自己編寫一個實現。我向您保證,我計劃使用幾個可用庫之一——只是在查看這些庫時,我想知道它們該做什麼。OpenSSL 庫應該做什麼?

是否有 OpenSSL 可互操作的 AES 加密標準?

使用以下 openssl 命令作為此答案的基礎:

echo -n 'Hello World!' | openssl enc aes-256-cbc -e -a -salt -pbkdf2 -iter 10000 

此命令加密明文“Hello World!” 使用 aes-256-cbc。密鑰是使用 pbkdf2 使用密碼和隨機鹽生成的,具有 10,000 次 sha256 散列迭代。當提示輸入密碼時,我輸入了密碼“p4$$w0rd”。該命令產生的密文輸出為:

U2FsdGVkX1/Kf8Yo6JjBh+qELWhirAXr78+bbPQjlxE=

現在,要回答提出的問題,’要解密以“U2FsdGVkX1”開頭並使用已知密碼的東西’:

請執行下列操作:

  1. base64 解碼 openssl 的輸出,utf-8 解碼密碼,這樣我們就有了這兩個的底層字節。
  2. salt 是 base64 解碼的 openssl 輸出的字節 8-15。
  3. 給定密碼字節和鹽以及 10,000 次 sha256 散列迭代,使用 pbkdf2 派生一個 48 字節的密鑰。
  4. key 是派生密鑰的 0-31 字節,iv 是派生密鑰的 32-47 字節。
  5. 密文是經過 base64 解碼的 openssl 輸出末尾的第 16 個字節。
  6. 使用 aes-256-cbc、給定密鑰、iv 和密文解密密文。
  7. 從純文字中刪除 PKCS#7 填充。明文的最後一個字節表示附加到明文末尾的填充字節數。這是要刪除的字節數。

下面是上述過程的python3實現:

import base64
import hashlib
from Crypto.Cipher import AES       #requires pycrypto

#inputs
openssloutputb64='U2FsdGVkX1/Kf8Yo6JjBh+qELWhirAXr78+bbPQjlxE='
password='p4$$w0rd'
pbkdf2iterations=10000

#convert inputs to bytes
openssloutputbytes=base64.b64decode(openssloutputb64)
passwordbytes=password.encode('utf-8')

#salt is bytes 8 through 15 of openssloutputbytes
salt=openssloutputbytes[8:16]

#derive a 48-byte key using pbkdf2 given the password and salt with 10,000 iterations of sha256 hashing
derivedkey=hashlib.pbkdf2_hmac('sha256', passwordbytes, salt, pbkdf2iterations, 48)

#key is bytes 0-31 of derivedkey, iv is bytes 32-47 of derivedkey 
key=derivedkey[0:32]
iv=derivedkey[32:48]

#ciphertext is bytes 16-end of openssloutputbytes
ciphertext=openssloutputbytes[16:]

#decrypt ciphertext using aes-cbc, given key, iv, and ciphertext
decryptor=AES.new(key, AES.MODE_CBC, iv)
plaintext=decryptor.decrypt(ciphertext)

#remove PKCS#7 padding. 
#Last byte of plaintext indicates the number of padding bytes appended to end of plaintext.  This is the number of bytes to be removed.
plaintext = plaintext[:-plaintext[-1]]

#output results
print('openssloutputb64:', openssloutputb64)
print('password:', password)
print('salt:', salt.hex())
print ('key:', key.hex())
print ('iv:', iv.hex())
print ('ciphertext:', ciphertext.hex())
print ('plaintext:', plaintext.decode('utf-8'))

正如預期的那樣,上面的 python3 腳本產生以下內容:

openssloutputb64: U2FsdGVkX1/Kf8Yo6JjBh+qELWhirAXr78+bbPQjlxE=
password: p4$$w0rd
salt: ca7fc628e898c187
key:  444ab886d5721fc87e58f86f3e7734659007bea7fbe790541d9e73c481d9d983
iv:  7f4597a18096715d7f9830f0125be8fd
ciphertext:  ea842d6862ac05ebefcf9b6cf4239711
plaintext:  Hello World!

注意:可以在https://github.com/meixler/web-browser-based-file-encryption-decryption找到javascript 中的等效/兼容實現(使用web crypto api ) 。

-pbkdf2(2020 年 12 月 25 日添加)此答案適用於支持該選項的 openssl v1.1.1 。舊版本的 openssl(或帶有該-md md5選項的新版本 openssl)使用基於 md5 雜湊函式的弱密鑰派生方法,如dave_thompson_085下面的回答中Thomas Pornin他的回答中所解釋的。有關如何實現此密鑰派生方法的詳細資訊,請參閱https://security.stackexchange.com/questions/29106/openssl-recover-key-and-iv-by-passphrase/242567#242567

引用自:https://crypto.stackexchange.com/questions/3298