Share via


ASP.NET Core 中的子機碼衍生和驗證的加密

金鑰通道中的大部分金鑰都會包含某種形式的熵,而且會有演算法資訊,指出「CBC 模式加密 + HMAC 驗證」或「GCM 加密 + 驗證」。 在這些情況下,我們將內嵌的熵稱為此金鑰的主要金鑰處理資料 (簡稱 KM),並執行金鑰衍生函數 (KDF) 來衍生將用於實際密碼編譯作業的金鑰。

注意

金鑰是抽象的,而且自訂實作的行為可能如下所示。 如果金鑰提供自己的 IAuthenticatedEncryptor 實作,而不是使用我們的其中一個內建處理站,本節所述的機制就不再適用。

額外驗證資料和子機碼衍生

IAuthenticatedEncryptor 介面可作為所有已驗證加密作業的核心介面。 其 Encrypt 方法會採用兩個緩衝區:plaintext 和 additionalAuthenticatedData (AAD)。 純文字內容會流式傳輸對 IDataProtector.Protect 的呼叫並且不進行任何變更,但 AAD 是由系統產生,由三個元件組成:

  1. 32 位元 magic 標頭 09 F0 C9 F0,用於識別此版本的資料保護系統。

  2. 128 位元金鑰識別碼。

  3. 由目的鏈結形成的可變長度字串,目的鏈接建立了執行這項作業的 IDataProtector

由於 AAD 對於這三個元件的元組而言是唯一的,因此我們可以使用它從 KM 衍生新的金鑰,而不必在所有密碼編譯作業中都使用 KM 本身。 每次呼叫 IAuthenticatedEncryptor.Encrypt 時,就會進行下列金鑰衍生程式:

( K_E, K_H ) = SP800_108_CTR_HMACSHA512(K_M, AAD, contextHeader || keyModifier)

在這裡,我們會在計數器模式下呼叫 NIST SP800-108 KDF (請參閱 NIST SP800-108 第 5.1 節) 並採用下列參數:

  • 金鑰衍生金鑰 (KDK) = K_M

  • PRF = HMACSHA512

  • label = additionalAuthenticatedData

  • context = contextHeader || keyModifier

內容標頭的長度可變,基本上可作為我們衍生 K_EK_H 演算法的指紋。 金鑰修飾詞是每次呼叫 Encrypt 時隨機產生的 128 位元字串,可確保 KE 和 KH 對於此特定驗證加密作業具備唯一性的概率是壓倒性的,即使 KDF 的其他所有輸入都是常數。

針對 CBC 模式加密 + HMAC 驗證作業,| K_E | 是對稱區塊編碼器金鑰的長度,| K_H | 是 HMAC 常式的摘要大小。 針對 GCM 加密 + 驗證作業 | K_H | = 0

CBC 模式加密 + HMAC 驗證

透過上述機制產生 K_E 之後,我們會產生隨機初始化向量,並執行對稱區塊編碼器演算法來加密純文字。 初始化向量和加密文字接著會透過 HMAC 常式 (已使用 K_H 金鑰初始化) 執行來產生 MAC。 此流程與傳回值會以圖形方式呈現於下方。

CBC-mode process and return

output:= keyModifier || iv || E_cbc (K_E,iv,data) || HMAC(K_H, iv || E_cbc (K_E,iv,data))

注意

IDataProtector.Protect 實作會在輸出的前面加上 magic 標頭和金鑰識別碼,再將它傳回給呼叫端。 因為 magic 標頭和金鑰識別碼是 AAD 的隱含部分,而且因為金鑰修飾元會當做 KDF 的輸入來提供,這表示 MAC 會驗證最終傳回承載的每一個位元組。

Galois/計數器模式加密 + 驗證

透過上述機制產生 K_E 之後,我們會產生隨機 96 位元 nonce,並執行對稱區塊編碼器演算法來加密純文字並產生 128 位元驗證標籤。

GCM-mode process and return

output := keyModifier || nonce || E_gcm (K_E,nonce,data) || authTag

注意

雖然 GCM 原生支援 AAD 的概念,但我們還是只將 AAD 饋送至原始 KDF,選擇將空字串傳遞至 GCM 作為其 AAD 參數。 原因有兩方面。 首先,為支援靈活度,我們永遠不想直接使用 K_M 作為加密金鑰。 其次,GCM 會對其輸入施加非常嚴格的唯一性要求。 以相同金鑰/nonce 對針對兩組或兩組以上不同輸入資料組叫用 GCM 加密常式的機率不得超過 2^-32。 如果我們固定 K_E,則我們執行的加密作業不能超過 2^32 次個,否則就會違反 2^-32 的上限。 這看起來可能是極大的作業數量,但高流量 Web 伺服器在短短幾天內就可能會處理 40 億個要求,此時這些金鑰在正常存留期內。 為了符合 2^-32 機率限制的規範,我們繼續使用 128 位元的索引鍵修飾元和 96 位元 nonce,這會大幅擴充任何給定 K_M 的可用作業計數。 為了簡化設計,我們在 CBC 和 GCM 作業之間共用 KDF 程式碼路徑,而且因為 KDF 中已考慮 AAD,因此不需要將它轉送至 GCM 常式。