验证DKIM签名(b=部分)的正确方法

6
我正在尝试开发我的家庭邮件服务器(使用NodeJS作为服务器端,但这不重要,因为我试图找出原则)。我使用此文档来指导我完成DKIM签名验证程序,但它需要一些复杂的步骤,我无法弄清楚我的错误在哪里。我使用从Mail.ru服务器发送的电子邮件作为示例。它应该是完全有效的。以下是其标头:
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mail.ru; s=mail2;
    h=References:In-Reply-To:Content-Type:Message-ID:Reply-To:Date:MIME-Version:Subject:To:From; bh=gCWDSCJf58CbaR+wjAV9dydu9JTKkvo1o+0zkj8bNr0=;
    b=pheltY+k/mio2x4CFQV8cXZxNiR7oSTkIsWTOZa1CGpEyK8KVSHY07OWSdZ1aFVtuaV32PbI0mNY0yliuqIbYTsnreFUYFM/iVR5PU74QHAe8yp46ydAYRbzLQu8dy+AkFhPtEdb8CAgoZKXgPLc888/Q6MsVAh6iH1L3SZj87Y=;
Received: by f427.i.mail.ru with local (envelope-from <[my name]@mail.ru>)
    id 1dbP18-0003I9-L7
    for madbr@[domain]; Sat, 29 Jul 2017 13:30:42 +0300
Received: by e.mail.ru with HTTP;
    Sat, 29 Jul 2017 13:30:42 +0300
From: =?UTF-8?B?0KHQtdGA0LPQtdC5?= <[my name]@mail.ru>
To: madbr@[domain]
Subject: =?UTF-8?B?UmU6IA==?=
MIME-Version: 1.0
X-Mailer: Mail.Ru Mailer 1.0
Date: Sat, 29 Jul 2017 13:30:42 +0300
Reply-To: =?UTF-8?B?0KHQtdGA0LPQtdC5?= <[my name]@mail.ru>
X-Priority: 3 (Normal)
Message-ID: <1501324242.448202607@f427.i.mail.ru>
Content-Type: multipart/mixed;
    boundary="----uEhsLqzDWmmGeA9EZ3XNsqSIGjlgVTmA-NI9QMhpqxNHWLEDT-1501324242"
Authentication-Results: f427.i.mail.ru; auth=pass smtp.auth=[my name]@mail.ru smtp.mailfrom=[my name]@mail.ru
X-7FA49CB5: 0D63561A33F958A58B4AE7CD4FB69874B38CA0D04717BA57612FFEEC28D99E31725E5C173C3A84C325A81A29FB5043FD044813140D6DB928F1C9CF18C8EB2269C4224003CC836476C0CAF46E325F83A50BF2EBBBDD9D6B0F2AF38021CC9F462D574AF45C6390F7469DAA53EE0834AAEE
X-Mailru-Sender: 080178E06F6B3F48806FD386034E228604900381AF51F7DD303A634C9E25199A8DFBC783E67F8C0305D8C6CDFE81985CCFB2E39DA8E91CCEEEC687A792225BA622DF1A08BD40178CA471C22AD050A14893AC9912533B2342AE208404248635DF
X-Mras: OK
X-Spam: undefined
In-Reply-To: <1500037364.788302144@mx47.mail.ru>
References: <1500037364.788302144@mx47.mail.ru>

验证指令如下:

In hash step 1, the Signer/Verifier MUST hash the message body,
canonicalized using the body canonicalization algorithm specified in
the "c=" tag and then truncated to the length specified in the "l="
tag.  That hash value is then converted to base64 form and inserted
into (Signers) or compared to (Verifiers) the "bh=" tag of the DKIM-
Signature header field.

In hash step 2, the Signer/Verifier MUST pass the following to the
hash algorithm in the indicated order.

1.  The header fields specified by the "h=" tag, in the order
    specified in that tag, and canonicalized using the header
    canonicalization algorithm specified in the "c=" tag.  Each
    header field MUST be terminated with a single CRLF.

2.  The DKIM-Signature header field that exists (verifying) or will
    be inserted (signing) in the message, with the value of the "b="
    tag (including all surrounding whitespace) deleted (i.e., treated
    as the empty string), canonicalized using the header
    canonicalization algorithm specified in the "c=" tag, and without
    a trailing CRLF.

