Skip to content

Verifying a DKIM signature in C#

Quick background: DomainKeys Identified Mail (DKIM) is a digital signature and hash which are placed on your emails to verify they are coming from the domain they claim to be. One step in the defence against SPAM and phishing emails.

What does the signature look like. Here’s a signature grabbed from an email sent from Yahoo mail:

DKIM-Signature: 
 v=1; 
 a=rsa-sha256; 
 c=relaxed/relaxed; 
 d=yahoo.com.au; 
 s=s1024; 
 t=1250214285; 
 bh=cHSvmWBBhlnc7HYJ0cXdBZfzQDwcJskaGayavbFvzSY=; 
 h=Message-ID:X-YMail-OSG:Received:X-Mailer:Date:From:Subject:To:MIME-Version:Content-Type; 
 b=gsz2P/9h3SL8PwBIU8g9dMGnHI+mwfSqjDhrkJljG3HNCbL0I4/DZJBGHnuqRwgpP4Rq0K3E/1z2Zwez4OhMI+naVzGg0P0ePG75GOwfXvl0XBvN8x8j1pgIOS8KQS24E1+6RogWIKJR2mht+KwiFK5uq3BYc9+nC9XMWsIE1fw=

Verifying a signature is going to require a public key. The public key is grabbed from a TXT record on the sender’s the domain. The domain name is built with the “s” and “d” tags above. “s” + “._domainkey.” + d = “s1024._domainkey.yahoo.com.au”.

Using nslookup from the command prompt give us:

C:\>nslookup
Default Server:  DD-WRT
Address:  192.168.1.1

> set type=TXT
> s1024._domainkey.yahoo.com.au
Server:  DD-WRT
Address:  192.168.1.1

Non-authoritative answer:
s1024._domainkey.yahoo.com.au   text =

        "k=rsa; t=y; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrEee0Ri4Juz+QfiWYui/E9UGSXau/2P8LjnTD8V4Unn+2FAZVGE3kL23bzeoULYv4Pel
eB3gfm"
        "JiDJOKU3Ns5L4KJAUUHjFwDebt0NP+sBK0VKeTATL2Yr/S3bT/xhy+1xtj4RkdV7fVxTn56Lb4udUnwuxK4V5b5PdOKj/+XcwIDAQAB; n=A 1024 bit key
;"

The “b=” tag in DKIM-Signature is a signature of the canonicalized headers. The canonicalization process is done to make sure the signature is resilient to header modifications by mail servers. In this example we are using the ‘relaxed’ canonicalization algorithm. (Much more details about canonicalization in the RFC4871).

The original headers:

Message-ID: <88014.32082.qm@web112514.mail.gq1.yahoo.com>
X-YMail-OSG: aJFcCYUVM1mfioaf5zF7Pv_aQQ27DjD57ShG_QdRO6jTz5SGM4lVBkiJ1SD0tZmtvLEHhR4YtNFpTpI26b3VFERE44rlTAN3nP7a9.snzPvxcIIVOy6d0XWg1fvWS5bpOInMbv7M0bltJc6ml3gWPn8S3tT6U.Wz4OXu22meLo5VrikBRKeSGItA5Kw5Asn20GT84javGzv9_3znhG0HMejyHxpDjDbn.C_9OpKMVheG6HmBpgFVYmsW9q2IKc4r08YZvyADKNKnjN066fNhCNaYKWu3PEpHpbV4pZf66cFChegx94phIMQCI0cScc8ZQ5BLRj7qwV2F85C2ib22DWbZvTY-
Received: from [114.76.223.211] by web112514.mail.gq1.yahoo.com via HTTP; Thu, 13 Aug 2009 18:44:43 PDT
X-Mailer: YahooMailRC/1358.27 YahooMailWebService/0.7.338.2
Date: Thu, 13 Aug 2009 18:44:43 -0700 (PDT)
From: Russell Sayers 
Subject: from yahoo
To: diagnostic@smtp2web.com
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary="0-1193717202-1250214283=:32082"

After relaxed canonicalization:

message-id:<88014.32082.qm@web112514.mail.gq1.yahoo.com>
x-ymail-osg:aJFcCYUVM1mfioaf5zF7Pv_aQQ27DjD57ShG_QdRO6jTz5SGM4lVBkiJ1SD0tZmtvLEHhR4YtNFpTpI26b3VFERE44rlTAN3nP7a9.snzPvxcIIVOy6d0XWg1fvWS5bpOInMbv7M0bltJc6ml3gWPn8S3tT6U.Wz4OXu22meLo5VrikBRKeSGItA5Kw5Asn20GT84javGzv9_3znhG0HMejyHxpDjDbn.C_9OpKMVheG6HmBpgFVYmsW9q2IKc4r08YZvyADKNKnjN066fNhCNaYKWu3PEpHpbV4pZf66cFChegx94phIMQCI0cScc8ZQ5BLRj7qwV2F85C2ib22DWbZvTY-
received:from [114.76.223.211] by web112514.mail.gq1.yahoo.com via HTTP; Thu, 13 Aug 2009 18:44:43 PDT
x-mailer:YahooMailRC/1358.27 YahooMailWebService/0.7.338.2
date:Thu, 13 Aug 2009 18:44:43 -0700 (PDT)
from:Russell Sayers 
subject:from yahoo
to:diagnostic@smtp2web.com
mime-version:1.0
content-type:multipart/alternative; boundary="0-1193717202-1250214283=:32082"

