Verifying a DKIM signature in C#
|
The article you are reading has moved! It is now available at: http://blog.tinisles.com/2009/08/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
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
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 <russell_sayers@yahoo.com.au> 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!"); }
Labels: cryptography, dkim