Key-Derivation

PBKDF2 為登錄密鑰,scrypt 為加密密鑰

  • February 26, 2015

我需要從單個密碼客戶端派生兩個密鑰。一個用於瀏覽器內登錄,另一個用於加密。

以下安全嗎?

login key      = pbkdf2(password, salt1, lowN)
encryption key = scrypt(password, salt2, highN)

解釋:

  • 伺服器不應該知道普通密碼
  • salt1登錄時從伺服器檢索
  • 使用者使用 登錄login key,下載數據 + salt2,然後使用encryption key客戶端解密。
  • 我很想只使用 scrypt(更強),但我發現的唯一經過審查的密鑰派生 JS 庫是 SJCL 的 pbkdf2。另外,我需要使用者能夠從移動設備登錄網站。

伺服器不應該知道普通密碼

如果使用者在瀏覽器的網頁中輸入密碼,則託管該網頁的伺服器始終有機會查看輸入的密碼。沒有加密機制可以防止這種情況(此時)。

為了解決這個限制,您需要建構一個可下載的應用程序(例如,您可以使用NW.jsCordova將 HTML/JS/CSS 打包到桌面/移動應用程序中)。如果您不這樣做,那麼嘗試從伺服器隱藏密碼基本上沒有意義。 更多關於這篇文章的內容。

如果你已經這樣做了(同樣,如果你沒有繼續的話,沒有意義),如果可能的話,我建議不要使用 PBKDF2 或 scrypt 的 JS 實現。任何一個的 JS 實現都會更慢,並迫使您使用較低的迭代次數(因為使用者只會等待很長時間)。攻擊者將可以訪問高度優化的 C 實現,因此這將為您的攻擊者帶來顯著優勢。請注意,大多數語言都有用於 PBKDF2 或 scrypt 的 C 實現的包裝庫(例如node-scrypt)。

要派生密鑰,我建議在客戶端上採用以下方法:

  • 使用 scrypt 派生一個 256 位主密鑰(也可以接受 PBKDF2)。使用盡可能高的安全參數,但請記住,移動設備可能會遇到困難。我建議為每個使用者使用獨特的鹽。
  • 使用HKDF-SHA256派生登錄密鑰。HKDF 允許您安全地從主密鑰派生密鑰,這樣就無法從派生密鑰中確定主密鑰(或其他密鑰)。在contextorinfo參數中,使用字元串"server-login-key"。128 位的輸出應該足夠了。
  • 再次使用 HKDF 導出加密密鑰。確保從主密鑰而不是登錄密鑰派生加密密鑰。在context/info參數中,使用字元串"client-encryption-key"。生成一個足夠長的密鑰以匹配您計劃使用的任何加密算法(例如 AES256 的 256 位)。

在伺服器上儲存登錄密鑰時,請務必將它們視為密碼(僅儲存加鹽雜湊)。您可以在這裡再次使用 PBKDF2/bcrypt/scrypt,但這並不重要,因為密鑰已經被拉伸。額外的拉伸是一件好事,但如果伺服器實際上是惡意的,這將無濟於事。

這與您的設計之間的一個重要區別是它只呼叫 PBKDF2/scrypt 一次,而不是兩次。這允許您將安全參數加倍(即,一次呼叫具有 2N 次迭代,而不是兩次呼叫,每次呼叫具有 N 次迭代)。

如果使用者沒有強密碼,那麼伺服器將能夠根據登錄密鑰破解密碼(字典攻擊),所以也要慎重考慮。

注意:在這種情況下,HKDF 的“提取”步驟是可選的,因為 PBKDF2 和 scrypt 已經輸出了加密強密鑰,但它不會受到傷害。

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