5. Signer 动作
5. Signer 动作
Signer 按顺序执行以下步骤.
5.1. 确定电子邮件是否应被签名以及由谁签名
显然, Signer 只能为其拥有私钥并掌握相应公钥和 selector 信息的域签署电子邮件. 但是, 除缺少私钥外, Signer 还可能因为许多其他原因选择不签署某封电子邮件.
INFORMATIVE NOTE: Signer 可以作为邮件系统中任何被认为适当的部分实现, 包括 MUA, SUBMISSION server 或 MTA. 无论实现于何处, Signer 都应谨慎签署可能有问题的消息 (从而声明对其负责). 特别是, 在可信 enclave 内, 签名域可能根据本地策略从 header 派生; SUBMISSION server 可能只签署来自已正确认证并授权的用户的消息.
INFORMATIVE IMPLEMENTER ADVICE: 如果出站网关 MTA 会混淆 Received header field, 例如隐藏内部拓扑细节, 则 SUBMISSION server 不应签署 Received header field.
如果某封电子邮件因某种原因无法签名, 如何处理该电子邮件属于本地策略决定.
5.2. 选择私钥和相应 Selector 信息
本规范不定义 Signer 应基于什么选择要使用的私钥和 selector 信息. 目前, 就本规范而言, 所有 selector 都是等价的, 因此该决定主要应出于管理便利性. 私钥的分发和管理也超出本文档范围.
INFORMATIVE OPERATIONS ADVICE: 如果预计包含相应公钥的 selector 会在 Verifier 有机会验证签名之前被撤销或移除, Signer 不应使用该私钥签名. Signer 应预期 Verifier 可以选择推迟验证, 也许直到最终收件人实际阅读消息时才验证. 特别是在轮换到新 key pair 时, 应立即开始使用新私钥签名, 并在从 key server 移除旧公钥之前保留它一段合理的验证间隔.
5.3. 规范化消息以防止传输转换
某些消息, 特别是使用 8-bit 字符的消息, 在传输期间可能被修改, 尤其是被转换为 7-bit 形式. 此类转换会破坏 DKIM 签名. 为尽量减少这种破坏的可能性, Signer SHOULD 在签名前把消息转换为 [RFC2045] 中描述的适当 MIME content-transfer encoding, 例如 quoted-printable 或 base64. 此类转换超出 DKIM 范围; 实际消息 SHOULD 在提交给 DKIM 算法之前由 MUA 或 MSA 转换为 7-bit MIME.
如果消息提交给 Signer 时采用任何会在传输前被修改的本地编码, 则向规范 [RFC5322] 形式的修改 MUST 在签名前完成. 特别是, 裸 CR 或 LF 字符 (某些系统用作本地行分隔约定) MUST 在消息签名前转换为 SMTP 标准 CRLF 序列. 此类任何转换 SHOULD 应用于实际发送给收件人的消息, 而不仅是提交给签名算法的版本.
更一般地说, Signer MUST 按预期 Verifier 将接收的形式签署消息, 而不是按某种本地或内部形式签署.
5.3.1. 正文长度限制
MAY 指定正文长度计数, 将签名计算限制为正文文本的初始前缀, 以 octet 计量. 如果未指定正文长度计数, 则签署整个消息正文.
INFORMATIVE RATIONALE: 提供此能力是因为邮件列表向消息添加尾注非常常见 (例如如何退订列表的说明). 在这些消息也被签名之前, 正文长度计数对 Verifier 是有用工具, 因为它可以根据策略接受带有有效签名但含有额外数据的消息.
实际被哈希的长度应插入 DKIM-Signature header field 的 "l=" tag. (见 Section 3.5.)
正文长度计数允许消息 Signer 许可数据追加到已签名消息正文末尾. 正文长度计数 MUST 在应用规范化算法后计算; 例如, 被规范化算法忽略的任何空白都不作为正文长度计数的一部分.
正文长度计数为零表示正文完全未签名.
希望确保不会发生任何类型修改的 Signer 应为 header 和正文都指定 "simple" 规范化算法, 并省略正文长度计数.
进一步讨论见 Section 8.2.
5.4. 确定要签名的 Header Field
From header field MUST 被签名 (即包含在生成的 DKIM-Signature header field 的 "h=" tag 中). Signer SHOULD NOT 签署可能在传输中被合法修改或移除的现有 header field. 特别是, [RFC5321] 明确允许在传输中修改或移除 Return-Path header field. Signer MAY 自行决定包含签名时存在的任何其他 header field.
INFORMATIVE OPERATIONS NOTE: 选择哪些 header field 进行签名并不显然. 一种策略是签署所有现有且不可重复的 header field. 另一种策略是只签署可能展示给接收方或可能影响接收方处理消息的 header field. 第三种策略是只签署 "well-known" header. 注意, Verifier 可能极其怀疑未签名 header field, 包括拒绝向最终用户显示它们, 甚至在签名未覆盖某些 header field 时忽略该签名. 因此, 强烈建议签署消息中存在的 Date, Subject, Reply-To, Sender 以及所有 MIME header field.
DKIM-Signature header field 始终被隐式签名, 并且 MUST NOT 包含在 "h=" tag 中, 除非用于指示其他预先存在的签名也被签署.
Signer MAY 声称已签署不存在的 header field (也就是说, 即使该 header field 不存在于消息中, Signer MAY 在 "h=" tag 中包含该 header field 名称). 计算签名时, 不存在的 header field MUST 被视为空字符串 (包括 header field 名称, header field 值, 所有标点以及尾随 CRLF).
INFORMATIVE RATIONALE: 这允许 Signer 明确断言某个 header field 不存在; 如果稍后添加该 header field, 签名将失败.
INFORMATIVE NOTE: 为防止进一步添加某个 header field, 只需在签名时按比消息中该 header field 实际数量多一次的次数列出该 header field 名称. 例如, 如果签名时只有一个 Comments header field, 则在 "h=" tag 中列出 Comments 两次足以防止追加任意数量的 Comments header field; 没有必要 (但合法) 在 "h=" tag 中列出 Comments 三次或更多次.
关于规范化包含某一 header field 名称多个实例的 header 时应遵循的过程, 见 Section 5.4.2.
Signer 需要谨慎签署那些可能在后续投递过程中添加额外实例的 header field, 因为此类 header field 可能插入到已签名实例之后, 或以其他方式重新排序. Trace header field (如 Received) 和 Resent-* 块是 [RFC5322] 唯一禁止重新排序的字段. 特别是, 由于 DKIM-Signature header field 可能被某些中间 MTA 重新排序, 签署现有 DKIM-Signature header field 容易出错.
INFORMATIVE ADMONITION: 尽管 [RFC5322] 不禁止重新排序 header field, 但中间 MTA 对具有多个实例的已签名 header field 重新排序会导致 DKIM 签名被破坏; 应避免此类不友好行为.
INFORMATIVE IMPLEMENTER'S NOTE: 尽管本规范不要求, 但所有最终用户可见的 header field 都应签名, 以避免可能的 "indirect spamming". 例如, 如果 Subject header field 未签名, 垃圾邮件发送者可以重发以前已签名的邮件, 并把合法主题替换为一行垃圾内容.
5.4.1. 推荐的签名内容
DKIM 加密算法的目的, 是以既能抵抗正常传输相关变更又能抵抗各种重放攻击的方式, 为消息附加一个标识符. 满足这些要求的关键方面, 是选择哪些 header field 包含在哈希中以及排除哪些字段.
选择要包含字段的基本规则是选择构成消息内容 "core" 的字段. 因此, 任何重放攻击都必须包含这些字段才能使签名成功; 但是, 只要包含这些字段, 即使消息被继续发送给新收件人, 消息核心也是有效的.
包含地址的字段以及包含与正文相关文本内容的字段的常见示例包括:
- From (REQUIRED; see 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=" 签名 tag (见 Section 3.5), Content-Type 字段也是候选包含项, 因为它可能被替换, 从而导致向接收用户呈现完全不同的内容.
决定什么构成消息 "core" 时存在权衡, 对某些字段而言这是主观概念. 例如, 如果认为能够区分同一消息的不同实例的机制属于核心内容, 则包含 "Message-ID" 等字段是有用的. 类似地, 如果认为消息线程是消息的核心部分, 则可能希望包含 "In-Reply-To" 和 "References".
另一类可能值得关注的字段是传达消息安全相关信息的字段, 例如 Authentication-Results [RFC5451].
选择要排除字段的基本规则是选择那些具有多个同名字段的字段, 以及会在传输中被修改的字段. 示例包括:
- Return-Path
- Received
- Comments, Keywords
注意, DKIM-Signature 字段也排除在 header 哈希之外, 因为它的处理方式另有规定.
通常, 最好排除其他可选字段, 因为验证前可能会合法添加或重新排序同名的额外字段. 鉴于可能应用于消息的应用特定 header field 种类繁多, 此规则很可能存在合法例外, 其中某些字段不太可能被复制, 修改或重新排序.
Signer SHOULD 根据其处理的消息类型及其风险厌恶程度选择规范化算法. 例如, 主要发送购买收据的电子商务站点, 其消息预期不会被邮件列表或其他可能修改消息的软件处理, 通常会偏好 "simple" 规范化.
主要发送个人到个人电子邮件的站点, 可能会偏好使用 "relaxed" 规范化, 以便更能抵抗传输期间的修改.
除非邮件通过中间方处理, 例如可能在消息正文底部添加 "unsubscribe" 说明的邮件列表, 否则 "l=" tag 可能不会带来额外收益, 却会为未经授权地向消息添加文本提供途径. 使用 "l=0" 则把这一点推向极端, 允许完全更改消息文本而不使签名失效. 此外, Verifier 完全有权认为部分签名的消息正文不可接受. 建议审慎使用.
5.4.2. 涉及字段多个实例的签名
选择签署消息中出现多次的现有 header field (如 Received) 的 Signer, MUST 签署 header block 中物理位置最后的该 header field 实例. 希望签署此类 header field 多个实例的 Signer, MUST 在 DKIM-Signature header field 的 "h=" tag 中多次包含该 header field 名称, 并且 MUST 按从 header field block 底部到顶部的顺序签署此类 header field. Signer MAY 在 "h=" 中包含多于实际对应 header field 数量的 header field 名称实例, 使得如果添加了该名称的额外 header field, 签名将无法验证.
INFORMATIVE EXAMPLE:
如果 Signer 希望签署两个现有 Received header field, 且现有 header 包含:
Received: <A>
Received: <B>
Received: <C>则生成的 DKIM-Signature header field 应为:
DKIM-Signature: ... h=Received : Received :...并且 Received header field <C> 和 <B> 会按此顺序被签名.
5.5. 计算消息哈希和签名
Signer MUST 按 Section 3.7 中的描述计算消息哈希, 然后使用选定的公钥算法对其签名. 这会生成一个 DKIM-Signature header field, 其中包含正文哈希以及 header 哈希的签名, 且该 header 包括 DKIM-Signature header field 本身.
实现 DKIM 且在重新传输消息前修改消息或 header field (例如插入退订信息) 的实体, 如邮件列表管理器, SHOULD 在输入时检查任何现有签名, 并且 MUST 在重新签署消息之前完成此类修改.
5.6. 插入 DKIM-Signature Header Field
最后, Signer MUST 在传输电子邮件之前插入上一步创建的 DKIM-Signature header field. DKIM-Signature header field MUST 与上述用于计算哈希的字段相同, 唯一例外是 "b=" tag 的值 MUST 是上一步计算出的已适当签名哈希, 该签名使用 DKIM-Signature header field 的 "a=" tag 指定的算法, 以及与 Section 5.2 中选择的 DKIM-Signature header field 的 "s=" tag 给定 selector 相对应的私钥生成.
DKIM-Signature header field MUST 插入在 header block 中任何其他 DKIM-Signature 字段之前.
INFORMATIVE IMPLEMENTATION NOTE: 实现这一点最简单的方法是在 header block 开头插入 DKIM-Signature header field. 特别是, 它可以放在任何现有 Received header field 之前. 这与把 DKIM-Signature 视为 trace header field 一致.