Search code examples
c#asp.net-mvcemaildkim

Signing email in C#


Based on this question I decided to sign emails send from ASP.NET.MVC to decrease SPAM score of emails, but I have some bug somewhere.

Code:

    public void SendEmail(MailMessage mailMessage)
    {
        string domain = "kup-nemovitost.cz";

        var message = MimeMessage.CreateFromMailMessage(mailMessage);

        HeaderId[] headers = new HeaderId[] { HeaderId.From, HeaderId.Subject, HeaderId.Date };
        DkimCanonicalizationAlgorithm headerAlgorithm = DkimCanonicalizationAlgorithm.Relaxed;
        DkimCanonicalizationAlgorithm bodyAlgorithm = DkimCanonicalizationAlgorithm.Relaxed;

        string dkimPath = Path.Combine(ConfigHelper.GetDataPath(), "DKIM");
        string privateKey = Path.Combine(dkimPath, "kup-nemovitost.cz.private.rsa");

        DkimSigner signer = new DkimSigner(privateKey, domain, "mail")
        {
            SignatureAlgorithm = DkimSignatureAlgorithm.RsaSha1,
            AgentOrUserIdentifier = "@" + domain,
            QueryMethod = "dns/txt",
        };         

        message.Prepare(EncodingConstraint.SevenBit);
        message.Sign(signer, headers, headerAlgorithm, bodyAlgorithm);

        using (var client = new MailKit.Net.Smtp.SmtpClient())
        {
            client.Connect("localhost", 25, false);
            client.Send(message);
            client.Disconnect(true);
        }          
    }

I check the result on http://www.isnotspam.com, the output is following:

DKIM check details:
----------------------------------------------------------

Result: invalid
ID(s) verified: header.From=no-reply@kup-nemovitost.cz
Selector=mail
domain=kup-nemovitost.cz
DomainKeys DNS Record=mail._domainkey.kup-nemovitost.cz

My DNS record is:

@ IN TXT "v=dkim1; s=mail; p=migfma0gcsqgsib3dqebaquaa4gnadcbiqkbgqdnov2pxnjmghdpxw5wpypk1rf7 kxs+5ouvh6f0hraryncku6wbvq+xovbgxz1kuddcb/s9o8wquftxrlffniik3wbm qc+upm+ndloxcxwy0bb2iktbgnmndjiexm/z0npaviwzebr2k6vqdzbp+lmcuece bwasqgw2fki5ospb4qidaqab"

UPDATE:

I fix some issues in DNS record and I have found better online checker at dkimcore.org

I still face validation issue of my public key. I generated 1024 RSA using puttyGen (ppk) and convert it to RSA format. The original file from PuttyGen is:

---- BEGIN SSH2 PUBLIC KEY ----
Comment: "rsa-key-20170606"
AAAAB3NzaC1yc2EAAAABJQAAAIEAiyEwx+Idlf/Qp2fTYrQMwV3MuF9W7yaKDMHk
hzoH+MqWKtNDngQoJcmbyrkMeF0VLYo246ma3gPZh9cDL7i8ygOYKagbyUjgtZFz
y+et0tY/+G/IZNaHiQp0QuG/J71uZrl4Jlgkq+0s5bZxpRR45aRpcG1HQMIm6Ku7
lgmOt88=
---- END SSH2 PUBLIC KEY ----

So I just copy the content (except the commented lines) to DNS record and I got following output from checker:

p=  AAAAB3NzaC1yc2EAAAABJQAAAIEAiyEwx+Idlf/Qp2fTYrQMwV3MuF9W7yaKDMHkhzoH+MqWKtNDngQoJcmbyrkMeF0VLYo246ma3gPZh9cDL7i8ygOYKagbyUjgtZFzy+et0tY/+G/IZNaHiQp0QuG/J71uZrl4Jlgkq+0s5bZxpRR45aRpcG1HQMIm6Ku7lgmOt88=
This doesn't seem to be a valid RSA public key: RSA.xs:178: OpenSSL error: wrong tag at blib/lib/Crypt/OpenSSL/RSA.pm (autosplit into blib/lib/auto/Crypt/OpenSSL/RSA/new_public_key.al) line 91.

Solution

  • As we figured out in comments - your code is fine, but you have problems with public key. First, it has spaces in dns txt record, which it cannot have. Then, you have wrong format for a public key (SSH2). Tools that verify signature expect different format (regular PEM RSA), which should look like this:

    --- BEGIN RSA PUBLIC KEY ---
    MII ....
    --- END RSA PUBLIC KEY ---
    

    So, the same as your private key. You can convert SSH2 key to PEM with

    ssh-keygen -f output.pub -m 'PEM' -e > public.pem
    

    Where output.pub is public key in SSH2 format. With such key (and without spaces in DNS record) it should work fine, assuming you put that into right dns record - key_selector._domainkey.your.domain.