Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DKIM - signature fails if message Content-Type is: multipart/alternative #178

Closed
punxrok opened this issue Oct 7, 2015 · 12 comments
Closed
Labels
bug Something isn't working

Comments

@punxrok
Copy link

punxrok commented Oct 7, 2015

Hello,

I've been trying to implement DKIM message singing with the help of your library.
The service that I've been using for validating DKIM is: http://dkimvalidator.com/

The DKIM signature validation passes if I build a simple mail message. The DKIM signature verification fails if message Content-Type is: multipart/alternative (method GenerateWithBodyBuilder()).
It also fails at Gmail (dkim=neutral (body hash did not verify) header.i=@dev.postar.eu).

Original message received by dkimvalidator.com:

Received: from [127.0.0.1](unknown [212.18.43.120])
by ip-10-212-6-2 (Postfix) with ESMTPS id F1F4B38065F
for postar@dkimvalidator.com; Wed, 7 Oct 2015 11:14:07 +0000 (UTC)
DKIM-Signature: v=1; a=rsa-sha256; d=dev.postar.eu; s=postar1024;
c=relaxed/relaxed; t=1444223643; h=from:to:subject:date:message-id;
bh=YFTLzdVcmulRq0rD7JHqdnjql/4m85WwE3/c73oFChw=;
b=Lauijgc+6haOkOfEsxpEO2VggdplHNM+o9jRlslyr6AmZnqmnfdy/oIaFpF5l4UD7VnugXZ3Yec
C9zfbrkHBSWZ17NtdLkk4SuCoLgkXA1e6XBiqKjFaDxpvbd4FXjUQYVgZ0/1dHS1Nf9zrTHes1/Pg
suCrbCWRWZhnShtdtj0=
From: Joey joey@friends.com
To: DKIM postar@dkimvalidator.com
Date: Wed, 07 Oct 2015 13:14:03 +0200
Subject: DKIM signature validation test which fails
Message-Id: B4AZVVINBWT4.1T8A1MBARP1P2@Rok
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary="=-CXTofpVZkZ2ZpmOk5gu3fQ=="
X-Antivirus: avast! (VPS 151007-1, 07.10.2015), Outbound message
X-Antivirus-Status: Clean

--=-CXTofpVZkZ2ZpmOk5gu3fQ==
Content-Type: text/plain; charset=utf-8

Hey Alice,

What are you up to this weekend? Monica is throwing one of her parties on
Saturday and I was hoping you could make it.

Will you be my +1?

-- Joey

--=-CXTofpVZkZ2ZpmOk5gu3fQ==
Content-Type: text/html; charset=utf-8
Content-Id: P053WVINBWT4.6J52K8AU0HP12@Rok

Hey Alice

--=-CXTofpVZkZ2ZpmOk5gu3fQ==--

DKIM Signature validation:

DKIM Information:

DKIM Signature

Message contains this DKIM Signature:
DKIM-Signature: v=1; a=rsa-sha256; d=dev.postar.eu; s=postar1024;
c=relaxed/relaxed; t=1444223643; h=from:to:subject:date:message-id;
bh=YFTLzdVcmulRq0rD7JHqdnjql/4m85WwE3/c73oFChw=;
b=Lauijgc+6haOkOfEsxpEO2VggdplHNM+o9jRlslyr6AmZnqmnfdy/oIaFpF5l4UD7VnugXZ3Yec
C9zfbrkHBSWZ17NtdLkk4SuCoLgkXA1e6XBiqKjFaDxpvbd4FXjUQYVgZ0/1dHS1Nf9zrTHes1/Pg
suCrbCWRWZhnShtdtj0=

Signature Information:
v= Version: 1
a= Algorithm: rsa-sha256
c= Method: relaxed/relaxed
d= Domain: dev.postar.eu
s= Selector: postar1024
q= Protocol:
bh= YFTLzdVcmulRq0rD7JHqdnjql/4m85WwE3/c73oFChw=
h= Signed Headers: from:to:subject:date:message-id
b= Data: Lauijgc+6haOkOfEsxpEO2VggdplHNM+o9jRlslyr6AmZnqmnfdy/oIaFpF5l4UD7VnugXZ3Yec
C9zfbrkHBSWZ17NtdLkk4SuCoLgkXA1e6XBiqKjFaDxpvbd4FXjUQYVgZ0/1dHS1Nf9zrTHes1/Pg
suCrbCWRWZhnShtdtj0=
Public Key DNS Lookup