第一步很简单:我已经获得了消息主体,并使用规范化的方式进行了处理。
 relaxed: function (data) {
    return data.replace(/[ \t]+\r\n/g, '\r\n').replace(/[ \t]+/g, ' ').replace(/\r\n{2,}$/g, CONST.CRLF);
  }

我首先根据标签创建了该内容的sha256哈希值。它与DKIM-Signature头部中的bh=标签匹配,我很高兴。

接下来我执行以下操作:

1)按照h=签名标签中给定的顺序从邮件中获取所有所需的标头。

References: <1500037364.788302144@mx47.mail.ru>
In-Reply-To: <1500037364.788302144@mx47.mail.ru>
Content-Type: multipart/mixed;
    boundary="----uEhsLqzDWmmGeA9EZ3XNsqSIGjlgVTmA-NI9QMhpqxNHWLEDT-1501324242"
Message-ID: <1501324242.448202607@f427.i.mail.ru>
Reply-To: =?UTF-8?B?0KHQtdGA0LPQtdC5?= <[my name]@mail.ru>
Date: Sat, 29 Jul 2017 13:30:42 +0300
MIME-Version: 1.0
Subject: =?UTF-8?B?UmU6IA==?=
To: madbr@[domain]
From: =?UTF-8?B?0KHQtdGA0LPQtdC5?= <[my name]@mail.ru>

2) 规范化它:

references:<1500037364.788302144@mx47.mail.ru>
in-reply-to:<1500037364.788302144@mx47.mail.ru>
content-type:multipart/mixed; boundary="----uEhsLqzDWmmGeA9EZ3XNsqSIGjlgVTmA-NI9QMhpqxNHWLEDT-1501324242"
message-id:<1501324242.448202607@f427.i.mail.ru>
reply-to:=?UTF-8?B?0KHQtdGA0LPQtdC5?= <[my name]@mail.ru>
date:Sat, 29 Jul 2017 13:30:42 +0300
mime-version:1.0
subject:=?UTF-8?B?UmU6IA==?=
to:madbr@[domain]
from:=?UTF-8?B?0KHQtdGA0LPQtdC5?= <[my name]@mail.ru>

3)获取DKIM签名,移除b=标签并进行规范化处理(根据文档还删除了结尾的\r\n):

dkim-signature:v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mail.ru; s=mail2; h=References:In-Reply-To:Content-Type:Message-ID:Reply-To:Date:MIME-Version:Subject:To:From; bh=gCWDSCJf58CbaR+wjAV9dydu9JTKkvo1o+0zkj8bNr0==;

4) 从DNS TXT记录中获取公钥,并添加-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----以符合PEM格式的兼容性。

5) 最后,我使用标准的RSA验证函数进行验证:

crypto.createVerify('sha256')
    .update(header + dkimHeader)
    .verify(publicKey, Buffer.from(signature.b, CONST.BASE64));

但是它失败了,我真的不知道要责备哪些行动。

在最后一步中,我将标题和DKIM-Signature连接起来,因为我真的不明白“按指示顺序将以下内容传递给哈希算法”是什么意思。尝试使用 .update(header).update(dkimHeader),但没有任何区别。

有人能解释一下吗?我做错了什么吗?

1个回答

8
从RFC的第3.7节计算消息哈希值中:
在哈希步骤2中,签名者/验证器必须按照指定顺序将以下内容传递给哈希算法。
1. 在"h="标签中指定的标题字段,按照该标签中指定的顺序进行规范化,并使用"c="标签中指定的标题规范化算法。每个标题字段必须以单个CRLF结尾。 2. 邮件中存在的DKIM-Signature头字段(用于验证)或将插入邮件的字段(用于签名),删除"b="标签中的(包括所有周围的空格),并使用"c="标签中指定的标题规范化算法进行规范化,不含尾随CRLF。
需要强调的是:只有值应该被删除,而不是完整的标签。
因此,输入的正确的最后一行应该是(请注意末尾的 b=;):
dkim-signature:v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mail.ru; s=mail2; h=References:In-Reply-To:Content-Type:Message-ID:Reply-To:Date:MIME-Version:Subject:To:From; bh=gCWDSCJf58CbaR+wjAV9dydu9JTKkvo1o+0zkj8bNr0=; b=;

哦,终于!谢谢你!看来我已经在这个地方读了100次而没有注意到。现在我终于得到了想要的true作为验证结果。 - MadBrozzeR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接