ASP.NET Core 中的內容標頭
背景和理論
在資料保護系統中,「金鑰」表示可以提供已驗證加密服務的物件。 每個金鑰都會以唯一識別碼 (GUID) 來識別,並隨附演算法資訊和熵材質。 其目的在於每個金鑰都帶有唯一的熵值,但系統無法強制執行,我們也需要考慮可能藉由修改金鑰環中現有金鑰的演算法資訊來手動變更金鑰通道的開發人員。 為了達到我們的安全性需求,鑒於這些情況,資料保護系統具有密碼編譯靈活度的概念,允許跨多個密碼編譯演算法安全地使用單一熵值。
大部分支援密碼編譯靈活度的系統執行此操作時,會在承載內包含演算法的一些識別資訊。 對此目的而言,演算法的 OID 通常是理想的候選項目。 不過,我們遇到一個問題是,有多種方式可以指定相同的演算法:「AES」(CNG) 和 Managed Aes、AesManaged、AesCryptoServiceProvider、AesCng 和 RijndaelManaged (指定特定參數) 類別實際上都是相同的,我們需要維護所有這些類別與正確 OID 的對應。 如果開發人員想要提供自訂演算法 (甚至是 AES 的另一個實作!),他們必須告訴我們其 OID。 這個額外的註冊步驟會讓系統設定特別痛苦。
退後一步,我們判斷我們解決問題的方向是錯誤的。 OID 會告訴您演算法是什麼,但實際上我們並不在意這一點。 如果我們需要在兩個不同的演算法中安全地使用單一熵值,我們就不需要知道演算法實際上是什麼。 我們實際上關心的是它們的行為。 任何體面的對稱區塊加密演算法也是強虛擬隨機排列 (PRP):修正輸入 (金鑰、鏈結模式、IV、純文字) 和加密文字輸出,其機率與給定相同輸入的任何其他對稱區塊加密演算法不同。 同樣地,任何體面的索引鍵雜湊函式也是強虛擬隨機函式 (PRF),而且對於固定輸入集,其輸出將與任何其他索引鍵雜湊函式不同。
我們會使用此強式 PRP 和 PRF 的概念來建立內容標頭。 此內容標頭基本上可作為任何指定作業使用演算法的穩定指紋,並提供資料保護系統所需的密碼編譯靈活度。 此標頭是可重現的,稍後會作為子機碼衍生程序的一部分。 根據基礎演算法的作業模式,有兩種不同的方式可以建置內容標頭。
CBC 模式加密 + HMAC 驗證
內容標頭包含下列元件:
[16 位元]值 00 00,這是表示「CBC 加密 + HMAC 驗證」的標記。
[32 位元]對稱區塊加密演算法的金鑰長度 (以位元組為單位,大端模式)。
[32 位元]對稱區塊加密演算法的金鑰長度 (以位元組為單位,大端模式)。
[32 位元] HMAC 演算法的金鑰長度 (以位元組為單位,大端模式)。 (目前金鑰大小一律符合摘要大小。)
[32 位元] HMAC 演算法的金鑰長度 (以位元組為單位,大端模式)。
EncCBC(K_E, IV, "")
,這是指定空字串輸入的對稱區塊加密演算法輸出,其中 IV 是全零向量。K_E
的建構如下所述。MAC(K_H, "")
,這是指定空字串輸入之 HMAC 演算法的輸出。K_H
的建構如下所述。
在理想情況下,我們可以傳遞全零向量作為 K_E
和 K_H
。 不過,我們想要避免基礎演算法在執行任何作業之前檢查弱式金鑰是否存在的情況 (尤其是 DES 和 3DES),這會排除使用簡單或可重複的模式,例如全零向量。
相反地,我們會在計數器模式中使用 NIST SP800-108 KDF (請參閱 NIST SP800-108,第 5.1 節),使用長度為零的金鑰、標籤和內容,並以 HMACSHA512 作為基礎 PRF。 我們會衍生 | K_E | + | K_H |
個位元組的輸出,然後將結果分解成 K_E
並自行 K_H
。 從數學上看,這會以下列方式表示。
( K_E || K_H ) = SP800_108_CTR(prf = HMACSHA512, key = "", label = "", context = "")
範例:AES-192-CBC + HMACSHA256
例如,假設對稱區塊加密演算法是 AES-192-CBC,且驗證演算法是 HMACSHA256。 系統會使用下列步驟來產生內容標頭。
首先,讓 ( K_E || K_H ) = SP800_108_CTR(prf = HMACSHA512, key = "", label = "", context = "")
,其中依據指定的演算法 | K_E | = 192 bits
和 | K_H | = 256 bits
。 這會導致下列範例中的 K_E = 5BB6..21DD
和 K_H = A04A..00A9
:
5B B6 C9 83 13 78 22 1D 8E 10 73 CA CF 65 8E B0
61 62 42 71 CB 83 21 DD A0 4A 05 00 5B AB C0 A2
49 6F A5 61 E3 E2 49 87 AA 63 55 CD 74 0A DA C4
B7 92 3D BF 59 90 00 A9
然後根據上述 IV = 0*
和 K_E
,計算 AES-192-CBC 的 Enc_CBC (K_E, IV, "")
。
result := F474B1872B3B53E4721DE19C0841DB6F
接下來,針對指定 K_H
的HMACSHA256計算 MAC(K_H, "")
,如上所示。
result := D4791184B996092EE1202F36E8608FA8FBD98ABDFF5402F264B1D7211536220C
這會產生下列完整內容標頭:
00 00 00 00 00 18 00 00 00 10 00 00 00 20 00 00
00 20 F4 74 B1 87 2B 3B 53 E4 72 1D E1 9C 08 41
DB 6F D4 79 11 84 B9 96 09 2E E1 20 2F 36 E8 60
8F A8 FB D9 8A BD FF 54 02 F2 64 B1 D7 21 15 36
22 0C
此內容標頭是已驗證加密演算法組的指紋 (AES-192-CBC 加密 + HMACSHA256 驗證)。 如以上所述,這些元件如下:
標記
(00 00)
區塊加密金鑰長度
(00 00 00 18)
區塊加密區塊大小
(00 00 00 10)
HMAC 金鑰長度
(00 00 00 20)
HMAC 摘要大小
(00 00 00 20)
區塊加密 PRP 輸出
(F4 74 - DB 6F)
和HMAC PRF 輸出
(D4 79 - end)
。
注意
不論演算法實作是由 Windows CNG 或 Managed SymmetricAlgorithm 和 KeyedHashAlgorithm 類型所提供,CBC 模式加密 + HMAC 驗證內容標頭的建置方式都相同。 這樣可讓不同作業系統上執行的應用程式可靠地產生相同的內容標頭,即使演算法的實作在 OS 之間有所不同也一樣。 (實際上,KeyedHashAlgorithm 不必是適當的 HMAC。它可以是任何索引鍵雜湊演算法類型。)
範例:3DES-192-CBC + HMACSHA1
首先,讓 ( K_E || K_H ) = SP800_108_CTR(prf = HMACSHA512, key = "", label = "", context = "")
,其中依據指定的演算法 | K_E | = 192 bits
和 | K_H | = 160 bits
。 這會導致下列範例中的 K_E = A219..E2BB
和 K_H = DC4A..B464
:
A2 19 60 2F 83 A9 13 EA B0 61 3A 39 B8 A6 7E 22
61 D9 F8 6C 10 51 E2 BB DC 4A 00 D7 03 A2 48 3E
D1 F7 5A 34 EB 28 3E D7 D4 67 B4 64
接下來,根據上述 IV = 0*
和 K_E
,計算 3DES-192-CBC 的 Enc_CBC (K_E, IV, "")
。
result := ABB100F81E53E10E
接下來,針對指定 K_H
的HMACSHA1計算 MAC(K_H, "")
,如上所示。
result := 76EB189B35CF03461DDF877CD9F4B1B4D63A7555
這會產生完整的內容標頭,這是已驗證加密演算法組的指紋 (3DES-192-CBC 加密 + HMACSHA1 驗證),如下所示:
00 00 00 00 00 18 00 00 00 08 00 00 00 14 00 00
00 14 AB B1 00 F8 1E 53 E1 0E 76 EB 18 9B 35 CF
03 46 1D DF 87 7C D9 F4 B1 B4 D6 3A 75 55
元件會細分如下:
標記
(00 00)
區塊加密金鑰長度
(00 00 00 18)
區塊加密區塊大小
(00 00 00 08)
HMAC 金鑰長度
(00 00 00 14)
HMAC 摘要大小
(00 00 00 14)
區塊加密 PRP 輸出
(AB B1 - E1 0E)
和HMAC PRF 輸出
(76 EB - end)
。
Galois/計數器模式加密 + 驗證
內容標頭包含下列元件:
[16 位元]值 00 01,這是表示「GCM 加密 + 驗證」的標記。
[32 位元]對稱區塊加密演算法的金鑰長度 (以位元組為單位,大端模式)。
[32 位元]在已驗證加密作業期間所使用的 nonce 大小 (以位元組為單位,大端模式)。 (在我們的系統中,此值固定為 nonce 大小 = 96 位元。)
[32 位元]對稱區塊加密演算法的金鑰長度 (以位元組為單位,大端模式)。 (對於 GCM,此值固定為區塊大小 = 128 位元。)
[32 位元]驗證加密函式所產生的驗證標籤大小 (以位元組為單位,大端模式)。 (在我們的系統中,此值固定為標籤大小 = 128 位元。)
[128 位元]
Enc_GCM (K_E, nonce, "")
的標記,這是指定空字串輸入的對稱區塊加密演算法輸出,其中 nonce 是 96 位元全零向量。
使用與 CBC 加密 + HMAC 驗證案例中相同的機制導出 K_E
。 不過,由於這裡沒有 K_H
,因此基本上有 | K_H | = 0
,而且演算法會萎縮成下列形式。
K_E = SP800_108_CTR(prf = HMACSHA512, key = "", label = "", context = "")
範例:AES-256-GCM
首先,讓 K_E = SP800_108_CTR(prf = HMACSHA512, key = "", label = "", context = "")
,其中 | K_E | = 256 bits
。
K_E := 22BC6F1B171C08C4AE2F27444AF8FC8B3087A90006CAEA91FDCFB47C1B8733B8
接下來,針對指定 nonce = 096
的 AES-256-GCM 計算 Enc_GCM (K_E, nonce, "")
的驗證標記,如上所示 K_E
。
result := E7DCCE66DF855A323A6BB7BD7A59BE45
這會產生下列完整內容標頭:
00 01 00 00 00 20 00 00 00 0C 00 00 00 10 00 00
00 10 E7 DC CE 66 DF 85 5A 32 3A 6B B7 BD 7A 59
BE 45
元件會細分如下:
標記
(00 01)
區塊加密金鑰長度
(00 00 00 20)
nonce 大小
(00 00 00 0C)
區塊加密區塊大小
(00 00 00 10)
驗證標籤大小
(00 00 00 10)
和執行區塊加密的驗證標記
(E7 DC - end)
。