密钥派生最佳实践:避免常见陷阱与安全指南

本文深入探讨密钥派生函数(KDF)的最佳实践,涵盖随机性提取与扩展、HKDF的正确使用、常见误用场景分析,以及混合密钥交换中的密钥组合技术,帮助开发者构建更安全的加密应用。

密钥派生最佳实践

密钥派生在许多密码学应用中至关重要,包括密钥交换、密钥管理、安全通信以及构建强大的密码学原语。但这也容易出错:尽管存在满足不同密钥派生需求的标准工具,但我们的审计经常发现这些工具的不当使用可能会危及密钥安全。Flickr的API签名伪造漏洞就是在密钥派生过程中误用哈希函数的著名示例。

这些误用表明对密钥派生函数(KDF)可能存在误解。本文涵盖了使用KDF的最佳实践,包括需要谨慎处理密钥派生以实现所需安全特性的专门场景。在此过程中,我们提供了回答常见问题的建议,例如:

  • 我是否需要向HKDF添加额外的随机性?
  • 我是否应该对HKDF使用盐?
  • 我是否应该使用不同的盐从HKDF派生多个密钥?
  • 如何组合多个密钥材料源?

在深入探讨密钥派生最佳实践之前,我们将回顾一些重要概念,以帮助我们更好地理解它们。

密钥材料源

诸如AEAD之类的密钥密码学原语需要满足特定要求的密钥材料来保证安全性。在大多数情况下,原语要求密钥是均匀随机生成的或在密码学上接近均匀随机。我们将区分四种类型的密钥材料:

  1. (均匀)随机,例如使用操作系统CSPRNG生成的32字节
  2. 非均匀但高熵,例如密钥交换的输出
  3. 低熵,例如密码和其他易于猜测的值
  4. 多个源的集合,例如前量子和后量子共享秘密

上面的最后一类与当前抗量子密码学的发展特别相关。结合经典和后量子密钥交换的混合密钥交换协议旨在防范“现在存储,以后解密”攻击。

密钥派生的工作原理

密钥派生是从某些初始密钥材料(IKM)生成适用于密码学使用的可接受密钥材料的过程。从密码学的角度来看,“可接受”通常意味着从所有可能密钥的集合中均匀随机选择,或与真正随机密钥无法区分。根据初始密钥材料的性质,有两个主要的密钥派生任务。

随机性提取从具有“足够随机性”的IKM中提取密码学密钥。随机性提取可选择使用盐。自然地,我们可以将随机性提取应用于已经密码学合适的密钥。

随机性扩展从密码学密钥派生子密钥。扩展通常使用每个子密钥唯一的“上下文”或“信息”输入。

这种分类深受广泛使用的KDF算法HKDF的影响;其他KDF设计不一定遵循相同的原则。然而,提取和扩展在大多数KDF应用中得到了很好的体现。此外,我们还将考虑与复杂密钥材料源(例如一组源)相关的额外KDF任务。

提取和扩展:简要了解HKDF

提示:如果您更喜欢HKDF的视觉演示,请参考下面的动画。

HKDF旨在同时提供提取和扩展。应用程序通常可以通过API访问HKDF,例如HKDF(ikm, salt, info, key_len)。然而,在底层,会发生以下情况:首先,提取过程从IKM和盐生成伪随机密钥(PRK):prk = HKDF.Extract(ikm, salt) = HMAC(salt, ikm)。然后,生成长度为key_len的子密钥:sub_key = feedback[HMAC](prk, info)。这里,feedback[HMAC]是HMAC的包装器,通过重复调用HMAC生成所需长度的输出;换句话说,它实现了可变长度的伪随机函数。对于给定的密钥,feedback将为每个新的信息输入返回所需长度的随机位串;固定的信息值将始终产生相同的输出。如果信息保持不变但长度可变,则较小的输出将是较长输出的前缀。

关于提取盐:HKDF的提取阶段可选择接受盐。提取盐是一个随机、非秘密的值,用于从密钥材料中提取足够的随机性。关键的是,盐不能被攻击者控制,因为这通常会导致KDF的灾难性后果。Hugo Krawczyk提供了一个理论示例,说明攻击者控制的盐会破坏盐和IKM之间的独立性,导致弱提取器构造。然而,后果也可能具有实际相关性,正如我们在下一节中讨论的那样。许多应用程序(例如,除了认证密钥交换之外)的一个典型痛点是对盐进行认证。因此,HKDF标准建议大多数应用程序使用常量,例如全零字节字符串。不使用盐的代价是对HMAC做出一些更强但仍合理的假设。

解决KDF误用

开发人员在选择KDF时必须考虑几个问题,但对KDF的误解可能导致引入安全问题的选择。下面,我们提供误用示例以及最佳实践,以帮助避免不当使用KDF。

我应该使用不同的盐来派生多个子密钥吗?