Building DNS Query for postar1024._domainkey.dev.postar.eu
Retrieved this publickey from DNS: v=DKIM1;p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4j/gLQVfpgJFtoflMnVS77zZq+EIC2mmZUN/rSyRTx2B4Q16/YLDy39AvL+A05gIpXR5DCJnga1h6345aVVQ6ktB1lyA+oJ4gacOswPdIgcWgSb+4d/1eJzICSmeAEwghV5SXx4WkWO8Js8ZVIjrH2l1e0fJGI30c7TmxJZ8s2wIDAQAB
Validating Signature

result = fail
Details: body has been altered

Example code:

namespace ConsoleApplication.MailKit.DkimTest
{
    class Program
    {
        private const string Domain = "dev.postar.eu";
        private const string Selector = "postar1024";
        /// <summary>
        /// Mail server
        /// </summary>
        private const string DkimValidatorMailServer = "30047900.in1.mandrillapp.com";
        /// <summary>
        /// dkimvalidator.com email address
        /// </summary>
        private static string SendToDkimValidatorEmailAddress = "postar@dkimvalidator.com";


        private static void Main(string[] args)
        {
            var publicKeyParameter = GetDkimKey();
            //MimeMessage msg = GenerateSimpleMessage(); //DKIM signature validation passes
            MimeMessage msg = GenerateWithBodyBuilder(); //DKIM signature validation fails


            var headersToSign = new[]
            {
                HeaderId.From,
                HeaderId.To,
                HeaderId.Subject,
                HeaderId.Date,
                HeaderId.MessageId,              
            };

            var signer = new DkimSigner(publicKeyParameter.Private, Domain, Selector);
            msg.Sign(signer, headersToSign, DkimCanonicalizationAlgorithm.Relaxed, DkimCanonicalizationAlgorithm.Relaxed);

            var verificationResult = msg.Verify(msg.GetDkimHeader(), new DkimLocator()); //verificationResult = true

            //Send the message to dkimvalidator
            using (var client = new SmtpClient())
            {
                client.Connect(DkimValidatorMailServer, 25, false);
                client.Send(msg);
                client.Disconnect(true);
            }
        }

        private static AsymmetricCipherKeyPair GetDkimKey()
        {
            var privateKeyString = ConfigurationManager.AppSettings["DKIM"];

            AsymmetricCipherKeyPair publicKeyParameter;
            using (var reader = GenerateStreamFromString(privateKeyString))
            using (var textReader = new StreamReader(reader))
            {
                publicKeyParameter = (AsymmetricCipherKeyPair) new PemReader(textReader).ReadObject();
            }
            return publicKeyParameter;
        }


        public static MimeMessage GenerateSimpleMessage()
        {

            var simpleMessage = new MimeMessage();
            simpleMessage.From.Add(new MailboxAddress("Joey", "joey@friends.com"));
            simpleMessage.To.Add(new MailboxAddress("DKIM", SendToDkimValidatorEmailAddress));
            simpleMessage.Subject = "DKIM signature validation test";

            simpleMessage.Body = new TextPart("plain")
            {
                Text = @"Hi,
this email validates successfully."
            };

            return simpleMessage;
        }

        public static MimeMessage GenerateWithBodyBuilder()
        {
            var message = new MimeMessage();
            message.Prepare(EncodingConstraint.SevenBit);

            message.From.Add(new MailboxAddress("Joey", "joey@friends.com"));
            message.To.Add(new MailboxAddress("DKIM", SendToDkimValidatorEmailAddress));
            message.Subject = "DKIM signature validation test which fails";

            var builder = new BodyBuilder();

            // Set the plain-text version of the message text
            builder.TextBody = @"Hey Alice,

What are you up to this weekend? Monica is throwing one of her parties on
Saturday and I was hoping you could make it.

Will you be my +1?

-- Joey
";

            // Set the html version of the message text
            builder.HtmlBody = string.Format(@"<p>Hey Alice</p>");

            // Now we just need to set the message body and we're done
            message.Body = builder.ToMessageBody();

            return message;
        }


        public static Stream GenerateStreamFromString(string s)
        {
            MemoryStream stream = new MemoryStream();
            StreamWriter writer = new StreamWriter(stream);
            writer.Write(s);
            writer.Flush();
            stream.Position = 0;
            return stream;
        }
    }


