Skip to main content

5. Signer Actions (签名者操作)

5. Signer Actions (签名者操作)

Signer 按顺序执行以下步骤。

5.1. Determine Whether the Email Should Be Signed and by Whom (确定电子邮件是否应被签名以及由谁签名)

Signer 显然只能为其拥有私钥以及相应公钥和选择器信息的必要知识的域签名电子邮件。然而, 除了缺少私钥之外, Signer 可能选择不签名电子邮件还有许多其他原因。

信息性注释: Signer 可以作为邮件系统的任何部分来实现, 包括 MUA, SUBMISSION 服务器或 MTA。无论在何处实现, Signer 都应注意不要签名 (从而声明对其负责) 可能存在问题的消息。特别是, 在受信任的范围内, 签名域可能根据本地策略从头派生; SUBMISSION 服务器可能只签名来自经过适当身份验证和授权的用户的消息。

信息性实施者建议: 如果出站网关 MTA 混淆 Received 头字段, 例如为了隐藏内部拓扑的详细信息, SUBMISSION 服务器不应签名 Received 头字段。

如果由于某种原因无法签名电子邮件, 则如何处理该电子邮件是本地策略决定。

5.2. Select a Private Key and Corresponding Selector Information (选择私钥和相应的选择器信息)

本规范未定义 Signer 应根据哪些依据选择要使用的私钥和选择器信息。目前, 就本规范而言, 所有选择器都是平等的, 因此该决定主要应是管理便利性的问题。私钥的分发和管理也超出了本文档的范围。

信息性操作建议: 当包含相应公钥的选择器预计在 Verifier 有机会验证签名之前被撤销或删除时, Signer 不应使用私钥进行签名。Signer 应预期 Verifier 可以选择推迟验证, 可能直到消息实际被最终收件人阅读时。特别是, 在轮换到新密钥对时, 应立即使用新私钥开始签名, 并且在从密钥服务器中删除之前, 应保留旧公钥一段合理的验证间隔。

5.3. Normalize the Message to Prevent Transport Conversions (规范化消息以防止传输转换)

某些消息, 特别是使用 8 位字符的消息, 在传输过程中会被修改, 特别是转换为 7 位形式。此类转换将破坏 DKIM 签名。为了最大限度地减少此类破坏的机会, Signer 应在签名之前将消息转换为合适的 MIME 内容传输编码, 例如 [RFC2045] 中描述的 quoted-printable 或 base64。此类转换超出了 DKIM 的范围; 在呈现给 DKIM 算法之前, 实际消息应由 MUA 或 MSA 转换为 7 位 MIME。

如果将消息提交给 Signer 时使用的任何本地编码将在传输之前被修改, 则必须在签名之前完成对规范 [RFC5322] 形式的修改。特别是, 裸 CR 或 LF 字符 (某些系统用作本地行分隔符约定) 必须在签名消息之前转换为 SMTP 标准 CRLF 序列。任何此类转换都应应用于实际发送给收件人的消息, 而不仅仅是呈现给签名算法的版本。

更一般地说, Signer 必须按 Verifier 预期接收的方式签名消息, 而不是以某种本地或内部形式。

5.3.1. Body Length Limits (正文长度限制)

可以指定正文长度计数以将签名计算限制为正文文本的初始前缀, 以八位字节为单位。如果未指定正文长度计数, 则签名整个消息正文。

信息性理由: 提供此功能是因为邮件列表向消息添加尾部信息 (例如, 有关如何退出列表的说明) 是非常常见的。在这些消息也被签名之前, 正文长度计数对于 Verifier 来说是一个有用的工具, 因为它可以作为策略问题接受具有有效签名和无关数据的消息。

实际哈希的长度应插入到 DKIM-Signature 头字段的 "l=" 标签中。(参见 Section 3.5。)

正文长度计数允许消息的 Signer 允许将数据追加到已签名消息正文的末尾。正文长度计数必须在规范化算法之后计算; 例如, 规范化算法忽略的任何空白都不包括在正文长度计数中。

正文长度计数为零意味着正文完全未签名。

希望确保不会发生任何类型的修改的 Signer 应为头和正文都指定 "simple" 规范化算法, 并省略正文长度计数。

有关进一步讨论, 请参阅 Section 8.2。

