Sending a DKIM Signed Email from C#
|
The article you are reading has moved! It is now available at: http://blog.tinisles.com/2009/09/sending-a-dkim-signed-email-from-c/ |
First off this code is intended to be instructional only! Yes it is possible to send a DKIM email from .net's SmtpClient. But there are a few hacks/warnings to go with the following code. I'm pretty sure DKIM is designed for signatures to be added by the STMP server. All the emails I've seen with DKIM signatures sign headers that would only be available to the server, e.g: Message-ID and Received. What does the RFC 4871 have to say about this:
Note that verifiers may treat unsigned header fields with extreme skepticism, including refusing to display them to the end user or even ignoring the signature if it does not cover certain header fields. For this reason, signing fields present in the message such as Date, Subject, Reply-To, Sender, and all MIME header fields are highly advised.Second: We need to sign the message body exactly as SmtpClient is going to deliver it to the SMTP server. Unfortunately MailMessage doesn't give us access to this. I've found some code to do the Quoted-printable encoding here: Nice Clean C# Generate Quoted-Printable. Still we couldn't guarantee we are going to hash the body exactly as SmtpClient is going to send. In this example I'm just replacing the carriage returns for an extremely simple sample email.
Let's get started. First use OpenSSL to generate your public and private keys: DomainKeys Public/Private Key-pair Generation. Now add the public key to the relevant TXT record on the domain you intend to send email from. I bought a cheap .info domain for this demo, and pointed the registrar to ZoneEdit. I had no trouble adding to TXT record to my domain via ZoneEdit.
...and now the code: (again I'm using Bouncy Castle Crypto APIs to create the signature)
string smtp = "~~ YOUR STMP SERVER HERE ~~"; string from = "russ@dkimtester.info"; string subject = "dkim test email"; string to = "check-auth@verifier.port25.com"; string body = "This is the body of the message." + Environment.NewLine + "This is the second line"; string base64privatekey = @"-----BEGIN RSA PRIVATE KEY----- ~~ YOUR PRIVATE KEY HERE ~~ -----END RSA PRIVATE KEY-----"; HashAlgorithm hash = new SHA256Managed(); // HACK!! simulate the quoted-printable encoding SmtpClient will use string hashBody = body.Replace(Environment.NewLine, "=0D=0A") + Environment.NewLine; byte[] bodyBytes = Encoding.ASCII.GetBytes(hashBody); string hashout = Convert.ToBase64String(hash.ComputeHash(bodyBytes)); // timestamp - seconds since 00:00:00 on January 1, 1970 UTC TimeSpan t = DateTime.Now.ToUniversalTime() - DateTime.SpecifyKind(DateTime.Parse("00:00:00 January 1, 1970"), DateTimeKind.Utc); string signatureHeader = "v=1; " + "a=rsa-sha256; " + "c=relaxed/relaxed; " + "d=dkimtester.info; " + "s=p; " + "t=" + Convert.ToInt64(t.TotalSeconds) + "; " + "bh=" + hashout + "; " + "h=From:To:Subject:Content-Type:Content-Transfer-Encoding; " + "b="; string canonicalizedHeaders = "from:" + from + Environment.NewLine + "to:" + to + Environment.NewLine + "subject:" + subject + Environment.NewLine + @"content-type:text/plain; charset=us-ascii content-transfer-encoding:quoted-printable dkim-signature:" + signatureHeader; TextReader reader = new StringReader(base64privatekey); Org.BouncyCastle.OpenSsl.PemReader r = new Org.BouncyCastle.OpenSsl.PemReader(reader); AsymmetricCipherKeyPair o = r.ReadObject() as AsymmetricCipherKeyPair; byte[] plaintext = Encoding.ASCII.GetBytes(canonicalizedHeaders); ISigner sig = SignerUtilities.GetSigner("SHA256WithRSAEncryption"); sig.Init(true, o.Private); sig.BlockUpdate(plaintext, 0, plaintext.Length); byte[] signature = sig.GenerateSignature(); signatureHeader += Convert.ToBase64String(signature); MailMessage message = new MailMessage(); message.From = new MailAddress(from); message.To.Add(new MailAddress(to)); message.Subject = subject; message.Body = body; message.Headers.Add("DKIM-Signature", signatureHeader); SmtpClient client = new SmtpClient(smtp); client.Send(message); Console.Write("sent to: " + to);
An email sent to gmail gets the cool "signed-by" info:
Labels: cryptography, dkim
Thank you Thank you Thank you very much...
Posted by Anonymous | 11:59 pm
Hi,
I am a not technical person and was searching whether it is possible to send a DKIM signed mail using MTA running on Windows platform? Someone suggested it can only be send using Unix platform.
Since I saw your post saying send using C#, just thought to check with you.
Request you to reply me at nimesh12345@gmail.com at your earliest.
Thanks
Nimesh
Posted by Unknown | 8:55 pm
Your article is very interesting. I applied it and all works fine! Please, can you help me about how sign a email using DomainKeys? (because DKIM<>DomainKeys). I have some days trying, but without success
Thanks. My email is jorgerodcas@hotmail.com
Posted by Jorge Martin Rodriguez Castro | 1:53 am
Hi thanks for the research on this.
We eventually decided the easiest option was to install hMailServer (free) which has a very simple GUI for setting up DKIM.
We have it listen on a non-standard port (so that IIS SMTP still works) and use the .Net SmtpClient to create the message in C#.
Hopefully this info might be useful to Windows admins. Certainly cheaper than the commercial option.
Posted by Alistair | 1:38 am
Hi Alistair, I've been meaning to write a quick article on setting up hMailServer. The details for setting up DKIM on hMailServer are here: http://www.hmailserver.com/documentation/latest/?page=reference_domain
Russ
Posted by Russ | 7:53 am
Hi Russ,
Thanks for this fantastic blog post! I had been trying to use a commercial DKIM library to sign my emails and it wasn't working. This post helped me get there. 4+ days of efforts got me nowhere. This article got me there in just a few hours.
Thanks again,
Amol.
Posted by Anonymous | 6:34 pm
Great !!!
Thank you very very very much :)
Posted by Anonymous | 5:14 am
Thank you very much... it still work very good
Posted by Fatih Ă–zdemir | 9:12 pm
Thank you! This was very helpful
Posted by Anonymous | 5:33 am
Good job, thank you.
Posted by Oz | 9:03 am
i am getting error at cipher encryption time.
Org.BouncyCastle.OpenSsl.PemReader r is null, hence nothing ahead is working.
plz help
Posted by nav | 8:08 pm
i am also getting same error.
Org.BouncyCastle.OpenSsl.PemReader o is null
plz someone reply
Posted by Manpreet Sandhu | 4:44 pm
nav / Manpreet - do you have a genuine base64 private key in base64privatekey?
Posted by Russ | 4:49 pm
yes it is a valid key. i generated it online using http://dkimcore.org/tools/
Posted by nav | 2:25 pm
and also used all the namespace references.
using System.Text;
using System.Net.Mail;
using Org.BouncyCastle.Crypto;
using System.IO;
using System.Security.Cryptography;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.X509;
Posted by nav | 4:27 pm
Hi Nav,
Do you mind emailing me the key you are using? And any code you've got going. This article is pretty old- there's a good chance the Bouncy Castle stuff has changed since I last looked at it.
Email: russ@tinisles.com
Russ
Posted by Russ | 4:30 pm
This comment has been removed by the author.
Posted by nav | 9:24 pm
Hi,
thanks for this code.
but I have a problem with gmail, it reports all my emails as dkim neutral (bad format) and I just can't pinpoint the reason for that.
Does anybody have any clue?
Posted by Bojan | 1:34 am
lol... I have dived deep into this subject for more than 2 hours. got it working ALMOST.
however, please note, THIS worked only for emails which did not contains any special characters (spaces, new lines etc). THe replace new line with quated-encoding thing DID not work. The sample code provided did not even for for mentioned new-line case. It produced invalid results.
I have found another dedicated encoded on the web for the purpose of encoding whole string to that encoding => did not work either.
Seems like that hack does not work anymore?
Used GMAIL acc to verify results integrity.
Then.. suddenty I read in the comments about hMailServer which I had installed already...
LOL
couple of clicks... whole new code to WIPE OUT just to be restored to what it has been all time long. thank you all
Posted by Unknown | 2:46 am
hello,
I have used this code but in the header of my emails I didn't have the signed by what is the problem !
Posted by Unknown | 7:57 pm
Hi,
Can we send mail using certificate.
After installing certificate.
smtp authentication using certificate public key or private key?
and without using smtp credentials just through certificate
Posted by Unknown | 6:47 pm
how public key supplied to the client?
Posted by Samoxin | 2:45 pm
Its working fine when i am sending plain text but getting error "dkim=neutral (body hash did not verify)" when i am trying to send email in HTML format.
I tried adding "Mailmessage.BodyEncoding = Encoding.UTF8;" but still facing same issue.
Posted by Travel | 10:15 am
jika Anda seorang pemain poker profesional yang bisa tidur pada hari berikutnya, tetapi Anda harus mempertimbangkan komitmen kerja pertimbangan jika Anda punya pekerjaan. Ketahui apa yang berpotensi Anda dapatkan saat mendaftar untuk acara ini
asikqq
dewaqq
sumoqq
interqq
pionpoker
bandar ceme
hobiqq
paito warna
http://199.30.55.59/dewaqq78/
data hk 2019
Posted by Tony Chew | 6:36 pm
This comment has been removed by the author.
Posted by Unknown | 7:18 pm
getting this type of error with this code.
dkim=neutral (body hash did not verify) header.i=@
Posted by Unknown | 7:22 pm