Now the canonicalized signature, minus the “b=” value. is placed after the headers. This had me scratching my head for a bit until I read this from the RFC:

The header field MUST be presented to the hash algorithm after the body of the message rather than with the rest of the header fields and MUST be canonicalized as specified in the “c=” (canonicalization) tag. The DKIM-Signature header field MUST NOT be included in its own h= tag..

We now have everything we need to verify the signature on the email using Bouncy Castle Crypto APIs


string base64pubkey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrEee0Ri4Juz+QfiWYui/E9UGSXau/2P8LjnTD8V4Unn+2FAZVGE3kL23bzeoULYv4PeleB3gfmJiDJOKU3Ns5L4KJAUUHjFwDebt0NP+sBK0VKeTATL2Yr/S3bT/xhy+1xtj4RkdV7fVxTn56Lb4udUnwuxK4V5b5PdOKj/+XcwIDAQAB";
RsaKeyParameters pubKey = PublicKeyFactory.CreateKey(Convert.FromBase64String(base64pubkey)) as RsaKeyParameters;
byte[] signature = Convert.FromBase64String("gsz2P/9h3SL8PwBIU8g9dMGnHI+mwfSqjDhrkJljG3HNCbL0I4/DZJBGHnuqRwgpP4Rq0K3E/1z2Zwez4OhMI+naVzGg0P0ePG75GOwfXvl0XBvN8x8j1pgIOS8KQS24E1+6RogWIKJR2mht+KwiFK5uq3BYc9+nC9XMWsIE1fw=");

string canonicalized =
@"message-id:<88014.32082.qm@web112514.mail.gq1.yahoo.com>
x-ymail-osg:aJFcCYUVM1mfioaf5zF7Pv_aQQ27DjD57ShG_QdRO6jTz5SGM4lVBkiJ1SD0tZmtvLEHhR4YtNFpTpI26b3VFERE44rlTAN3nP7a9.snzPvxcIIVOy6d0XWg1fvWS5bpOInMbv7M0bltJc6ml3gWPn8S3tT6U.Wz4OXu22meLo5VrikBRKeSGItA5Kw5Asn20GT84javGzv9_3znhG0HMejyHxpDjDbn.C_9OpKMVheG6HmBpgFVYmsW9q2IKc4r08YZvyADKNKnjN066fNhCNaYKWu3PEpHpbV4pZf66cFChegx94phIMQCI0cScc8ZQ5BLRj7qwV2F85C2ib22DWbZvTY-
received:from [114.76.223.211] by web112514.mail.gq1.yahoo.com via HTTP; Thu, 13 Aug 2009 18:44:43 PDT
x-mailer:YahooMailRC/1358.27 YahooMailWebService/0.7.338.2
date:Thu, 13 Aug 2009 18:44:43 -0700 (PDT)
from:Russell Sayers &lt;russell_sayers@yahoo.com.au&gt;
subject:from yahoo
to:diagnostic@smtp2web.com
mime-version:1.0
content-type:multipart/alternative; boundary=""0-1193717202-1250214283=:32082""
dkim-signature:v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com.au; s=s1024; t=1250214285; bh=cHSvmWBBhlnc7HYJ0cXdBZfzQDwcJskaGayavbFvzSY=; h=Message-ID:X-YMail-OSG:Received:X-Mailer:Date:From:Subject:To:MIME-Version:Content-Type; b=";
byte[] message = Encoding.ASCII.GetBytes(canonicalized);


ISigner sig = SignerUtilities.GetSigner("SHA256WithRSAEncryption");
sig.Init(false, pubKey);
sig.BlockUpdate(message, 0, message.Length);
if (sig.VerifySignature(signature))
{
    Console.WriteLine("all good!");
}

{ 6 } Comments

  1. Anonymous | September 15, 2009 at 9:48 am | Permalink

    Do you have an example signing an .eml message generated with SmtpClient?

  2. Russ | September 15, 2009 at 10:27 am | Permalink

    There's code in this article for adding custom headers: http://msdn.microsoft.com/en-us/vbasic/bb630227.aspx. So I think it can be done.. I'll have an attempt at it and post up and article if it works.

  3. Anonymous | September 18, 2009 at 3:27 pm | Permalink

    Great! Yes, is easy to add coustom headers, I am not sure wich part of the message and how to sign it, but it seems that following what you did could be similar.

  4. Russ | September 19, 2009 at 2:12 pm | Permalink

    FYI – I've added a new article with a working sample sending an email Sending a DKIM Signed Email from C#

  5. Dafna | December 18, 2016 at 11:44 am | Permalink

    Should copy paste this example return “all good!”….?
    Because I just copy & paste and the verification of the signature failed. Any idea why?

  6. russ | December 19, 2016 at 8:47 pm | Permalink

    Hi Dafna, yes it should return “all good”. I haven’t looked at this code for a loooooong time. Let me try rebuild it and test again, I’ll let you know.

{ 1 } Trackback

  1. Quora | April 7, 2011 at 3:58 pm | Permalink

    Does Amazon SES support DKIM ? If so, how can we implement it?…

    Hi everyone, Amazon SES is great for sending transactional emails. But in order to increase delivery rate and not to be flagged as spam, we need to do some settings such as : * SPF Records * SenderID * DKIM SPF and SenderID settings are quite easy. But…

Post a Comment

Your email is never published nor shared. Required fields are marked *