5.4. Determine the Header Fields to Sign (确定要签名的头字段)

From 头字段必须被签名 (即, 包含在生成的 DKIM-Signature 头字段的 "h=" 标签中)。Signer 不应签名可能在传输过程中合法修改或删除的现有头字段。特别是, [RFC5321] 明确允许在传输过程中修改或删除 Return-Path 头字段。Signer 可以根据 Signer 的判断包括在签名时存在的任何其他头字段。

信息性操作注释: 选择要签名的头字段并不明显。一种策略是签名所有现有的不可重复头字段。另一种策略是仅签名可能向收件人显示或以其他方式可能影响收件人处理消息的头字段。第三种策略是仅签名 "众所周知" 的头。请注意, Verifier 可能对未签名的头字段持极端怀疑态度, 包括拒绝向最终用户显示它们, 或者如果签名不覆盖某些头字段, 甚至忽略签名。因此, 强烈建议签名消息中存在的字段, 例如 Date, Subject, Reply-To, Sender 和所有 MIME 头字段。

DKIM-Signature 头字段始终隐式签名, 除了表示其他预先存在的签名也被签名外, 不得包含在 "h=" 标签中。

Signer 可以声称已签名不存在的头字段 (即, 即使该头字段在消息中不存在, Signer 也可以在 "h=" 标签中包含头字段名称)。在计算签名时, 不存在的头字段必须被视为空字符串 (包括头字段名称, 头字段值, 所有标点符号和尾随 CRLF)。

信息性理由: 这允许 Signer 明确声明头字段的缺失; 如果稍后添加该头字段, 签名将失败。

信息性注释: 头字段名称只需比签名时消息中该头字段的实际数量多列出一次, 即可防止任何进一步的添加。例如, 如果在签名时有一个 Comments 头字段, 则在 "h=" 标签中列出 Comments 两次就足以防止追加任意数量的 Comments 头字段; 没有必要 (但合法) 在 "h=" 标签中列出 Comments 三次或更多次。

有关在规范化具有特定头字段名称的多个实例的头时要遵循的过程的讨论, 请参阅 Section 5.4.2。

Signer 需要小心签名可能在传递过程中稍后添加其他实例的头字段, 因为此类头字段可能在已签名实例之后插入或以其他方式重新排序。跟踪头字段 (例如 Received) 和 Resent-* 块是 [RFC5322] 禁止重新排序的唯一字段。特别是, 由于某些中间 MTA 可能会重新排序 DKIM-Signature 头字段, 因此签名现有 DKIM-Signature 头字段容易出错。

信息性警告: 尽管 [RFC5322] 不禁止重新排序头字段, 但中间 MTA 重新排序具有多个实例的已签名头字段将导致 DKIM 签名被破坏; 应避免此类反社会行为。

信息性实施者注释: 虽然本规范不要求, 但应签名所有最终用户可见的头字段, 以避免可能的 "间接垃圾邮件"。例如, 如果未签名 Subject 头字段, 垃圾邮件发送者可以重新发送先前签名的邮件, 用单行垃圾邮件替换合法主题。

DKIM 加密算法的目的是以既对正常传输相关更改具有鲁棒性又能抵抗各种重放攻击的方式向消息附加标识符。满足这些要求的一个重要方面是选择在哈希中包含哪些头字段以及排除哪些字段。

选择要包含的字段的基本规则是选择构成消息内容 "核心" 的那些字段。因此, 任何重放攻击都必须包含这些字段才能使签名成功; 然而, 包含这些字段后, 消息的核心是有效的, 即使发送给新收件人也是如此。

具有地址的字段和具有与正文相关的文本内容的字段的常见示例有:

  • From (必需; 参见 Section 5.4)
  • Reply-To
  • Subject
  • Date
  • To, Cc
  • Resent-Date, Resent-From, Resent-To, Resent-Cc
  • In-Reply-To, References
  • List-Id, List-Help, List-Unsubscribe, List-Subscribe, List-Post, List-Owner, List-Archive

如果使用 "l=" 签名标签 (参见 Section 3.5), Content-Type 字段也是包含的候选对象, 因为它可能被替换, 从而导致向接收用户呈现完全不同的内容。

