摘要

  • RFC 9598 规定:本地部分含非 ASCII 字符的邮箱必须以 X.509 otherName 中的 SmtpUTF8Mailbox 表示;本地部分只有 ASCII 字符时仍用 rfc822Name,即使域名是国际化域名。
  • 域名需按 IDNA2008 转为小写 A-label,本地部分则不得做大小写折叠、Unicode 归一化或兼容映射;域名准备完成后,以完整邮箱的 UTF-8 字节逐个比较。
  • 名称匹配只证明一次受限比较成功。证书路径、名称约束、用途、邮箱控制、SMTPUTF8 可达性、本地授权和实际结果都要另留证据。

事故通常不是从密码学开始,而是从一个看似无害的公共函数开始。身份平台已有一套处理用户名的 Unicode“清洗”逻辑:去掉显示语法,统一大小写,执行某种规范化,再把结果作为账号键。团队接入国际化邮箱证书时,自然想复用这套函数。于是证书中的邮箱和用户输入先被变成“干净字符串”,再送给比较器。

这一步把便利变成了权力。两个本地部分可以在人眼看来完全相同,却由不同的 UTF-8 字节组成;也可能只有大小写不同,而邮箱系统从未授权依赖方替它判断二者等价。通用 Unicode 库能够说明字符如何显示、搜索或排序,不能替证书、邮件服务和账号系统共同发明一个主体。

RFC 9598 正文故意不把邮箱地址当作一个可以整体规范化的字符串。它把 @ 两边交给不同的规则:域名有 IDNA2008 的准备契约,本地部分没有通用的等价映射。工程上最重要的结果不是“支持 Unicode”,而是知道必须在哪里停止转换。

同一邮箱命名空间,两种互斥的证书形式

RFC Editor 条目与 IETF Datatracker将 RFC 9598 记录为 2024 年 5 月发布的建议标准。它更新 RFC 5280,并取代 RFC 8398。

传统 rfc822Name 能容纳 ASCII 邮箱,却无法表示国际化邮件中的非 ASCII 本地部分。新规范因此使用 X.509 GeneralName 可扩展的 otherName 分支,定义 SmtpUTF8Mailbox,对象标识符为 1.3.6.1.5.5.7.8.9;IANA SMI Numbers 登记表保存了这一分配。

选择哪一种形式,不取决于整条地址是否“看起来国际化”,而只取决于本地部分。本地部分只要含非 ASCII 字符,就必须使用 SmtpUTF8Mailbox;本地部分全是 ASCII,就必须使用 rfc822Name,哪怕域名需要国际化处理。两种形式共同表示一个邮件地址命名空间,但不能随意互换。

这条分界让资产清单变得具体。只索引 rfc822Name 的扫描器会漏掉国际化本地部分;把 ASCII 本地部分塞进 SmtpUTF8Mailbox 的签发器会产生不符合约定的证书;解析后擅自把一种 GeneralName 类型改写成另一种的网关,则抹掉了签名工件原本携带的证据。

证书里存的是信封邮箱,不是显示地址。短语、注释和尖括号不属于这个身份值。SmtpUTF8Mailbox 是非空 ASN.1 UTF8String,其 UTF-8 编码不得包含字节顺序标记。编码边界来自 RFC 3629,国际化消息头的上下文则见 RFC 6532。

域名可以准备,因为协议已给它准备规则

域名侧遵循 IDNA2008。RFC 5890定义 A-label、U-label 与 LDH 等术语,RFC 5891规定协议转换。RFC 9598 要求 X.509 邮箱中的所有域名符合 IDNA2008,而且不能依靠框架中讨论的额外映射来偷换结果。

在 SmtpUTF8Mailbox 内,非 ASCII 域标签以 A-label 保存,而不是 U-label;ASCII 标签满足 NR-LDH 限制;A-label 与 NR-LDH 中的字母全部小写。这样,证书给出一个稳定的比较形式,路径验证器不必先把证书域名转换回 Unicode 再猜一次。

候选地址如果来自输入框、邮件头或外部目录,比较器可以做有限准备:剥离显示短语、注释和尖括号,把域名中的 U-label 转成 A-label,并把允许的域名字母变成小写。做到这里就必须停手。这个过程只是在协议授权的域名范围内建立规范形式,不是给整条邮箱“美容”。

