Python邮件服务中dkimpy生成的DKIM签名验证失败问题求助
核心问题:To头添加时机错误
你在签名完成后才添加To头,但签名配置的include_headers包含了To字段。这会导致:
- 签名生成时,邮件中还没有
To头,签名的哈希计算不包含该字段 - 最终发送的邮件带有
To头,验证方会检查该字段是否被签名,发现签名与实际内容不匹配,直接判定DKIM失败
修复方案及潜在问题排查
1. 调整To头设置顺序
将To头的设置移到签名之前,确保签名时包含所有指定的头字段:
def sendTest(self, target): self.mail_tests = MIMEText("YO") self.mail_tests['Subject'] = "TEST" self.mail_tests['From'] = "NAME <" + self.address + ">" self.mail_tests['Message-ID'] = email.utils.make_msgid(domain='DOMAIN.TLD') # 先设置To头,再执行签名 self.mail_tests['To'] = target self.signMail(self.mail_tests) self.sendMail(self.mail_tests.as_bytes(), target)
2. 避免格式转换破坏签名
使用as_string()可能会自动修改邮件的换行格式(如将\n转为\r\n),破坏签名的哈希一致性。建议直接发送as_bytes()生成的原始字节内容:
def sendMail(self, content_bytes, target): try: self.smtp.sendmail(self.address, target, content_bytes) print("sended", content_bytes.decode()) except smtplib.SMTPException as e: print(f"Error sending a mail : {e}")
3. 统一域名格式
签名时使用的域名是DOMAIN.tld,但邮件和DNS记录中是DOMAIN.TLD。虽然DKIM对域名大小写不敏感,但统一格式可避免潜在解析问题:
sig = dkim.sign( message=mail.as_bytes(), selector="SELECTOR".encode(), domain="DOMAIN.TLD".encode(), # 与DNS记录保持一致 privkey=self.private.encode(), include_headers=headers, )
4. 验证私钥有效性
确保私钥是标准PEM格式,包含完整的首尾标记(-----BEGIN RSA PRIVATE KEY-----和-----END RSA PRIVATE KEY-----),无多余空格或换行错误。
验证修复效果
修复后可通过以下方式确认:
- 对比邮件
bh字段与正文的SHA256哈希(注意保持正文换行符与签名时一致) - 使用离线DKIM验证工具检查签名有效性
内容的提问来源于stack exchange,提问作者lou habert
相关产品推荐
相关产品推荐