在决定什么构成消息的 "核心" 时存在权衡, 对于某些字段来说这是一个主观概念。例如, 如果认为能够区分同一消息的单独实例的机制是核心内容, 则包含诸如 "Message-ID" 之类的字段是有用的。同样, 如果认为消息线程是消息的核心部分, 则可能希望包含 "In-Reply-To" 和 "References"。

可能感兴趣的另一类字段是那些传达有关消息的安全相关信息的字段, 例如 Authentication-Results [RFC5451]。

选择要排除的字段的基本规则是选择具有相同名称的多个字段以及在传输过程中被修改的字段。这些示例有:

  • Return-Path
  • Received
  • Comments, Keywords

请注意, DKIM-Signature 字段也从头哈希中排除, 因为其处理是单独指定的。

通常, 最好排除其他可选字段, 因为在验证之前可能会合法添加或重新排序同名的其他字段。由于可能应用于消息的各种特定于应用程序的头字段种类繁多, 其中一些不太可能被重复, 修改或重新排序, 因此可能存在此规则的合法例外。

Signer 应根据他们处理的消息类型和他们对风险的厌恶程度选择规范化算法。例如, 主要发送购买收据的电子商务站点, 不期望由邮件列表或其他可能修改消息的软件处理, 通常会偏好 "simple" 规范化。

主要发送人对人电子邮件的站点可能更希望通过使用 "relaxed" 规范化在传输过程中对修改更具弹性。

除非邮件通过中介处理, 例如可能在消息正文底部添加 "取消订阅" 说明的邮件列表, 否则 "l=" 标签可能不会传达任何额外的好处, 同时为未经授权向消息添加文本提供了途径。使用 "l=0" 将此推向极端, 允许完全更改消息文本而不会使签名无效。此外, Verifier 有权认为部分签名的消息正文不可接受。建议谨慎使用。

5.4.2. Signatures Involving Multiple Instances of a Field (涉及字段的多个实例的签名)

选择签名消息中多次出现的现有头字段 (例如 Received) 的 Signer 必须签名头块中该头字段的物理上最后一个实例。希望签名此类头字段的多个实例的 Signer 必须在 DKIM-Signature 头字段的 "h=" 标签中多次包含头字段名称, 并且必须按从头字段块底部到顶部的顺序签名此类头字段。Signer 可以在 "h=" 中包含比实际对应头字段更多的头字段名称实例, 以便如果添加了该名称的其他头字段, 则签名将无法验证。

信息性示例:

如果 Signer 希望签名两个现有的 Received 头字段, 并且现有头包含:

Received: <A>
Received: <B>
Received: <C>

则生成的 DKIM-Signature 头字段应为:

DKIM-Signature: ... h=Received : Received :...

并且 Received 头字段 <C> 和 <B> 将按该顺序签名。

5.5. Compute the Message Hash and Signature (计算消息哈希和签名)

Signer 必须按 Section 3.7 中所述计算消息哈希, 然后使用选定的公钥算法对其进行签名。这将产生一个 DKIM-Signature 头字段, 其中将包括正文哈希和头哈希的签名, 其中该头包括 DKIM-Signature 头字段本身。

实现 DKIM 并在重新传输消息之前修改消息或头字段 (例如, 插入取消订阅信息) 的邮件列表管理器等实体应检查输入上的任何现有签名, 并且必须在重新签名消息之前进行此类修改。

5.6. Insert the DKIM-Signature Header Field (插入 DKIM-Signature 头字段)

最后, Signer 必须在传输电子邮件之前插入上一步中创建的 DKIM-Signature 头字段。DKIM-Signature 头字段必须与上述用于计算哈希的字段相同, 除了 "b=" 标签的值必须是上一步中计算的经过适当签名的哈希, 使用 DKIM-Signature 头字段的 "a=" 标签中指定的算法签名, 并使用与 DKIM-Signature 头字段的 "s=" 标签中给出的选择器对应的私钥, 如上述 Section 5.2 中所选择的。

DKIM-Signature 头字段必须插入到头块中任何其他 DKIM-Signature 字段之前。

信息性实现注释: 实现此目的的最简单方法是在头块的开头插入 DKIM-Signature 头字段。特别是, 它可以放置在任何现有 Received 头字段之前。这与将 DKIM-Signature 视为跟踪头字段是一致的。