DKIM is on our list of possible future enhancements, but not very high at the moment. DKIM signatures are usually added by mail transfer agents (such as mail servers), while Rebex SMTP is most often used as an SMTP client that sends e-mail through a server (which can usually be set up quite easily to add DKIM signatures).
However, we wrote a proof-of-concept code that shows how to add DKIM signature using Rebex Secure Mail with a bit of custom code:
string privateKeyPath = "..."; // path to domain private key
string domain = "rebex.net"; // domain name
string selector = "jun2016"; // selector
string from = "support@rebex.net";
string to = "someone@example.org";
string subject = "DKIM test email";
string body =
"\r\n\r\nThis is the body of the message, with more than\r\n" +
"one line at <B>the END and START</B>.\r\n" +
"\r\n";
// create new MailMessage
MailMessage mail = new MailMessage();
mail.From = from;
mail.To.Add(to);
mail.Subject = subject;
mail.BodyText = body;
mail.BodyHtml = body;
// save message into MemoryStream in final MIME format
MemoryStream ms = new MemoryStream();
// save it using MimeMessage to be able to omit Preamble (not part of canonicalized data)
MimeMessage message = mail.ToMimeMessage();
message.Preamble = null; // no preamble
message.Save(ms);
// load message from MemoryStream using DoNotParseMimeTree option
ms.Position = 0;
message.Options = MimeOptions.DoNotParseMimeTree;
message.Load(ms);
// read the body part
using (Stream s = message.GetContentStream())
{
byte[] b = new byte[s.Length];
int n = b.Length;
while (n > 0)
n -= s.Read(b, b.Length - n, n);
body = Encoding.UTF8.GetString(b);
body = RemoveCharset(body); // remove charsets (not part of canonicalized data)
}
// compute body hash
HashAlgorithm hash = new SHA256Managed();
// use desired Body Canonicalization Algorithm (see RFC 4871)
string hashBody = BodyRelaxedCanonicalization(body);
byte[] bodyBytes = Encoding.UTF8.GetBytes(hashBody);
string hashout = Convert.ToBase64String(hash.ComputeHash(bodyBytes));
// prepare Timestamp - seconds since 00:00:00 on January 1, 1970 UTC
TimeSpan t = DateTime.Now.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
// prepare data to sign
string signatureHeader =
"v=1; " +
"a=rsa-sha256; " +
"c=relaxed/relaxed; " +
"d=" + domain + "; " +
"s=" + selector + "; " +
"t=" + Convert.ToInt64(t.TotalSeconds) + "; " +
"bh=" + hashout + "; " +
//"h=From:To:Subject:Content-Type:Content-Transfer-Encoding; " +
"h=From:To:Subject; " +
"b=";
string canonicalizedHeaders =
"from:" + message.Headers["From"].Raw + Environment.NewLine +
"to:" + message.Headers["To"].Raw + Environment.NewLine +
"subject:" + message.Headers["Subject"].Raw + Environment.NewLine +
"dkim-signature:" + signatureHeader;
// load RSA private key
Rebex.Security.Cryptography.Pkcs.PrivateKeyInfo pkinfo = new Rebex.Security.Cryptography.Pkcs.PrivateKeyInfo();
using (FileStream fs = File.OpenRead(privateKeyPath))
{
pkinfo.Load(fs, null);
}
// sign prepared header data using RSA-SHA256
using (RSACryptoServiceProvider signer = new RSACryptoServiceProvider())
{
signer.ImportParameters(pkinfo.GetRSAParameters());
byte[] plaintext = Encoding.UTF8.GetBytes(canonicalizedHeaders);
byte[] signature = signer.SignData(plaintext, "SHA256");
signatureHeader += Convert.ToBase64String(signature);
}
// add the DKIM-Signature header
message.Headers.Add(new MimeHeader("DKIM-Signature", new Unparsed(signatureHeader)));
// save the message (for debugging purposes)
message.Save(targetMailPath);
// send the message
...
public static string BodySimpleCanonicalization(string body)
{
// Ignores all empty lines at the end of the message body
return body.TrimEnd(' ', '\t', '\r', '\n') + "\r\n";
}
public static string BodyRelaxedCanonicalization(string body)
{
// Ignores all empty lines at the end of the message body
body = body.TrimEnd(' ', '\t', '\r', '\n') + "\r\n";
// Reduces all sequences of WSP within a line to a single SP character
body = body.Replace('\t', ' ');
int originalLength = body.Length;
body = body.Replace(" ", " ");
while (body.Length != originalLength)
{
originalLength = body.Length;
body = body.Replace(" ", " ");
}
// Ignores all whitespace at the end of lines
body = body.Replace(" \r\n", "\r\n");
return body;
}
public static string RemoveCharset(string body)
{
return System.Text.RegularExpressions.Regex.Replace(body, ";\r\n[ \t]*charset=", "; charset=");
}
Although this code is certainly not production-quality, it demonstrates that adding DKIM signature using a bit of custom code is possible. Please note that in addition to this, you need to add proper DKIM DNS records as well.