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。 [![Python](https://img.shields.io/badge/Python-3.x-blue?logo=python&logoColor=white)](https://www.python.org/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Platform](https://img.shields.io/badge/Platform-Linux-lightgrey?logo=linux&logoColor=white)](https://www.linux.org/) [![RFC 6376](https://img.shields.io/badge/Standard-RFC%206376-blue)](https://www.rfc-editor.org/rfc/rfc6376)
Light Mode Dark Mode
DKIM Verifier — light mode DKIM Verifier — dark/cyber mode
## 📋 目录 - [密码](#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)
标签:DKIM, Python, RFC 6376, 密码学, 手动系统调用, 数字取证, 无后门, 自动化脚本, 逆向工具, 邮件验证