使用上述KDF抽象,子密钥生成更适合随机性扩展。给定一个伪随机密钥(可能在提取步骤后获得),可以使用随机性扩展为每个子密钥使用唯一的信息输入来获得子密钥。盐用于提取。此外,如上所述,攻击者控制的盐可能对安全有害。考虑一个按需生成用户密钥的密钥管理应用程序。一种实现可能决定使用用户名作为盐从主密钥派生密钥。除了自由选择他们的用户名之外,用户还可以提供一个上下文字符串(例如,“文件加密密钥”),指示密钥的用途并确保不同的应用程序使用独立的密钥。核心功能如下面的代码片段所示:

1
2
3
4
# 对于每个子密钥
def generate_user_key(username, purpose, key_len):
    ikm = fetch_master_key_from_kms()
    sub_key = hkdf(ikm=ikm, salt=username, info=purpose, key_len=key_len)

这种构造是不好的:由于盐被用作提取的HMAC“密钥”,它首先通过PAD-or-HASH方案(密钥填充、密钥哈希)进行预处理以处理可变长度密钥。在这种实现中,如果您的用户名是b”A”*65,而我选择我的用户名是sha256(b”A”*65),那么我将获得您的所有密钥!

那么我们应该怎么做呢?首先要避免的是可能被攻击者控制的盐。在上面的示例中,应用程序可以在初始化时生成一个随机盐,并在需要时从受信任的地方检索它。或者,应用程序也可以使用常量盐,如全零字节字符串,正如RFC 5869所推荐的那样。值得注意的是,对于HMAC,如果ikm已经是一个均匀随机密钥,使用常量不需要更强的假设。最后,如果IKM最初是一个随机密钥,并且用户名被限制在我们关于双PRF的讨论中描述的一组值中,也可以避免这个问题。

我应该使用什么作为信息值?

应用程序必须确保每个新的子密钥使用唯一的信息值。在信息值中包含尽可能多的上下文信息(例如会话标识符或传输哈希)也是一个好习惯。将上下文编码到信息中必须是单射的,例如,通过注意规范化问题。

我需要在HKDF的信息参数中添加额外的随机性吗?

我们经常遇到在信息参数中包含额外随机性以生成子密钥的实现。希望是使HKDF更加随机。

1
2
3
# 对于每个子密钥
extra_randomness = random(32)
sub_key = hkdf(ikm=ikm, salt=salt, info=concat(info, extra_randomness), key_len=key_len)

虽然这没有坏处,但也没有太大帮助于随机性提取的初始任务。请注意,额外的随机性只影响随机性扩展。考虑以下思想实验:如果IKM没有足够的熵,或者HMAC结果是一个非常糟糕的随机性提取器,额外的随机性将无助于创建在随机性扩展期间使用的合适密钥。一个远非随机的密钥用于随机性扩展偏离了安全要求,因此不提供安全保证。从上面的讨论来看,假设HKDF是安全的,如果ikm有足够的随机性,我们将为其提取一个随机密钥。然后,扩展将确保sub_key与相同长度的随机密钥无法区分。此外,HKDF不要求信息材料是秘密的;它只需要对每个子密钥是唯一的。

然而,应用程序可以使用额外的随机性来进一步保证信息输入的唯一性。除非您对该额外随机性做一些奇怪的事情,否则使用它不会更糟。

我应该在低熵输入上使用HKDF吗?

不。HKDF仅由几个HMAC调用组成。对于并非故意设计为缓慢且内存密集型的KDF,密码破解者可以相当高效地破解大量密码。最好使用缓慢、内存困难的算法,如Argon2,用于哈希和从密码派生密钥。此外,最好避免使用密码哈希作为加密数据的密钥。更喜欢创建密钥层次结构(例如密钥加密密钥),使用密码哈希加密随机生成的密钥,从中可以根据需要派生进一步的密钥。

我应该使用哈希函数作为通用KDF吗?

哈希函数不应在KDF中用于通用目的。在密钥派生过程中使用的信息被攻击者控制的场景中,使用哈希函数作为KDF可能使应用程序面临长度扩展攻击。这些攻击是主要关注点,适用于从秘密结合用户提供的数据生成随机性的应用程序(如Flickr的API签名伪造漏洞)。相反,更喜欢HKDF和其他专门为密钥派生设计的KDF。尽管在特定情况下哈希作为KDF是可接受的,但我们警告不要这种做法,除非用户能够合理地正式论证其应用程序的使用。如果您的应用程序确实遭受一两个额外压缩函数调用的困扰,请咨询专家,如果您没有强有力的理由使用现有的KDF。此建议也适用于其他临时构造,例如YOLO构造。

我应该将共享的Diffie-Hellman密钥用于AEAD吗?

AEAD(以及大多数其他密钥对称算法)的安全合同要求适当长度的均匀随机位串以提供有意义的安全保证。DH输出是高熵的,但通常不是均匀位串。因此,将它们用作密钥偏离了安全合同。一些实现可能允许不知情的用户对给定原语使用错误的密钥材料(例如,将DH输出馈送到Chacha20密码中)。这种使用违反了AEAD构造的要求。

组合密钥

密码学中的一个常见任务是组合原语的两个实例,使得整体构造与最强的那个一样强。自然地,这对于密钥派生是一个高度相关的问题:我们能否从一组密钥材料中派生一个秘密,使得只要其中一个密钥材料安全,整体秘密就是安全的?混合密钥交换协议是目前相关

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计