我正在尝试使用DKIM标头向gmail(或任何其他电子邮件提供商)发送一封非常简单的电子邮件。
gmail中的结果是:dkim = neutral(正文哈希未验证)
我假设身体哈希不正确。 我让身体变得非常简单并且仍然得到同样的错误。
这是SMTP数据字符串:
var ws = new WebSocket("ws://192.168.2.6:8000/")
ws.onopen = function(){
ws.send("Hello World");
};
ws.onmessage = function(event){
console.log(event.data)
}
ws.onclose = function(){
alert("Lost connection to the server!");
}
我唯一能想到的是身体哈希码中必定存在错误。
DKIM-Signature:v=1; a=rsa-sha1; q=dns/txt; s=default;\r\n c=simple/simple; d=cumulo9.com; h=Date:From:To:Content-Type:Content-Transfer-Encoding;\r\n t=1489977499; bh=rtE3fSBFa/HdaPcuGaMM2mZVL7Mljo9sPTNOBjmNBdgIpGYh+ukt71Joc/qFd/nY70yn/hW0nASN+SZARGY2ri0ymA6NUrCIcSX7yJxJ6MkO78cyGZUoHY6Y+kOsDfCUcH5ANHJs88iUtu4IviWP4vWHXBd/tqP9k7Q+UKaC+m4=;\r\n b=klwC+c8qFKVD32SK22K04/YID+TerTvd26+VnlTljNA3fOEVbi2YlvTFo5LM1VksmO08hu5iJfwmF/3GgSEOnGT3mrzXxofjPbvIWU181zluxObNt8FwrP0kCIUskJEQz2SPF1VzaMQ8QvVchnkEFYrW9Pvssk6hunNr8J6CGrc=\r\nDate: Mon, 20 Mar 2017 15:38:17 +1300\r\nFrom: <leo@cumulo9.com>\r\nTo: leo@cumulo9.com\r\nContent-Type: text/plain; charset=UTF-8\r\nContent-Transfer-Encoding: 7bit\r\n\r\nhelloleo\r\n.
“PrivateKeySigner”类中的函数:
public string SignBody(string body)
{
var cb = body + "\r\n";
IPrivateKeySigner _privateKeySigner = new MailPost.DKIM.PrivateKeySigner(PrivateKey);
byte[] defaultEncoding = Encoding.UTF8.GetBytes(cb);
byte[] hash = _privateKeySigner.Sign(defaultEncoding, SigningAlgorithm.RSASha1);
string bodyHash = Convert.ToBase64String(hash);
return bodyHash;
}
“OpenSslKey”类中的函数:
public byte[] Sign(byte[] data, SigningAlgorithm algorithm)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
using (var rsa = OpenSslKey.DecodeRSAPrivateKey(m_key))
{
byte[] signature = rsa.SignData(data, GetHashName(algorithm));
return signature;
}
}
GetIntegerSize()代码:
public static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
{
if (privkey == null)
{
throw new ArgumentNullException("privkey");
}
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
// --------- Set up stream to decode the asn.1 encoded RSA private key ------
//var mem = new MemoryStream(privkey);
using (var binr = new BinaryReader(new MemoryStream(privkey))) //wrap Memory Stream with BinaryReader for easy reading
{
ushort twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
twobytes = binr.ReadUInt16();
if (twobytes != 0x0102) //version number
return null;
byte bt = binr.ReadByte();
if (bt != 0x00)
return null;
//------ all private key components are Integer sequences ----
int elems = GetIntegerSize(binr);
MODULUS = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
E = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
D = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
P = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
Q = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DP = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DQ = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
IQ = binr.ReadBytes(elems);
// ------- create RSACryptoServiceProvider instance and initialize with public key -----
var RSA = new RSACryptoServiceProvider();
var RSAparams = new RSAParameters
{
Modulus = MODULUS,
Exponent = E,
D = D,
P = P,
Q = Q,
DP = DP,
DQ = DQ,
InverseQ = IQ
};
RSA.ImportParameters(RSAparams);
return RSA;
}
}
目前我真的遇到了这个问题而且不知道我做错了什么。由于项目的性质,我无法使用像“MimeKit”这样的其他库。如果您需要有关此问题的更多信息,请告诉我,我会尽力为您提供。
感谢大家帮助我。
答案 0 :(得分:2)
很可能你有几个问题(在这里你以为你只有1个!)。
首先,您是如何确定邮件的body
的?这只是你在MailMessage
身上设置的字符串吗?如果是这样,除非您只发送非常简单的短信,否则这种情况不太可能发挥作用(即便如此......它可能不会,具体取决于MailMessage
是否决定它需要使用例如文本进行编码base64
或quoted-printable
编码)。您需要确保在生成正文哈希之前Content-Transfer-Encoding
已应用。
其次,您是否记得使用rfc6376, section 3.4.3中描述的Simple
正文规范化规则来转换正文?在我看来,你只是在"\r\n"
,但这不是规则所要做的。
如果您不想重新发明轮子,可以尝试使用像MimeKit这样的库来构建和DKIM签名您的消息,如下所示:
var message = new MimeMessage ();
message.From.Add (new MailboxAddress ("", "leo@cumulo9.com"));
message.To.Add (new MailboxAddress ("", "leo@cumulo9.com"));
message.Body = new TextPart ("plain") { Text = "helloleo" };
var headers = new HeaderId[] { HeaderId.Date, HeaderId.From, HeaderId.To, HeaderId.ContentType, HeaderId.ContentTransferEncoding };
var headerAlgorithm = DkimCanonicalizationAlgorithm.Simple;
var bodyAlgorithm = DkimCanonicalizationAlgorithm.Simple;
var signer = new DkimSigner ("privatekey.pem", "cumulo9.com", "default") {
SignatureAlgorithm = DkimSignatureAlgorithm.RsaSha1,
QueryMethod = "dns/txt",
};
// Prepare the message body to be sent over a 7bit transport (such as
// older versions of SMTP).
// Note: If the SMTP server you will be sending the message over supports
// the 8BITMIME extension, then you can use `EncodingConstraint.EightBit`
// instead, although it never hurts to use `SevenBit`.
message.Prepare (EncodingConstraint.SevenBit);
message.Sign (signer, headers, headerAlgorithm, bodyAlgorithm);
// to write out the message so you have something to compare with:
var options = FormatOptions.Default.Clone ();
options.NewLineFormat = NewLineFormat.Dos;
message.WriteTo (options, "message.txt");
然后,一旦您收到DKIM签名的邮件,就可以使用MailKit通过SMTP发送邮件,如下所示:
using (var client = new SmtpClient ()) {
// For demo-purposes, accept all SSL certificates
client.ServerCertificateValidationCallback = (s,c,h,e) => true;
client.Connect ("smtp.gmail.com", 587, SecureSocketOptions.StartTls);
// Note: since we don't have an OAuth2 token, disable
// the XOAUTH2 authentication mechanism.
client.AuthenticationMechanisms.Remove ("XOAUTH2");
// Note: only needed if the SMTP server requires authentication
client.Authenticate ("joey@gmail.com", "password");
client.Send (message);
client.Disconnect (true);
}
答案 1 :(得分:1)
我本来可以评论的,但我的声誉还不够高。 :/
我正在使用 PHPMailer,但如果我的解决方案更适用,我想我会留下笔记。
我已经在测试环境中验证了我的电子邮件系统和 DKIM 密钥后,我的电子邮件触发了 dkim=neutral (body hash did not verify)
错误。
事实证明,至少对于 PHPMailer,如果 $body 字符串的末尾有一个尾随空格,它被送入 $mail->Body = $body;
则 DKIM 正文哈希将不匹配,从而触发错误。