cainepavl/DKIM_bh_ID
GitHub: cainepavl/DKIM_bh_ID
基于Python和RFC 6376标准的DKIM签名验证工具,用于检验邮件正文哈希和RSA签名的完整性与真实性。
Stars: 0 | Forks: 0
自从 Ralphie 解码出那句“喝你的 Ovaltine”,以及 Cap'n Crunch 把解码戒指放进他的麦片盒里以来,我就对密码产生了浓厚的兴趣。当我得知一封邮件里竟然藏着一个 body hash(正文哈希)时,我立刻被吸引了——而且解码说明也就藏在里面:算法 (`rsa-sha256`)、规范化方法 (`relaxed/relaxed`),以及用于计算哈希的确切 header 字段。这就像在同一个麦片盒里找到了解码戒指和密信!我到处寻找,却一直找不到关于如何自己动手验证它的好教程——直到我遇见了我的朋友 Claude……
于是我们打造了一枚解码戒指——一枚能读懂 RFC 6376 的解码戒指。
# 🕵️ DKIM Body Hash (bh=) 完整性验证工具
这是一个用于在原始 `.eml` 文件上全面验证 **DKIM (DomainKeys Identified Mail)** 签名的 Python 工具,实现了 [RFC 6376](https://www.rfc-editor.org/rfc/rfc6376) 标准。导入任意一封邮件,它就会通过实时的 DNS 密钥查找,同时验证 body hash (`bh=`) 和 RSA header 签名 (`b=`) —— 无需手动提取 header。
[](https://www.python.org/)
[](LICENSE)
[](https://www.linux.org/)
[](https://www.rfc-editor.org/rfc/rfc6376)
## 📋 目录
- [密码](#the-secret-code)
- [项目结构](#project-structure)
- [前置条件](#prerequisites)
- [如何使用](#how-to-use)
- [逐步解码演练](#step-by-step-decode-walkthrough)
- [隐私与安全](#privacy--safety)
- [工作流与实现](#workflow--implementation)
- [测试](#testing)
- [许可证](#license)
- [联系与交流](#contactconnect)
## 💡 密码
你收到的每一封邮件都带有一个隐藏的签名。发送邮件的服务器会在投递前对其进行签名,而任何收件人——或任何有足够好奇心去查看的人——都可以验证它。这个标准就是 DKIM。
**Body hash (`bh=`)** 是第一层。邮件正文会经过一套称为 *relaxed 规范化* 的特定流程处理:折叠空白字符,去除每一行尾部的空格,统一换行符,修剪尾部的空行,最后添加一个换行符。用 SHA-256 对结果进行哈希处理,再进行 base64 编码,你就得到了邮件正文的指纹。这个指纹就直接印在 `DKIM-Signature` header 中——所以操作说明*和*答案都装在同一个信封里。这就是那个“喝你的 Ovaltine”时刻:自己按照流程操作,看看你的答案是否匹配。
**为什么 CRLF 是隐藏的陷阱。** 邮件要求使用 `\r\n` 作为换行符,但大多数工具在保存文件时会默默地将其转换为 `\n`。如果这一步弄错了,无论你的计算多么正确,哈希值永远无法匹配——规范不会给你任何错误提示,只会给你一个无声的 FAIL。一旦你知道需要留意这一点,就会迎来那种让整个谜题豁然开朗的“顿悟”时刻。
**完整签名 (`b=`)** 则更进一步。除了正文之外,一组关键的 header(如 `From`、`Subject`、`Date` 等)也会使用 RSA 进行签名。公钥存放在 `selector._domainkey.domain` 的 DNS TXT 记录中——对任何人都是公开可用的,就像印在麦片盒侧面的解码戒指说明书一样。获取该公钥并验证签名,你就能证明这封邮件确实来自它所声称的域名。
## 🗂️ 项目结构
| 文件 | 用途 |
|---|---|
| `dkim_verifier.py` | 核心库 + CLI。所有的验证逻辑都在这里。 |
| `gui.py` | tkinter GUI —— 文件选择器、主题化结果显示、亮色/暗色切换。 |
| `requirements.txt` | `dnspython`, `cryptography` |
| `verify_bh.py` | 最初的纯 body hash 脚本 —— 作为展示手动处理方式的参考产物保留。 |
| `test_dkim_verifier.py` | 针对 `dkim_verifier.py` 的单元测试和集成测试(41 个测试,无外部网络调用)。 |
| `screenshots/` | 本 README 中使用的 GUI 截图。 |
## ⬇️ 前置条件
```
Python 3.x
pip3 install dnspython cryptography
# tkinter (仅限 GUI)
sudo dnf install python3-tkinter # Fedora / RHEL
sudo apt install python3-tk # Debian / Ubuntu
```
## 🐧 WSL (Windows Subsystem for Linux)
在 WSL 下运行 GUI 模式需要显示后端支持。
**Windows 11 — 带有 WSLg 的 WSL 2(推荐)**
WSLg 内置于 Windows 11(21H2 及更高版本)中,可自动渲染 GUI 应用。无需额外的显示配置 —— 只需像平常一样运行 `python3 gui.py` 即可。
**Windows 10 — 不带 WSLg 的 WSL 2**
在 Windows 端安装一个 X server(例如 [VcXsrv](https://sourceforge.net/projects/vcxsrv/)),启动时勾选“Disable access control”,然后在运行前设置显示变量:
```
export DISPLAY=$(grep nameserver /etc/resolv.conf | awk '{print $2}'):0.0
python3 gui.py
```
CLI 模式 (`python3 dkim_verifier.py`) 没有 GUI 依赖,无需任何显示配置即可在 WSL 中运行。
## 🚀 如何使用
### GUI(推荐)
```
python3 gui.py
```
点击 **Load .eml File** 并选择任意导出的电子邮件。body hash 结果会立即显示(无需网络)。完整的 RSA 签名检查会在 DNS 查找之后进行——通常需要一两秒钟。
点击右上角的 **DARK** 开关即可切换赛博朋克主题。
### CLI
```
python3 dkim_verifier.py "my_email.eml"
```
### 获取 .eml 文件
此工具适用于任何带有 `DKIM-Signature` header 的电子邮件 —— Gmail、Outlook、Yahoo、SendGrid、Mailchimp 以及大多数其他现代邮件服务提供商都会对其外发邮件进行签名。
| 客户端 | 导出方法 |
|---|---|
| Gmail | ⋮ 菜单 → **Download message** |
| Outlook (网页版) | ⋮ 菜单 → **Save as** |
| Thunderbird | 右键点击邮件 → **Save As** |
| Apple Mail | **File** → **Save As** → `.eml` |
有关涵盖数十种客户端(包括 Outlook 2003–2016、ProtonMail、Zoho、Exchange Online 等)的全面指南,请参阅 **[MXToolbox Email Headers Guide](https://mxtoolbox.com/public/content/emailheaders/)** *(致谢:MXToolbox)*。
## 🔑 逐步解码演练
以下是该工具以解码戒指的方式破解真实电子邮件的过程。下面的示例取自一封由 Outlook 签名的邮件。
**1. 找到戒指**
打开邮件并找到 `DKIM-Signature:` header。你需要的一切都印在那里:
```
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=outlook.com;
s=selector1;
h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:...;
bh=2Xz66rWfrVgWAeWuVgoYF3IZCwn50UywwNAlxj/m+Gc=;
b=f27A6Pa2...
```
算法 (`a=`)、规范化流程 (`c=`)、已签名的 header (`h=`)、预期的 body hash (`bh=`),以及获取公钥的位置 (`d=` + `s=`) —— 全部一目了然。
**2. 遵循流程**
对邮件正文应用 relaxed 规范化:
- 将空格/制表符序列折叠为单个空格
- 去除每一行尾部的空白字符
- 将所有换行符统一为 `\r\n`
- 去除正文尾部的空行
- 精确添加一个尾部的 `\r\n`
**3. 哈希处理**
对规范化后的正文进行 SHA-256 哈希处理,并将结果进行 base64 编码。如果它与 `bh=` 匹配,说明邮件正文与发件人撰写的内容完全一致。
**4. 获取密码本**
在 `selector1._domainkey.outlook.com` 进行 DNS TXT 查找,返回 RSA 公钥 —— 即 `p=` 字段。这是公开信息,互联网上的任何人都可以获取。
**5. 验证签名**
对 `h=` 中列出的 header 进行规范化,附加 DKIM-Signature header 本身(将 `b=` 清空),然后使用 RSA-PKCS1v15 + SHA-256 验证 `b=` 值与公钥是否匹配。
**结果:**
```
Body Hash (bh=): PASS
Expected: 2Xz66rWfrVgWAeWuVgoYF3IZCwn50UywwNAlxj/m+Gc=
Computed: 2Xz66rWfrVgWAeWuVgoYF3IZCwn50UywwNAlxj/m+Gc=
Signature (b=): PASS
Key fetched from selector1._domainkey.outlook.com
```
## 🔒 隐私与安全
- `.eml` 文件仅在本地读取,绝不会传输到任何地方。
- 唯一的网络调用是为了获取签名域公钥而进行的 DNS TXT 查找 —— 这与每台接收邮件服务器在投递邮件时自动执行的查找完全相同。
- 该查询仅会暴露签名域和 selector,而这两者本身就已经在 `DKIM-Signature` header 中公开了。
## 🔧 工作流与实现
### 解析
`dkim_verifier.py` 以二进制方式读取 `.eml` 文件,通过找到第一个空行来分隔 header 与正文(同时处理 `\r\n` 和 `\n` 换行符),然后遍历 header 块以查找并展开 `DKIM-Signature` header。无需手动使用 `grep` 或 `tail`。
最初的 `verify_bh.py` 记录了手动处理方式以供参考 —— 如果你想在添加自动化之前了解工具底层的工作原理,它会非常有用。
### Relaxed 规范化(正文)
根据 RFC 6376 §3.4.3:
1. 将所有空白字符序列折叠为单个空格
2. 去除每一行尾部的空白字符
3. 去除正文尾部的空行
4. 精确添加一个尾部的 `\r\n`
5. SHA-256 → base64 → 与 `bh=` 进行比较
### Relaxed 规范化(headers)
根据 RFC 6376 §3.4.2,对于 `h=` 中指定的每个 header:
1. 将 header 名称转换为小写
2. 展开续行 (CRLF+WSP → 单个空格)
3. 折叠空白字符序列
4. 去除冒号周围和值尾部的空白字符
5. 输出 `lowercasename:value\r\n`
DKIM-Signature header 在最后追加,并将 `b=` 值清空,且没有尾部的 CRLF。
### 签名验证
从 DNS 中获取 RSA 公钥 (`selector._domainkey.domain` TXT 记录中的 `p=` 字段),使用 `cryptography` 库进行加载,并通过 RSA-PKCS1v15 + SHA-256 用于验证 `b=` 值。
## 🧪 测试
测试套件涵盖了 `dkim_verifier.py` 中的所有核心逻辑 —— 解析、规范化、哈希处理、RSA 签名验证,以及完整的端到端 `verify_email` 集成(通过 mock DNS,因此无需任何网络调用)。
```
python3 -m unittest test_dkim_verifier -v
```
**跨越 8 个测试类的 41 个测试:**
| 类 | 覆盖范围 |
|---|---|
| `TestParseEml` | CRLF/LF 分隔符、空正文、缺少分隔符错误 |
| `TestSplitHeaders` | 简单和折叠(制表符/空格续行)header |
| `TestParseDkimSignature` | 所有标签解析、折叠 header、不区分大小写匹配、缺少 header 错误 |
| `TestCanonicalizeHeader` | 转换为小写、折叠空白字符、展开、去除空白字符 |
| `TestCanonicalizeBodyRelaxed` | 所有 RFC 6376 §3.4.3 规则 —— CRLF 规范化、空白字符、尾部空行 |
| `TestVerifyBodyHash` | SHA-256 和 SHA-1 路径、匹配/不匹配、篡改检测 |
| `TestVerifySignature` | 有效 RSA 签名、无效签名、被篡改的数据、`b=` 值中的空白字符 |
| `TestVerifyEmail` | 全面集成:`bh=` + `b=` 通过,`bh=` 错误,DNS 失败,缺少 DKIM header |
`verify_bh.py` 是一个硬编码的参考产物,没有可导入的函数;其逻辑已通过 `dkim_verifier.py` 的测试得到了充分验证。tkinter GUI (`gui.py`) 需要实时显示支持,因此未涵盖在自动化测试中。
## 📄 许可证
本项目基于 MIT 许可证授权 —— 详情请参阅 [LICENSE](LICENSE) 文件。
## 📩 联系与交流
**Caine Pavlosky**
* 邮箱:[cainepavl@outlook.com](mailto:cainepavl@outlook.com)
* 作品集:[fairdinkumstudios.com](https://fairdinkumstudios.com/)
* LinkedIn:[linkedin.com/in/cainepavlosky008](https://linkedin.com/in/cainepavlosky008)
| Light Mode | Dark Mode |
![]() |
![]() |
标签:DKIM, Python, RFC 6376, 密码学, 手动系统调用, 数字取证, 无后门, 自动化脚本, 逆向工具, 邮件验证