RFC 9598 相对 RFC 8398 的主要修正也在这里。旧规则在某些条件下允许 A-label 或 U-label,新规则统一要求证书使用 A-label。这不会自动证明所有签发器、路径构建器和应用都已部署新行为,却使每个输出都可以被机器检查,而不是凭视觉判断。

本地部分必须保留,因为没有普遍等价关系

本地部分来自 RFC 6530所述国际化邮件框架,以及 RFC 6531定义的 SMTP 扩展。它使用 UTF-8,但 UTF-8 只是一种编码,不是邮箱供应商共同认可的身份归一化算法。

RFC 9598 对本地部分的要求很直接:不得变换。不能做大小写折叠,不能做 Unicode 规范化,不能做兼容映射。域名准备完成后,整条邮箱按八位组逐个比较。两个已经编码的 SmtpUTF8Mailbox 值甚至不需要准备,字节完全相同才等价。SmtpUTF8Mailbox 与 rfc822Name 则必然不匹配,因为二者对本地部分的字符条件没有重叠。

严格比较保护的是权力边界。归一化库可以帮助搜索,也可以改善显示;它不能被悄悄提升为证书身份裁判。如果证书机构签入一组字节,邮件系统却配置了另一组字节,安全的结果是明确拒绝并调查签发或配置,而不是由依赖方猜测它们“其实是同一个人”。

代价确实存在:用户看到相同名字,验证器却报告不匹配。可是相反做法的代价更大——依赖方本地的库版本、操作系统更新或字符数据库升级,都可能在证书签发后改变“谁与谁相同”。签名保护的名字就会被应用重写,审计也无法重现当时的判断。

名称约束限制签发范围,却不会创造邮箱

rfc822Name 与 SmtpUTF8Mailbox 处于同一个邮箱命名空间。若旧的下级 CA 已通过 rfc822Name 名称约束限定到某个域,新出现的 otherName 不能成为逃离边界的通道。

RFC 9549更新了 PKIX 国际化规则,使 rfc822Name 约束覆盖两种形式。CA 证书使用符合 IDNA2008、以 A-label 表示的 rfc822Name 域约束。比较时先准备主体域名,去掉本地部分,再进行精确主机或域后缀测试;不应使用只针对某一个邮箱的约束。

这回答的是“下级 CA 是否获准在这个域内签发”。它没有证明邮箱存在,没有证明证书主体仍控制邮箱,也没有证明密钥适用于正在请求的操作,更没有要求应用授予访问权。约束成功是签发范围的收据,不是账号所有权证书。

一份可审计的验证记录至少拆成四句:工件包含预期的 GeneralName 类型和确切字节;邮箱按 RFC 9598 算法匹配;证书路径与适用名称约束按既定信任策略通过;应用为这个用途接受了证书。即便四句都成立,也没有证明邮件能通过支持 SMTPUTF8 的完整链路到达。

名称匹配不是投递,更不是授权

SMTPUTF8 的运输能力必须独立观察。RFC 6531 允许 SMTP 节点通告并使用国际化信封地址。证书可以与邮箱名称匹配,而下一跳中继并不支持该扩展;反过来,一封邮件可以在支持 SMTPUTF8 的链路上成功传送,全程无需展示 RFC 9598 证书。

控制权也不能从比较结果推出。证书机构可能按自身实践验证过地址,依赖方仍需确认信任锚、路径、策略、密钥用途或扩展密钥用途、撤销状态、应用绑定与当前权限。签名验证成功时,动作仍可能被拒绝;邮件到达时,也不能证明有人读取;登录成功时,后续变更仍可能失败。

Lu Heng 关于运行代码优先的论述给出可操作的检验:“支持 RFC 9598”不是结果。结果是一条保留证书字节、解析器输出、候选域名准备过程、未变换本地部分、比较结果、证书路径、名称约束、策略决定、动作与影响的轨迹。

最小初始规范说明了这种窄规则为何可持续:共同层只规定名称形式、域名表示和比较算法,不集中控制邮箱开通、CA 取证、应用权限或邮件运营。现实层次则要求把被表示的名字、匹配的名字、有效证书、受控邮箱、获准动作和实际结果看成可关联但不可互换的记录。

领导层真正要取消的,是那盏试图替所有记录说话的绿色“证书有效”灯。

来源