在所有身份认证手段中,密码是历史最悠久、部署最广泛的一种。从
Unix 系统的 /etc/passwd
到如今数十亿用户依赖的网络服务,密码始终扮演着数字世界”钥匙”的角色。然而,一个看似简单的问题——“如何在不泄露密码的前提下,让服务器确信用户知道密码”——却驱动了数十年的密码学研究。本文将从密码存储的演进讲起,逐步深入到
PAKE(Password-Authenticated Key
Exchange,密码认证密钥交换)协议家族,最终详解当今最先进的
OPAQUE 协议。
一、密码存储的演进
要理解密码认证协议的动机,首先需要回顾服务器端存储密码的方式如何一步步走向成熟。
明文存储时代。
最早期的系统直接将用户密码以明文形式写入数据库。一旦数据库被攻破,所有用户的密码立刻暴露无遗。这种做法在今天看来不可思议,但在互联网发展初期却极为普遍。早期
Unix 系统便将密码明文存放在 /etc/passwd
文件中,任何有读权限的用户都能看到其他人的密码。
简单哈希时代。 人们很快意识到明文存储的危险,开始使用单向哈希函数(如 MD5、SHA-1)对密码进行处理。服务器存储的不再是密码本身,而是密码的哈希值。验证时,服务器对用户提交的密码重新计算哈希,与存储的哈希值比较。这种方法的问题在于:相同的密码总是产生相同的哈希值。攻击者可以预先构建彩虹表(Rainbow Table),即大量常见密码与其哈希值的映射表,然后对窃取到的哈希值进行反向查找。MD5 的计算速度极快,一台现代 GPU 每秒可以计算数十亿次 MD5,这使得暴力破解变得轻而易举。
加盐哈希时代。 为了对抗彩虹表,人们引入了盐值(Salt)——为每个用户生成一段随机字符串,将其与密码拼接后再计算哈希。这样,即使两个用户使用相同密码,由于盐值不同,存储的哈希也不同。盐值与哈希一起存储在数据库中。这有效地打破了彩虹表攻击,因为攻击者无法预计算所有可能的盐值组合。然而,加盐哈希并未改变一个根本事实:通用哈希函数的计算速度仍然太快。攻击者拿到数据库后,仍可对每个用户的盐值逐一发起暴力枚举。
慢哈希函数时代。 真正的防线转移到了计算成本上。bcrypt(1999 年提出)是第一个广泛使用的慢哈希函数,它内置盐值,并通过可调节的成本参数(cost factor)控制迭代次数,使得单次哈希计算需要消耗可观的时间。scrypt(2009 年)进一步增加了内存消耗维度,以抵抗基于 ASIC 和 GPU 的专用破解硬件。Argon2(2015 年赢得密码哈希竞赛)则同时提供了可调的时间成本、内存成本和并行度,被认为是当前最先进的密码哈希算法。Argon2 有三个变体:Argon2d 对 GPU 攻击有更强的抵抗力,Argon2i 对侧信道攻击有更好的防护,Argon2id 则结合两者优点,是推荐的默认选择。
为什么仅有哈希存储仍然不够? 即使服务器使用了 Argon2 存储密码哈希,认证过程本身仍然存在致命弱点。在传统的认证流程中,用户将密码(或其哈希)通过网络发送给服务器。即使传输层使用了 TLS 加密,密码仍然以明文形式出现在服务器的内存中。一旦服务器被入侵,攻击者可以直接拦截认证过程中的密码明文,而无需费力破解哈希。此外,用户可能在多个网站使用相同密码,一个恶意或被攻陷的服务器就能获取用户在其他服务上的凭证。这些问题的本质在于:传统认证需要服务器”看到”密码。如果我们能让服务器在从未接触到密码的情况下完成认证,安全性将获得根本性提升。这正是 PAKE 协议要解决的问题。
二、PAKE 的动机
PAKE(Password-Authenticated Key Exchange,密码认证密钥交换)的核心思想是:通信双方仅凭一个共享的低熵秘密(通常是密码),就能在不安全的信道上完成相互认证,并协商出一个高熵的会话密钥。在这个过程中,密码本身永远不会以任何形式在网络上传输。
传统的密钥交换协议(如 Diffie-Hellman)能够在不安全的信道上建立共享密钥,但它无法抵抗中间人攻击(Man-in-the-Middle Attack)——攻击者可以分别与通信双方建立密钥交换,而双方却无法察觉。公钥基础设施(PKI)通过数字证书解决了这个问题,但它要求用户管理证书或信任第三方证书颁发机构。PAKE 的独特价值在于:它仅使用一个低熵的密码就能同时实现认证和密钥协商,不需要证书、不需要安全信道传输密码、不需要预先建立的公钥基础设施。
PAKE 必须抵抗离线字典攻击(Offline Dictionary Attack)。密码的熵很低——一个典型的用户密码可能只有 20 到 40 比特的熵。如果协议泄露了任何关于密码的信息,攻击者就可以在离线环境中逐一尝试所有可能的密码,直到找到匹配的那个。一个安全的 PAKE 协议必须确保:窃听者从协议交互中获取的信息量为零(除了认证是否成功这一比特);主动攻击者每次在线交互最多只能测试一个密码猜测。换句话说,攻击者破解密码的唯一方式是反复与真实的服务器交互,而每次交互只能验证一个猜测——这使得暴力破解在实践中不可行。
PAKE 的概念最早由 Bellovin 和 Merritt 在 1992 年提出的 EKE(Encrypted Key Exchange)协议中奠定。此后三十年间,研究者提出了数十种 PAKE 变体,从理论走向标准化,直到近年来 OPAQUE 被 IETF 标准化,PAKE 才真正迎来大规模部署的时代。
三、平衡与增强 PAKE
PAKE 协议可以按照一个关键维度进行分类:双方对密码的了解程度。
平衡 PAKE(Balanced PAKE)。 也称为对称 PAKE,协议双方都直接持有密码(或从密码派生出的相同秘密)。典型的应用场景是两个对等节点之间的认证,例如 Wi-Fi 设备配对。在平衡 PAKE 中,如果攻击者攻破了任何一方,就等于获取了密码本身。代表性协议包括 SPAKE2、CPace 和早期的 EKE。
增强 PAKE(Augmented PAKE)。 也称为非对称 PAKE 或验证者 PAKE(Verifier-based PAKE)。在这类协议中,客户端持有密码,而服务器仅存储从密码派生出的验证信息(Verifier)——类似于存储密码哈希而非密码本身。关键的安全属性是服务器妥协安全(Server Compromise Security):即使服务器被完全攻破,攻击者获得了验证信息,也无法直接冒充客户端,仍然需要对验证信息进行(代价高昂的)离线字典攻击才能恢复密码。这显著提高了服务器被入侵后的安全性。增强 PAKE 的代表协议包括 SRP、OPAQUE 和 AuCPace。
增强 PAKE 是客户端-服务器场景的理想选择。它结合了两个世界的优点:服务器不存储明文密码(类似于传统的密码哈希方案),同时认证过程中密码也不会出现在网络上或服务器内存中(PAKE 的核心优势)。理解了这一分类之后,我们来依次考察几个具有代表性的协议。
四、SRP
SRP(Secure Remote Password,安全远程密码协议)是最早得到广泛部署的增强 PAKE 协议。它由 Tom Wu 在 1998 年提出,经过多次改进,当前版本为 SRP-6a。SRP 被纳入 TLS 扩展(RFC 5054),并在 Apple 的 iCloud Keychain 和 1Password 等产品中得到实际应用。
SRP 的注册流程如下:用户选择一个密码 pw,客户端生成随机盐值 s,计算 x = H(s, pw)(其中 H 是哈希函数),然后计算验证者 v = g^x mod N(其中 g 和 N 是预定义的大素数群参数)。客户端将 (s, v) 发送给服务器存储。服务器存储的是验证者 v 而非密码 pw,也不知道 x 的值。
SRP 的认证流程基于一种改进的 Diffie-Hellman 交换。客户端生成随机数 a,计算 A = g^a mod N 并发送给服务器。服务器生成随机数 b,计算 B = kv + g^b mod N(其中 k 是一个乘法参数)并连同盐值 s 一起发送给客户端。双方各自利用自己的秘密(客户端使用 x,服务器使用 v 和 b)计算出相同的会话密钥 S。最后,双方通过交换会话密钥的哈希来完成相互验证。协议的巧妙之处在于:只有知道正确密码的客户端才能从 B 中”剥离”kv 项,进而正确计算会话密钥。
然而 SRP 存在若干局限性。首先,SRP 的安全性证明依赖于非标准的困难性假设——它不是在标准的计算模型下被证明安全的,而是依赖于启发式论证和数十年的密码分析检验。这在理论上留下了隐患。其次,SRP 仅支持有限域上的离散对数群,无法直接适配椭圆曲线群。由于现代密码学强烈倾向使用椭圆曲线(更短的密钥长度、更高的效率),SRP 的群结构限制导致它在性能上不占优势,密钥也更长。第三,SRP 的协议设计较为复杂,容易在实现中引入错误。特别是参数 k 的计算、临时值的范围验证等细节,历史上多个 SRP 实现曾出现安全漏洞。最后,SRP 缺乏形式化的安全性证明意味着我们无法精确量化其安全性——我们只知道目前没有人攻破它,但不知道它”有多安全”。这些因素促使密码学界寻找更优雅、有更强安全保证的替代方案。
五、SPAKE2
SPAKE2 是一个简洁优雅的平衡 PAKE 协议,由 Abdalla 和 Pointcheval 在 2005 年提出。与 SRP 不同,SPAKE2 具有在随机预言模型(Random Oracle Model)下可证明的安全性。
SPAKE2 的核心思想惊人地简单。设 G 是一个素数阶循环群的生成元,M 和 N 是群中两个随机选取的”什么都不知道的”(nothing-up-my-sleeve)元素——即没有人知道 M 和 N 相对于 G 的离散对数。设密码的数值表示为 w。客户端选择随机数 x,计算 X = xG + wM 并发送;服务器选择随机数 y,计算 Y = yG + wN 并发送。客户端收到 Y 后计算 K = x(Y - wN) = xyG;服务器收到 X 后计算 K = y(X - wM) = xyG。双方得到了相同的值 K = xyG,由此派生会话密钥。
为什么这个协议是安全的?密码 w 被”掩藏”在 M 和 N 的倍数中。窃听者看到的是 X = xG + wM 和 Y = yG + wN,但由于不知道 M 和 N 的离散对数,无法从 X 中分离出 w 的信息。主动攻击者如果猜测密码为 w’,可以尝试计算 x(Y - w’N),但只有当 w’ = w 时才能得到正确的会话密钥。因此,每次在线交互最多验证一个密码猜测,完美满足 PAKE 的安全要求。
SPAKE2 的局限在于它是平衡 PAKE:双方都需要知道密码 w。IETF 已将 SPAKE2 标准化为 RFC 9382,并定义了一个扩展变体 SPAKE2+,试图提供增强安全属性。SPAKE2+ 让服务器存储 (wG, w·pG)(其中 p 是额外的标量),而非密码本身。但 SPAKE2+ 的安全性证明不如 OPAQUE 干净,且其增强属性的强度存在学术争议。在实际部署中,Google 的 Titan 安全密钥使用 SPAKE2 进行设备配对,Matter 智能家居协议也采用 SPAKE2 作为设备入网时的认证手段。
六、OPRF 回顾
在深入 OPAQUE 之前,我们需要理解它的核心构件之一:OPRF(Oblivious Pseudorandom Function,不经意伪随机函数)。在本系列此前的文章中我们已经接触过 OPRF,这里做一个面向密码认证场景的简要回顾。
OPRF 是一种两方协议:服务器持有密钥 k,客户端持有输入 x。协议结束后,客户端学到 F(k, x)(伪随机函数在密钥 k 下对 x 的求值结果),而服务器不知道 x 也不知道 F(k, x)。“不经意”(Oblivious)一词正是指服务器对客户端的输入和输出一无所知。
在密码认证场景中,OPRF 的作用至关重要。客户端的输入 x 是密码,服务器的密钥 k 是一个高熵随机值。OPRF 的输出 F(k, pw) 被用作密钥派生的种子。这种设计带来了一个关键优势:即使攻击者攻破了服务器,获取了 k 和加密后的用户数据,他仍然需要在离线环境中对每个密码猜测计算 F(k, pw’) 来验证——这本质上就是一次带有服务器密钥增强的离线字典攻击。如果 OPRF 的底层群运算足够耗时,或者结合了 Argon2 等慢哈希函数,离线攻击的成本将大幅增加。
更重要的是,OPRF 确保了服务器在认证过程中完全不接触密码。客户端将密码映射到群元素 H(pw),然后用随机致盲因子 r 计算 blinded = rH(pw) 发送给服务器。服务器用密钥 k 计算 evaluated = k·blinded = k·rH(pw) 并返回。客户端去除致盲因子得到 kH(pw) = F(k, pw)。在整个过程中,服务器只看到一个随机群元素 blinded,无法推断出密码的任何信息。IETF 在 RFC 9497 中标准化了基于椭圆曲线的 OPRF(称为 VOPRF,Verifiable OPRF,其中可验证性允许客户端确认服务器正确使用了密钥 k)。这正是 OPAQUE 协议的基石。
七、OPAQUE 详解
OPAQUE 是由 Stanislaw Jarecki、Hugo Krawczyk 和 Jiayu Xu 在 2018 年提出的增强 PAKE 协议,名称来源于”O”(OPRF)和”PAKE”的组合。2025 年,OPAQUE 以 RFC 9807 的形式被 IETF 正式标准化。OPAQUE 的设计哲学是模块化:它将 OPRF、密钥派生函数和认证密钥交换(AKE,Authenticated Key Exchange)三个组件清晰分离,使得每个组件都可以独立选择和替换。
注册流程
OPAQUE 的注册流程(Registration)分为两个阶段。第一阶段是 OPRF 求值。客户端将密码 pw 通过致盲后发送给服务器,服务器用其 OPRF 密钥 kU 进行求值并返回。客户端去盲后得到 rwd = F(kU, pw),这被称为随机化密码(Randomized Password)。关键点在于:rwd 是一个高熵值,即使密码本身是低熵的。
第二阶段是信封构造。客户端生成一对长期密钥 (skC, pkC),并使用从 rwd 派生的对称密钥将私钥 skC 加密到一个”信封”(Envelope)中。客户端将信封、公钥 pkC 和致盲后的 OPRF 交互记录发送给服务器。服务器存储以下内容:用户的信封(加密后的私钥)、用户的公钥 pkC、OPRF 密钥 kU 和相关参数。注意,服务器从不接触密码 pw,也无法解密信封——只有知道密码的客户端才能通过 OPRF 重新派生出 rwd,进而解密信封、恢复私钥。
认证流程
OPAQUE 的认证流程(Login)同样分为两个逻辑阶段。第一阶段再次执行 OPRF:客户端发送致盲后的密码,服务器用 kU 求值并返回,同时附上该用户的信封和相关公钥信息。客户端去盲得到 rwd,解密信封恢复私钥 skC。
第二阶段是一个标准的 AKE 协议。双方现在各自持有长期密钥对——客户端有 (skC, pkC),服务器有 (skS, pkS)——于是执行一个经过认证的密钥交换(如 3DH 或 HMQV),协商出会话密钥。AKE 阶段与 OPRF 阶段可以在消息层面进行流水线化,使得整个认证流程只需要三条消息(即一次往返加一条确认消息)。
安全属性
OPAQUE 在标准模型和随机预言模型下均有严格的安全性证明,具备以下核心属性。第一,离线字典攻击抵抗:窃听者和主动攻击者无法从协议交互中获取任何可用于离线密码猜测的信息。第二,服务器妥协安全:即使服务器被完全攻破,攻击者获取了 OPRF 密钥和加密的信封,仍需进行完整的离线字典攻击才能恢复密码。第三,前向安全性(Forward Secrecy):由于 AKE 阶段使用临时密钥,即使长期密钥后来被泄露,过去的会话密钥仍然安全。第四,输入不可知性(Input Obliviousness):服务器在整个过程中从未接触到密码的任何表示形式。
笔者认为,OPAQUE 代表了密码认证领域的一次范式转换。传统认证模型的隐含假设是”服务器是可信的处理密码的实体”——即使我们用 TLS 保护传输、用 Argon2 保护存储,密码仍然以明文形式短暂存在于服务器内存中。这个假设在服务器被入侵、被植入后门、或运维人员恶意行为的场景下完全不成立。OPAQUE 从根本上消除了这个假设:服务器自始至终不接触密码——不是在注册时,不是在认证时,甚至不是在服务器内存中。密码通过 OPRF 的致盲机制被转化为一个服务器无法理解的群元素,而只有客户端能将其去盲还原。这意味着即使攻击者获得了服务器的完整内存快照,他看到的也只是随机的群元素,无法从中提取任何关于密码的信息。从”信任服务器不会泄露密码”到”服务器在数学上不可能看到密码”,这不仅是安全级别的提升,更是信任模型的根本重构。
一个值得深思的现象是,密码作为认证机制,在理论上是最差的选择,在实践中却是最顽强的幸存者。每一个被宣称为”密码杀手”的替代方案都未能完全取代它:生物特征无法撤销(你不能更换指纹),且需要专用传感器硬件;硬件令牌可以丢失、被盗、被遗忘在酒店房间里;Passkey/FIDO2 依赖设备生态系统的支持和用户对同步机制的理解。密码之所以存活至今,恰恰因为它拥有一个其他认证因子都不具备的独特属性——它只存在于用户的记忆中,不需要任何物理设备、不需要生物特征采集、不需要网络连接即可”携带”。这并不意味着密码是好的认证方式,而是意味着任何试图替代密码的方案都必须在便利性和普适性上达到相同的门槛——而这个门槛远比大多数安全工程师认为的更高。
从工程实践来看,OPAQUE 协议从学术论文到生产部署之间的鸿沟,是密码学领域”理论领先于实践”这一老问题的最新案例。OPAQUE 由 Jarecki、Krawczyk 和 Xu 于 2018 年发表,IETF 草案(draft-irtf-cfrg-opaque)于 2020 年启动,但截至 2025 年,生产级别的 OPAQUE 部署仍然凤毛麟角。原因并非技术障碍——OPAQUE 的密码学原语(OPRF、AKE)都已有成熟实现——而是生态系统惯性:现有的认证基础设施(OAuth 2.0、SAML、LDAP)围绕”服务器看到密码”这一假设构建了整个中间件栈,从 WAF 的登录审计到风控系统的行为分析,都依赖于在服务器端获取密码明文或其哈希。迁移到 OPAQUE 意味着重构这些依赖链条的每一个环节。密码学领域的教训是:从”可证明更好”到”实际部署”之间的距离,通常以十年为单位衡量,而这个距离主要不是由密码学决定的,而是由软件架构的惯性决定的。
简化的 Python 演示
以下是 OPAQUE 注册和认证流程的简化 Python 演示。为了可读性,我们使用简单的数学运算代替真正的椭圆曲线操作,并省略了许多安全关键的细节(如致盲因子的正确处理、密钥确认步骤等)。这段代码仅用于说明协议的逻辑流程,绝对不可用于生产环境。
import hashlib
import os
import hmac
def H(*args):
"""简化的哈希函数,将任意数量的参数拼接后取 SHA-256。"""
data = b"".join(
a.encode() if isinstance(a, str) else a for a in args
)
return hashlib.sha256(data).digest()
def xor_bytes(a: bytes, b: bytes) -> bytes:
return bytes(x ^ y for x, y in zip(a, b))
# ── OPRF 模拟 ──────────────────────────────────────────────
# 真实 OPAQUE 使用椭圆曲线上的标量乘法;
# 此处用 HMAC 模拟 F(k, x) = HMAC(k, x) 作为伪随机函数。
def oprf_server_key():
"""服务器为每个用户生成一个 OPRF 密钥。"""
return os.urandom(32)
def oprf_evaluate(key: bytes, blinded_input: bytes) -> bytes:
"""服务器端 OPRF 求值(简化:省略致盲/去盲过程)。"""
return hmac.new(key, blinded_input, hashlib.sha256).digest()
def oprf_client(password: str, oprf_key: bytes) -> bytes:
"""客户端完整 OPRF 流程(简化:直接调用求值代替交互)。"""
blinded = H(password) # 真实协议中需要致盲
evaluated = oprf_evaluate(oprf_key, blinded)
return evaluated # 即 rwd(随机化密码)
# ── 注册流程 ───────────────────────────────────────────────
class ServerRecord:
"""服务器为每个用户存储的记录。"""
def __init__(self):
self.oprf_key = None
self.envelope = None # 加密后的客户端私钥
self.client_pubkey = None # 客户端公钥
self.server_pubkey = None # 服务器公钥
def registration(username: str, password: str):
"""
OPAQUE 注册流程(简化版)。
返回 (客户端私钥, 服务器记录)。
"""
# 1) 服务器生成 OPRF 密钥
oprf_key = oprf_server_key()
# 2) 客户端通过 OPRF 获得随机化密码
rwd = oprf_client(password, oprf_key)
# 3) 客户端生成长期密钥对(简化为随机字节)
client_sk = os.urandom(32)
client_pk = H(b"pk", client_sk) # 简化:公钥 = 私钥的哈希
# 4) 用 rwd 派生的密钥加密私钥,构造信封
envelope_key = H(b"envelope", rwd)
envelope = xor_bytes(client_sk, envelope_key)
# 5) 服务器也有自己的密钥对
server_sk = os.urandom(32)
server_pk = H(b"pk", server_sk)
# 6) 组装服务器记录
record = ServerRecord()
record.oprf_key = oprf_key
record.envelope = envelope
record.client_pubkey = client_pk
record.server_pubkey = server_pk
record.server_sk = server_sk # 服务器自己保留
print(f"[注册] 用户 '{username}' 注册成功")
print(f" OPRF 密钥: {oprf_key.hex()[:16]}...")
print(f" 信封: {envelope.hex()[:16]}...")
print(f" 客户端公钥: {client_pk.hex()[:16]}...")
return client_sk, record
# ── 认证流程 ───────────────────────────────────────────────
def authentication(username: str, password: str, record: ServerRecord):
"""
OPAQUE 认证流程(简化版)。
返回协商的会话密钥,若密码错误则返回 None。
"""
# 阶段一:OPRF 重新求值
rwd = oprf_client(password, record.oprf_key)
# 阶段二:解密信封,恢复客户端私钥
envelope_key = H(b"envelope", rwd)
recovered_sk = xor_bytes(record.envelope, envelope_key)
recovered_pk = H(b"pk", recovered_sk)
# 验证恢复的公钥是否与注册时一致
if not hmac.compare_digest(recovered_pk, record.client_pubkey):
print(f"[认证] 用户 '{username}' 密码错误,认证失败")
return None
# 阶段三:AKE 密钥协商(极度简化)
# 真实协议使用 3DH 或类似方案;此处仅演示概念
client_ephemeral = os.urandom(32)
server_ephemeral = os.urandom(32)
session_key = H(
b"session",
recovered_sk, # 客户端长期私钥
record.server_sk, # 服务器长期私钥(真实中不直接共享)
client_ephemeral,
server_ephemeral,
)
print(f"[认证] 用户 '{username}' 认证成功")
print(f" 会话密钥: {session_key.hex()[:16]}...")
return session_key
# ── 演示 ───────────────────────────────────────────────────
if __name__ == "__main__":
print("=" * 56)
print("OPAQUE 协议简化演示(仅用于说明逻辑流程)")
print("=" * 56)
# 注册
sk, record = registration("alice", "correct-password")
print()
# 正确密码认证
key1 = authentication("alice", "correct-password", record)
print()
# 错误密码认证
key2 = authentication("alice", "wrong-password", record)在上述代码中,注册阶段通过 OPRF 将低熵密码提升为高熵的 rwd,然后用 rwd 加密客户端私钥形成信封。认证阶段重新执行 OPRF 得到 rwd,解密信封恢复私钥,再利用私钥完成 AKE 密钥协商。如果密码错误,rwd 不同,解密出的私钥也不同,公钥验证将失败。整个过程中,服务器从未接触到密码明文。
八、安全性分析
OPAQUE 的安全性可以从三个维度进行深入分析。
离线字典攻击抵抗。 这是 PAKE 最核心的安全目标。在 OPAQUE 中,窃听者观察到的只是 OPRF 交互中的致盲群元素和 AKE 阶段的临时公钥——这些都是随机值,不包含密码的任何信息。主动攻击者要验证一个密码猜测,必须与真实的服务器完成一次完整的 OPRF 交互,而服务器可以轻松实施速率限制(Rate Limiting)。OPAQUE 的安全性证明表明:在每个协议会话中,攻击者最多只能测试一个密码猜测。这是信息论意义上最优的——没有任何协议能做得更好。
服务器妥协安全。 假设攻击者完全控制了服务器,获取了所有存储的数据:OPRF 密钥 kU、加密的信封、客户端公钥。攻击者能做什么?首先,攻击者无法直接冒充用户,因为信封中的客户端私钥是用 rwd 加密的,而 rwd 依赖于密码和 OPRF 密钥的组合。攻击者必须逐一猜测密码 pw’,计算 F(kU, pw’),尝试解密信封,验证解密出的私钥是否与存储的公钥匹配。这就是一次离线字典攻击,但其代价取决于 OPRF 底层运算的成本。如果在 OPRF 输出之上还应用了 Argon2 等慢哈希函数(这在 OPAQUE 的配置中是推荐做法),那么每次密码猜测的计算成本将非常高昂。与传统的密码哈希存储方案相比,OPAQUE 提供了额外的防线:攻击者不仅需要破解慢哈希,还需要逆向 OPRF。
预计算抵抗。 在传统方案中,如果多个用户使用相同密码,攻击者可以一次破解、多次获益。OPAQUE 通过为每个用户分配独立的 OPRF 密钥 kU 来彻底杜绝这种攻击。即使两个用户使用完全相同的密码,由于 OPRF 密钥不同,他们的 rwd 也不同,加密信封也不同。攻击者必须对每个用户独立发起字典攻击,无法共享计算结果。这在效果上等价于(甚至优于)传统的加盐机制,因为 OPRF 密钥的空间远大于通常的盐值空间。
此外,OPAQUE 还天然抵抗钓鱼攻击(Phishing Resistance)。在传统的密码认证中,用户可能被诱骗将密码提交给伪造的服务器。而在 OPAQUE 中,AKE 阶段绑定了服务器的长期公钥——客户端会验证与之交互的确实是注册时的那个服务器。如果攻击者运行一个假服务器,AKE 握手将失败,密码永远不会被暴露。
九、PAKE 部署实践
从学术论文到工业部署,PAKE 协议正在经历一个加速落地的阶段。
Apple。 Apple 是 SRP 最大的工业用户之一。iCloud Keychain 的同步机制和 Apple ID 的认证流程长期使用 SRP-6a。近年来,随着 OPAQUE 标准化的推进,Apple 的安全团队也在评估向更现代的协议迁移的路径。Apple 的 HomeKit 配件配对协议则使用了 SRP 的变体来在本地网络上安全配对设备。
Zoom。 2023 年,Zoom 宣布在其端到端加密会议产品中部署 OPAQUE 协议,用于通过密码认证会议参与者。这是 OPAQUE 在大规模消费产品中最引人注目的部署案例之一。Zoom 的方案允许用户通过密码加入端到端加密会议,而 Zoom 的服务器无法获知会议密码,从而确保了真正的端到端安全。
Signal。 Signal 在其安全值存储(Secure Value Recovery,SVR)功能中使用 OPRF 来保护用户的 PIN 码。用户的 PIN 通过 OPRF 与 Signal 服务器交互后,派生出加密密钥,用于加密存储在服务器上的个人数据(如联系人列表)。Signal 的 Intel SGX 硬件飞地进一步限制了 OPRF 密钥的暴露面,即使服务器操作系统被攻破也无法直接获取 OPRF 密钥。
CPace 与 IETF 标准化。 除了 OPAQUE,IETF 还标准化了另一个 PAKE 协议——CPace(Composable PAKE),这是一个平衡 PAKE,特别适合设备配对等对等场景。CPace 的设计非常紧凑,核心只需要一次群运算,适合资源受限的嵌入式设备。Matter 智能家居标准选择了 SPAKE2 作为设备入网协议,而 Wi-Fi Alliance 的某些配置流程也在评估 PAKE 方案。
标准化现状。 截至目前,IETF 已经标准化了以下 PAKE 相关的 RFC:RFC 9497(OPRF/VOPRF/POPRF)、RFC 9382(SPAKE2/SPAKE2+)、RFC 9807(OPAQUE)。此外,CFRG(Crypto Forum Research Group)还在持续评估 CPace 等协议的标准化。PAKE 的标准化标志着这类协议已从纯学术研究阶段进入工业级部署阶段。可以预见,在未来几年内,越来越多的应用将采用 OPAQUE 或类似协议来替代传统的”发送密码到服务器”的认证模式。
PAKE 协议对比
在实际选型中,SRP、SPAKE2 和 OPAQUE 各有其适用场景和局限。以下表格从多个关键维度进行对比:
| 维度 | SRP(SRP-6a) | SPAKE2 / SPAKE2+ | OPAQUE |
|---|---|---|---|
| 服务器存储 | 验证者 v = g^x mod N + 盐值 | 密码标量 w(SPAKE2)或 (wG, w·pG)(SPAKE2+) | 加密信封 + OPRF 密钥 + 公钥 |
| 交互轮次 | 2 轮 | 1 轮(单消息交换) | 3 条消息(可流水线为 1 轮 + 确认) |
| 专利状态 | Stanford 非排他许可,实践中无障碍 | 无已知专利 | 无已知专利 |
| 部署成熟度 | 高——Apple iCloud、1Password 等 | 中——Google Titan 密钥、Matter 协议 | 新兴——Zoom E2EE、RFC 9807(2025) |
| 预计算抵抗 | 依赖盐值 | 依赖密码到标量的映射 | 强——每用户独立 OPRF 密钥 |
| 安全证明 | 无标准模型证明,启发式安全 | ROM 下可证明(SPAKE2);SPAKE2+ 增强属性有争议 | 标准模型 + ROM 下均有严格证明 |
| 群支持 | 仅有限域 DH | 椭圆曲线原生支持 | 椭圆曲线原生支持 |
| PAKE 类型 | 增强(Augmented) | 平衡(SPAKE2)/ 增强(SPAKE2+) | 增强(Augmented) |
密码或许不会消失——它太方便、太普遍了。但密码认证的方式正在从”信任服务器不会泄露我的密码”演进为”即使服务器被攻破,我的密码也是安全的”。从 SRP 到 SPAKE2,再到 OPAQUE,每一代协议都在安全性、效率和可证明性上取得了显著进步。OPAQUE 的标准化和工业部署意味着,在不远的将来,我们的密码将在一个真正”不经意”的世界中被使用——服务器帮我们完成认证,却对我们的密码一无所知。
密码学百科系列 · 第 24 篇