    public class DkimLocator : IDkimPublicKeyLocator
    {

        /// <summary>
        /// Public Key file path
        /// </summary>
        private const string DkimPublicKeyFilePath = @"C:\DKIM\publickeyPostarDev.pem";

        public AsymmetricKeyParameter LocatePublicKey(string methods, string domain, string selector, CancellationToken cancellationToken = new CancellationToken())
        {

            using (var reader = File.OpenText(DkimPublicKeyFilePath)) //for simplicity
            {
                var publicKeyParameter = (AsymmetricKeyParameter)new PemReader(reader).ReadObject();

                return publicKeyParameter;
            }
        }
    }

    public static class MimeKitHelpers
    {
        public static Header GetDkimHeader(this MimeMessage msg)
        {
            return msg.Headers.FirstOrDefault(header => header.Id == HeaderId.DkimSignature);
        }
    }
}
@breedbekkikker
Copy link

My guess is that in both DkimSimpleBodyFilter.cs and DkimRelaxedBodyFilter.cs an extra NewLine is added when flush is true and the buffer does not end with a NewLine, while the canonicalization methods only require to add a NewLine if the body does not end with a CRLF. Currently, (flush && !lastWasNewLine) can be true even if we are not yet at the end of the body.
Additionally, multiple empty lines must only be replaced by a single NewLine for canonicalization if they exist at the end of the body.

jstedfast added a commit that referenced this issue Oct 7, 2015
The problem is that MimePart.WriteTo() will flush the stream
after transcoding the content in cases where the content is
not already in its target encoding.

Fixes issue #178
@jstedfast
Copy link
Owner

Fixed.

@jstedfast jstedfast added the bug Something isn't working label Oct 7, 2015
@jstedfast
Copy link
Owner

I've just released MimeKit 1.2.13 to nuget.org with a fix for this issue.

@punxrok
Copy link
Author

punxrok commented Oct 13, 2015

Thanks. It works great now.

@ghost
Copy link

ghost commented Jan 31, 2017

@jstedfast

I maybe wrong, but I think even the latest versions exhibit the similar problems.

I put it as question in SO, because I wasn't really sure.

http://stackoverflow.com/questions/41955049/dkim-signing-fails-with-body-hash-did-not-verify-for-mimekit-1-10-1-0-mailkit

Regards,
Krishnan

@jstedfast
Copy link
Owner

Are you calling message.Prepare () before DKIM signing? If not, that's the problem you are hitting.

@ghost
Copy link

ghost commented Feb 1, 2017 via email

@VickyKhan123
Copy link

receiving Details: message has been altered error messaged

@jstedfast
Copy link
Owner

@VickyKhan123 do you know for sure that the signature is good and that the message has not been altered?

@VickyKhan123
Copy link

I solved it, as there was a rule on the firewall scanning outgoing SMTP traffic, I have some questions

  1. if i want to enable firewall rule then then signature will be altered, so is it fine with this message i.e. message has been altered error messaged
  2. Which solution is better DKIM /DMARC or scanning all SMTP using the firewall for viruses etc? as i cannot enable both at the same time.

@jstedfast
Copy link
Owner

Glad you figured out the issue.

Let me try to answer your questions below:

  1. The message has been altered error message is not a good thing, it makes your DKIM signature not only worthless, but may get your message flagged by the receiving software as bad (or spam?).
  2. That's a tough question to answer. Is it possible to scan SMTP for viruses with your firewall but have the scanner not alter the message? Just have it simply quarantine messages that it finds suspicious until you are able to manually check them? That would be a better solution, I think.

@VickyKhan123
Copy link

thanks, i will check how to enable SMTP scanning and not altered .
this morning i receive dmarc reports from yahoo, gmail etc i checked and found out that at some places under the xml file DKIM and SPF are set to pass and at other places Dkim is set to fail while SPF is set to pass

<source_ip>X.X.X.X</source_ip>
13
<policy_evaluated>
none
fail
pass
</policy_evaluated>


<header_from>mydomain.com</header_from>

<auth_results>

mydomain.com
permerror


mydomain.com
pass

</auth_results>



<source_ip>X.X.X.X</source_ip>
8
<policy_evaluated>
none
pass
pass
</policy_evaluated>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants