子项派生和 ASP.NET Core 中的经过身份验证的加密Subkey derivation and authenticated encryption in ASP.NET Core

本文内容

密钥环中的大多数密钥将包含某种形式的熵,并将具有说明 "CBC-模式加密 + HMAC 验证" 或 "GCM 加密 + 验证" 的算法信息。在这些情况下,我们将嵌入的平均信息量称为此密钥的主密钥材料(或千米),并执行密钥派生函数来派生用于实际加密操作的密钥。

备注

键是抽象的,自定义实现的行为可能不如下。如果密钥提供自己的 IAuthenticatedEncryptor 实现,而不是使用我们的某个内置工厂,则本部分中所述的机制将不再适用。

附加经过身份验证的数据和子项派生Additional authenticated data and subkey derivation

IAuthenticatedEncryptor 接口充当所有经过身份验证的加密操作的核心接口。它的 Encrypt 方法采用两个缓冲区:纯文本和 additionalAuthenticatedData (AAD)。纯文本内容在对 IDataProtector.Protect的调用中保持不变,但 AAD 由系统生成并由三个部分组成:

  • 32位幻标头 09 F0 C9 F0,用于标识此版本的数据保护系统。

  • 128位密钥 id。

  • 由创建了执行此操作的 IDataProtector 的用途链构成的可变长度字符串。

由于 AAD 对于所有三个组件的元组都是唯一的,因此我们可以使用它从公里派生新密钥,而不是在我们的所有加密操作中使用公里本身。对于每次调用 IAuthenticatedEncryptor.Encrypt,都将发生以下密钥派生过程:

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

在这里,我们将在计数器模式下(请参阅NIST SP800-108,Sec. 5.1)调用以下参数:

  • 密钥派生密钥(KDK) = K_M

  • PRF = HMACSHA512

  • 标签 = additionalAuthenticatedData

  • context = contextHeader | |keyModifier

上下文标头的长度是可变的,主要作为要 K_E 和 K_H 派生的算法的指纹。密钥修饰符是为每次调用 Encrypt 随机生成的128位字符串,可以确保 KE 和 KH 对于此特定身份验证加密操作是唯一的,即使 KDF 的所有其他输入都是常量,也是如此。

对于 CBC 模式加密 + HMAC 验证操作,|K_E |对称块加密密钥的长度和 |K_H |是 HMAC 例程的摘要大小。对于 GCM 加密 + 验证操作,|K_H |= 0。

CBC 模式加密 + HMAC 验证CBC-mode encryption + HMAC validation

通过上述机制生成 K_E 后,我们将生成一个随机初始化向量,并运行对称块密码算法以加密纯文本。然后,初始化向量和密码文本将通过使用密钥 K_H 初始化的 HMAC 例程运行,以生成 MAC。下面以图形方式表示此过程和返回值。

CBC 模式进程和返回

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

备注

IDataProtector.Protect 实现将在将幻标头和密钥 id返回到调用方之前,先将其添加到输出中。由于幻标头和密钥 id 是AAD的一部分,并且由于密钥修饰符作为输入送回 KDF,这意味着最终返回的有效负载的每个字节都由 MAC 进行身份验证。

Galois/Counter 模式加密 + 验证Galois/Counter Mode encryption + validation

通过上述机制生成 K_E 后,我们将生成一个随机的96位 nonce,并运行对称块加密算法以加密纯文本并生成128位身份验证标记。

GCM 模式进程和返回

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

备注

尽管 GCM 本身支持 AAD 的概念,但我们仍只向原始 KDF 提供 AAD,选择将空字符串传递给 GCM 以用于其 AAD 参数。这样做的原因是两折。首先,为了支持灵活性,我们永远不希望使用 K_M 直接作为加密密钥。此外,GCM 对其输入施加非常严格的唯一性要求。对于两个或多个具有相同(键、nonce)对的不同输入数据集,GCM 加密例程被调用的概率不得超过 2 ^ 32。如果修复 K_E 在运行 2 ^-32 限制的落入之前,不能执行 2 ^ 32 个以上的加密操作。这似乎是一种非常大的操作,但高流量 web 服务器可以在一天内只经过4000000000请求,在这些密钥的正常生存期内。为了保持符合 2 ^-32 概率限制,我们继续使用128位密钥修饰符和96位 nonce,这大大扩展了任何给定 K_M 的可用操作计数。为简单起见,我们在 CBC 和 GCM 操作之间共享 KDF 代码路径,由于 AAD 已在 KDF 中考虑,因此无需将其转发到 GCM 例程。