SMTP 服务器搭建完全指南 / 第 7 章:DKIM 邮件签名
第 7 章:DKIM 邮件签名
DKIM 是邮件世界的数字签名——证明邮件确实来自声称的域名,且未被篡改。
7.1 DKIM 概述
7.1.1 什么是 DKIM
DKIM(DomainKeys Identified Mail)是一种邮件认证技术,通过公钥密码学对邮件进行数字签名。接收方可以通过 DNS 查询发件域名的公钥来验证签名。
核心原理:
发送方 接收方
│ │
│ 1. 生成邮件 │
│ 2. 用私钥对邮件头和正文签名 │
│ 3. 将签名添加到邮件头 (DKIM-Signature) │
│──── 发送邮件 ────────────────────►│
│ │ 4. 从 DKIM-Signature 中提取域名和选择器
│ │ 5. 查询 DNS TXT: selector._domainkey.domain
│ │ 6. 获取公钥
│ │ 7. 用公钥验证签名
│ │ 8. 验证通过 → 邮件可信
7.1.2 DKIM 的作用
| 作用 | 说明 |
|---|---|
| 身份验证 | 证明邮件来自域名持有者授权的服务器 |
| 完整性保护 | 确保邮件在传输过程中未被篡改 |
| 品牌保护 | 防止冒用域名发送钓鱼邮件 |
| 送达率提升 | 通过验证的邮件更少被标记为垃圾 |
7.1.3 DKIM 与其他认证技术的关系
| 技术 | 验证内容 | 与 DKIM 的关系 |
|---|---|---|
| SPF | 发件 IP 是否被授权 | 互补,验证不同维度 |
| DKIM | 邮件签名是否有效 | 核心认证机制 |
| DMARC | SPF/DKIM 与域名对齐 | 策略框架,依赖 SPF 和 DKIM |
7.2 安装 OpenDKIM
7.2.1 安装软件包
# Ubuntu / Debian
sudo apt install -y opendkim opendkim-tools
# RHEL / Rocky Linux
sudo dnf install -y opendkim opendkim-tools
# 检查安装
opendkim -V
7.2.2 创建目录结构
# 创建密钥目录
sudo mkdir -p /etc/opendkim/keys/example.com
# 设置权限
sudo chown -R opendkim:opendkim /etc/opendkim
sudo chmod -R 750 /etc/opendkim/keys
7.3 生成 DKIM 密钥
7.3.1 生成密钥对
# 生成 DKIM 密钥对
# -s: 选择器名称
# -d: 域名
# -b: 密钥长度(2048 位推荐)
sudo opendkim-genkey -s mail -d example.com -b 2048
# 移动密钥文件
sudo mv mail.private /etc/opendkim/keys/example.com/
sudo mv mail.txt /etc/opendkim/keys/example.com/
# 设置密钥权限
sudo chown opendkim:opendkim /etc/opendkim/keys/example.com/mail.private
sudo chmod 600 /etc/opendkim/keys/example.com/mail.private
7.3.2 密钥生成参数说明
| 参数 | 说明 | 推荐值 |
|---|---|---|
-s | 选择器名称 | mail, default, selector1 |
-d | 域名 | 你的邮件域名 |
-b | 密钥位数 | 2048(最低 1024) |
-D | 输出目录 | /etc/opendkim/keys/ |
7.3.3 查看生成的 DNS 记录
# 查看需要添加到 DNS 的 TXT 记录
sudo cat /etc/opendkim/keys/example.com/mail.txt
# 输出示例:
# mail._domainkey IN TXT (
# "v=DKIM1; h=sha256; k=rsa; "
# "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
# "..." )
7.4 配置 OpenDKIM
7.4.1 主配置文件
# /etc/opendkim.conf — OpenDKIM 主配置
# 基础设置
AutoRestart Yes
AutoRestartRate 10/1h
Syslog yes
SyslogSuccess Yes
LogWhy Yes
# 监听地址
Socket inet:8891@localhost
# 或使用 Unix socket:
# Socket local:/var/spool/postfix/opendkim/opendkim.sock
# 用户和组
UserID opendkim:opendkim
# 签名表
SigningTable refile:/etc/opendkim/SigningTable
KeyTable refile:/etc/opendkim/KeyTable
InternalHosts /etc/opendkim/TrustedHosts
# 签名算法
Canonicalization relaxed/simple
Mode sv
SubDomains No
AutoRestart Yes
Background Yes
DNSTimeout 5
SignatureAlgorithm rsa-sha256
# 头部签名
OversignHeaders From
# 忽略的头部
IgnoreMalformedMail Yes
7.4.2 签名表配置
# /etc/opendkim/SigningTable
# 签名表:发件域名 → 密钥选择器
# 单域名
*@example.com mail._domainkey.example.com
# 多域名
*@example.com mail._domainkey.example.com
*@example.org mail._domainkey.example.org
# 使用正则表达式
*@* mail._domainkey.$2
7.4.3 密钥表配置
# /etc/opendkim/KeyTable
# 密钥表:选择器域名 → 密钥文件路径
# 格式:选择器._domainkey.域名 域名:选择器:密钥路径
mail._domainkey.example.com example.com:mail:/etc/opendkim/keys/example.com/mail.private
# 多域名
mail._domainkey.example.com example.com:mail:/etc/opendkim/keys/example.com/mail.private
mail._domainkey.example.org example.org:mail:/etc/opendkim/keys/example.org/mail.private
7.4.4 可信主机配置
# /etc/opendkim/TrustedHosts
# 这些来源的邮件需要签名
127.0.0.1
localhost
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
203.0.113.10/32
# 域名形式
.example.com
7.4.5 设置目录权限
# 确保 OpenDKIM 可以访问密钥和配置
sudo chown -R opendkim:opendkim /etc/opendkim
sudo chmod 640 /etc/opendkim/keys/example.com/mail.private
# 创建 Postfix 可访问的 socket 目录
sudo mkdir -p /var/spool/postfix/opendkim
sudo chown opendkim:postfix /var/spool/postfix/opendkim
7.5 集成 Postfix
7.5.1 配置 Postfix 使用 OpenDKIM
# /etc/postfix/main.cf — DKIM 配置
# 使用 inet socket
milter_default_action = accept
milter_protocol = 6
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891
# 或使用 Unix socket
# smtpd_milters = local:/var/spool/postfix/opendkim/opendkim.sock
# non_smtpd_milters = local:/var/spool/postfix/opendkim/opendkim.sock
7.5.2 启动服务
# 启动 OpenDKIM
sudo systemctl enable --now opendkim
# 重新加载 Postfix
sudo systemctl reload postfix
# 检查 OpenDKIM 状态
sudo systemctl status opendkim
# 查看日志
sudo tail -f /var/log/mail.log | grep -i dkim
7.6 配置 DNS 记录
7.6.1 添加 DKIM TXT 记录
从生成的 mail.txt 文件中提取 DNS 记录内容,添加到 DNS 管理面板:
; DKIM TXT 记录
; 主机记录: mail._domainkey
; 类型: TXT
; 值: 从 mail.txt 文件中提取
mail._domainkey.example.com. IN TXT "v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...(完整公钥)..."
7.6.2 DNS 记录格式说明
| 字段 | 说明 | 示例 |
|---|---|---|
v | DKIM 版本 | DKIM1 |
h | 哈希算法 | sha256 |
k | 密钥类型 | rsa |
s | 服务类型(可选) | email |
t | 标记(可选) | y(测试模式), s(严格子域名) |
p | 公钥(Base64) | MIIBIjAN... |
7.6.3 验证 DNS 记录
# 查询 DKIM DNS 记录
dig TXT mail._domainkey.example.com +short
# 使用 opendkim-testkey 验证
sudo opendkim-testkey -d example.com -s mail -vvv
# 期望输出: key OK
# 使用在线工具
# https://mxtoolbox.com/dkim.aspx
7.7 测试 DKIM 签名
7.7.1 发送测试邮件
# 发送测试邮件
echo "DKIM 测试邮件" | mail -s "DKIM Test" [email protected]
# 使用 swaks 测试
swaks --to test@[email protected] \
--from [email protected] \
--server mail.example.com \
--port 587 \
--auth-user [email protected] \
--auth-password "password" \
-tls
# 检查返回的验证报告
7.7.2 验证 DKIM 签名
# 检查邮件头中的 DKIM-Signature
# 查看收到的邮件原始内容
cat /var/mail/vhosts/example.com/admin/new/* | grep -A20 "DKIM-Signature"
# 使用 opendkim-testmsg
cat test-email.eml | opendkim-testmsg
7.7.3 DKIM 签名字段解读
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple;
d=example.com; s=mail;
h=from:to:subject:date:message-id;
bh=abc123...=; ← 正文哈希
b=xyz789...= ← 签名值
| 字段 | 说明 |
|---|---|
v | 签名版本 |
a | 签名算法 |
c | 规范化方法 |
d | 签名域名 |
s | 选择器 |
h | 签名的头部列表 |
bh | 正文哈希值 |
b | 签名值 |
7.8 多域名 DKIM 配置
7.8.1 为多个域名生成密钥
#!/bin/bash
# gen-dkim-keys.sh — 批量生成 DKIM 密钥
SELECTOR="mail"
KEY_LENGTH=2048
DOMAINS="example.com example.org example.net"
for DOMAIN in $DOMAINS; do
echo "=== 生成 $DOMAIN 的 DKIM 密钥 ==="
# 创建目录
mkdir -p /etc/opendkim/keys/$DOMAIN
# 生成密钥
opendkim-genkey -s $SELECTOR -d $DOMAIN -b $KEY_LENGTH
# 移动文件
mv ${SELECTOR}.private /etc/opendkim/keys/$DOMAIN/
mv ${SELECTOR}.txt /etc/opendkim/keys/$DOMAIN/
# 设置权限
chown -R opendkim:opendkim /etc/opendkim/keys/$DOMAIN
chmod 600 /etc/opendkim/keys/$DOMAIN/${SELECTOR}.private
echo "✅ $DOMAIN 密钥已生成"
echo "DNS 记录:"
cat /etc/opendkim/keys/$DOMAIN/${SELECTOR}.txt
echo ""
done
7.8.2 更新签名表和密钥表
# /etc/opendkim/SigningTable
*@example.com mail._domainkey.example.com
*@example.org mail._domainkey.example.org
*@example.net mail._domainkey.example.net
# /etc/opendkim/KeyTable
mail._domainkey.example.com example.com:mail:/etc/opendkim/keys/example.com/mail.private
mail._domainkey.example.org example.org:mail:/etc/opendkim/keys/example.org/mail.private
mail._domainkey.example.net example.net:mail:/etc/opendkim/keys/example.net/mail.private
# /etc/opendkim/TrustedHosts
127.0.0.1
localhost
.example.com
.example.org
.example.net
7.9 密钥轮换
7.9.1 为什么需要密钥轮换
| 原因 | 说明 |
|---|---|
| 安全最佳实践 | 定期更换密钥降低泄露风险 |
| 合规要求 | 某些安全标准要求定期轮换 |
| 密钥泄露 | 怀疑密钥泄露时需要立即更换 |
7.9.2 密钥轮换步骤
#!/bin/bash
# rotate-dkim-key.sh — DKIM 密钥轮换脚本
DOMAIN="example.com"
OLD_SELECTOR="mail"
NEW_SELECTOR="mail2"
KEY_LENGTH=2048
echo "=== DKIM 密钥轮换 ==="
echo "域名: $DOMAIN"
echo "旧选择器: $OLD_SELECTOR"
echo "新选择器: $NEW_SELECTOR"
# 1. 生成新密钥
echo "[1/4] 生成新密钥..."
mkdir -p /etc/opendkim/keys/$DOMAIN
opendkim-genkey -s $NEW_SELECTOR -d $DOMAIN -b $KEY_LENGTH
mv ${NEW_SELECTOR}.private /etc/opendkim/keys/$DOMAIN/
mv ${NEW_SELECTOR}.txt /etc/opendkim/keys/$DOMAIN/
chown opendkim:opendkim /etc/opendkim/keys/$DOMAIN/${NEW_SELECTOR}.private
chmod 600 /etc/opendkim/keys/$DOMAIN/${NEW_SELECTOR}.private
# 2. 更新配置
echo "[2/4] 更新配置..."
sed -i "s|${OLD_SELECTOR}\._domainkey.${DOMAIN}|${NEW_SELECTOR}._domainkey.${DOMAIN}|g" /etc/opendkim/SigningTable
sed -i "s|${OLD_SELECTOR}\._domainkey.${DOMAIN}|${NEW_SELECTOR}._domainkey.${DOMAIN}|g" /etc/opendkim/KeyTable
# 3. 重载 OpenDKIM
echo "[3/4] 重载服务..."
systemctl reload opendkim
# 4. 提示添加新 DNS 记录
echo "[4/4] 请添加以下 DNS 记录:"
cat /etc/opendkim/keys/$DOMAIN/${NEW_SELECTOR}.txt
echo ""
echo "⚠️ 注意:请先添加新 DNS 记录,等待 DNS 传播完成后再删除旧记录"
echo ""
echo "=== 轮换完成 ==="
7.10 业务场景:SaaS 平台 DKIM 配置
场景描述
一个 SaaS 平台需要为每个租户配置独立的 DKIM 签名。
自动化方案
#!/bin/bash
# saas-dkim-setup.sh — SaaS 平台 DKIM 自动配置
TENANT_DOMAIN="$1"
SELECTOR="app"
if [ -z "$TENANT_DOMAIN" ]; then
echo "用法: $0 <tenant-domain>"
exit 1
fi
# 生成密钥
opendkim-genkey -s $SELECTOR -d $TENANT_DOMAIN -b 2048 -D /etc/opendkim/keys/$TENANT_DOMAIN/
# 更新签名表
echo "*@${TENANT_DOMAIN} ${SELECTOR}._domainkey.${TENANT_DOMAIN}" >> /etc/opendkim/SigningTable
# 更新密钥表
echo "${SELECTOR}._domainkey.${TENANT_DOMAIN} ${TENANT_DOMAIN}:${SELECTOR}:/etc/opendkim/keys/${TENANT_DOMAIN}/${SELECTOR}.private" >> /etc/opendkim/KeyTable
# 更新可信主机
echo ".${TENANT_DOMAIN}" >> /etc/opendkim/TrustedHosts
# 设置权限
chown -R opendkim:opendkim /etc/opendkim/keys/$TENANT_DOMAIN
chmod 600 /etc/opendkim/keys/$TENANT_DOMAIN/${SELECTOR}.private
# 重载服务
systemctl reload opendkim
# 输出 DNS 记录
echo "=== 请为 $TENANT_DOMAIN 添加以下 DNS 记录 ==="
cat /etc/opendkim/keys/$TENANT_DOMAIN/${SELECTOR}.txt
7.11 注意事项
⚠️ 密钥安全:
- 私钥文件权限必须是
600,仅 OpenDKIM 用户可读- 不要将私钥提交到版本控制系统
- 密钥泄露时立即轮换
⚠️ DNS 记录长度:
- 2048 位 RSA 密钥的 DNS TXT 记录较长
- 某些 DNS 提供商对 TXT 记录长度有限制(通常 255 字符)
- 超长记录需要分段:
mail._domainkey.example.com. IN TXT ( "v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkq..." "...HkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..." )
💡 DKIM 测试模式:
- 在 DNS TXT 记录中添加
t=y表示测试模式- 测试模式下,验证失败不会影响邮件投递
- 配置完成后移除
t=y
7.12 扩展阅读
上一章:← 第 6 章:反垃圾邮件策略 下一章:第 8 章:SPF 发件人策略框架 →