為什麼 Bcrypt 被稱為密鑰派生函式?
我試圖理解為什麼 bcrypt 被稱為密鑰派生函式?
我在 Usenix文章上查找了Ekfblowfish的詳細資訊。
讀完之後,我明白了:
- 密碼是加密密鑰。密碼的長度可以變化,因此加密密鑰的長度也可以變化(最大為 56 字節或 448 位)。
- 128位鹽,它沒有提供有關如何生成鹽的詳細資訊?它只是說,128 位鹽與可變長度加密密鑰一起用於修改 S 盒和 P 數組。
- 當它說 Key Schedule 時,它指的是 S Boxes 和 P Arrays。Key Schedule 使用 128 位 salt 和加密密鑰的位不斷變化。
現在,我們在這裡導出什麼密鑰?不清楚,為什麼 bcrypt 被稱為 Key Derivation Function?
那麼,這是否意味著在 Expensive Key Schedule 結束時,我們對 S 框和 P 數組內容具有唯一值,這些內容進一步用於使用 ECB 模式加密“OrpheanBeholderScryDoubt”。
那麼,我們不是推導出密鑰計劃(S 框 + P 數組)而不是加密密鑰(與開始時作為參數提供的密碼相同)嗎?
哪個密鑰是使用 Eksblowfish 派生的?
它被稱為密鑰派生函式,因為這就是您通常使用其輸出的目的——作為其他密碼算法的密鑰。(當然,您也可以將 Bcrypt 的輸出用於其他目的,例如將其作為密碼雜湊儲存在數據庫中,但這實際上是次要案例。)
通常,密鑰派生函式 (KDF) 有多種用途:
- **密鑰分離:**這是 KDF 最基本的案例。基本上,您只有一把鑰匙,但需要幾把。這可能是因為您是一個伺服器,需要使用不同的密鑰與多個客戶端進行通信,或者僅僅是因為您一起使用的算法(例如密碼和 MAC)僅在以下假設下被證明是安全的他們每個人都有獨立的鑰匙。您可以通過使用 KDF(使用不同的鹽)從原始密鑰派生多個(準)獨立子密鑰來解決此問題。
- **密鑰擴展:**與上述相關,出於實際原因,某些算法可能需要相當長的密鑰,即使可以通過較短的密鑰來滿足針對暴力猜測的所需安全級別。在這種情況下,您可以使用 KDF 來有效地擴展密鑰的長度。
- **密鑰白化:**許多加密算法(例如分組密碼)需要由固定數量(有效)隨機字節組成的密鑰。如果您的密鑰不是所需格式(例如任意長度的密碼或 Diffie-Hellman 共享密鑰),您可以使用接受任意輸入密鑰材料的 KDF 將其散列為合適的字節字元串尺寸。
- **密鑰拉伸:**這是 Bcrypt(和其他“基於密碼”的 KDF,如 PBKDF2 或 scrypt)設計的特定案例。基本上,假設您有一個使用者輸入的密碼片語,除了上面提到的所有其他問題(太短/太長、格式錯誤)之外,熵可能相對較小(例如,從 20 到 40 位),並且您想使用它來加密/解密某些數據(或對使用者進行身份驗證),同時通過蠻力猜測密碼片語來難以破解加密。
解決方案是使用故意緩慢(並且可能需要大量記憶體)的 KDF,例如 Bcrypt,從密碼片語中派生加密密鑰。通過故意使派生過程花費很長時間(例如,一秒鐘,這可能是簡單的非拉伸密鑰派生所需的一百萬倍),您可以通過相同的因素減慢任何暴力攻擊。之所以將其稱為“密鑰拉伸”,大概是因為您使用“短”(低熵)密鑰並有效地使其對暴力攻擊的抵抗力與“更長”(更高熵)密鑰一樣。
現在,這是一大堆工作,確實有人可能會反對將為部分或全部這些任務設計的所有功能集中在單一標籤“KDF”下,但這就是既定術語的工作方式。這是有道理的,因為需要後者屬性的系統通常也需要前者。
儘管如此,不同的 KDF 確實有不同的優勢。例如,Bcrypt 非常適合密鑰拉伸,但在其他方面有些尷尬。如果我想要一個基於 Bcrypt 的系統來處理上述所有問題,我可能會考慮將 Bcrypt 與 HKDF(來自RFC 5869)結合起來,如下所示:
- 使用 HKDF-Extract 來白化輸入密鑰材料/密碼,避免 Bcrypt 的輸入長度限制。
- 將 HKDF-Extract 生成的偽隨機密鑰輸入 Bcrypt 以抵禦暴力攻擊。
- 使用 Bcrypt 的輸出作為 HKDF-Expand 的 PRK,提供高效的密鑰分離和擴展。
BCrypt 不是密鑰派生函式,它是一種密碼散列算法。
如果您需要從密碼中派生密鑰,bcrypt 無法生成 256 位密鑰。
另一方面,人們確實使用實際的密鑰派生函式,例如:
- pbkdf2
- 加密
- 氬2
作為他們自己的密碼雜湊算法的基礎。
這些密鑰派生功能全部:
- 接受密碼
- 接受*“成本”*
- 接受您想要生成的字節數
- 並導出請求字節數的密鑰
但是 bcrypt 無法做到這一點。Bcrypt 總是並且只生成 24 個字節。
bcrypt 的內部實現細節是它對文本進行加密:
OrpheanBeholderScryDoubt (24 個字元)
並對其進行 64 次加密。
然後以特殊的序列化字元串形式(以及其他內容)將生成的 24 個字節返回給您:
$2a$10$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm \___________________________/\_____________________________/ | | other stuff 24-bytes of "strong" data
雖然你可以足夠聰明並濫用 bcrypt - 撕掉 base-64 編碼數據的最後 24 字節:
hSCvnwM9s4wIX9JeLapehKK5YdLxKcm
(24 字節的 base64 編碼數據)但是您沒有派生 key。
- 不僅因為無處傳遞參數詢問您想要導出多少字節
- 但因為它只有 192 位
- 如果您想派生 256 位密鑰
- 你不走運
- 因為 bcrypt 不是密鑰派生函式
這是一種密碼雜湊算法
濫用 bcrypt 將其用作 KDF
也許你可以聰明點,使用 bcrypt 的輸出作為基礎來建構一個密鑰派生函式:
//Derive 256-bit key using bcrypt output as the "salt": String password = "correct battery horse staple"; String salt = bcrypt(password, 14); Byte[] key = PBKDF2(password, salt, 32, 1);
順便說一句,這就是 scrypt 的工作原理:
//Derive 256-bit key using BlockMix/ROMix/Sasls8 output as the "salt": String password = "correct battery horse staple"; String salt = ROMix(password, GetRandom(16), 14, 8, 1); Byte[] key PBKDF2(password, salt, 32, 1);
但是 bcrypt 本身並不是一個密鑰派生函式。這是一個密碼雜湊算法。