Link to home
Start Free TrialLog in
Avatar of tandtsoftware
tandtsoftware

asked on

Where can I find a JavaDKIM Example integrating MimeMessage

Can anyone provide a more comprehensive example using BadPenguin's JavaDKIM (http://www.badpenguin.co.uk/dkim) to sign an outgoing JavaMail message? By more comprehensive I mean that the example Signer in the BadPenguin JavaDKIM documentation only shows signing a text input stream. I am trying to clearly understand exactly what I need to do from start to finish, including how to integrate the signed message with the MimeMessage. Am I simply signing the body of the message and passing that to mimeMessage.setText(...)? If so what parameters are used for the character set and subtype, etc.? Exactly what do I need to do differently with the MimeMessage for a DKIM signed message than for an unsigned message?

Note that I have been able to sign outboud e-mail with Agitos's DKIM for JavaMail, which more clearly integrates with the MimeMessage, but particularly larger messages signed by it are not passing on the receiving end (body hash doesn't verify); shorter messages pass. I seem to find more evidence of real implementations using BadPenguin's JavaDKIM so I thought I would try that route.
Avatar of CEHJ
CEHJ
Flag of United Kingdom of Great Britain and Northern Ireland image

>>Can anyone provide a more comprehensive example using BadPenguin's JavaDKIM (http://www.badpenguin.co.uk/dkim) to sign an outgoing JavaMail message?

Please post the example you've seen so far
Avatar of tandtsoftware
tandtsoftware

ASKER

The examples I have seen are the one with the BadPenguin's JavaDKIM documentation (attached here) and another Q&A based on that example here within experts-exchange. Neither one addresses how you would use it in the context of the JavaMail MimeMessage though.


try {
   msg = new FileInputStream("/home/mark/mymail.txt");
   DkimSignature dksig = new DkimSignature("savage", "badpenguin.co.uk");
   dksig.setMethod(CanonicalMethod.RELAXED);
   dksig.setItag("dkim@badpenguin.co.uk");
   dksig.addHeader("received");
   Signer signer = new Signer(dksig,key);
   String header = signer.signMail(msg);
   System.out.println(header);
   msg.close();
} catch (DkimException d) {
      
      if ( d.getError().equals(DkimError.TEMPFAIL)) {
         // message failed, but may succeed later
      } else if ( d.getError().equals(DkimError.PERMFAIL)) {
         // message failed and will never verify
      } else {
         // domainkey error
   }
} catch (Exception e) {
      e.printStackTrace();
}

Open in new window

SOLUTION
Avatar of Mick Barry
Mick Barry
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
When you say "...the text used to build the message," I don't understand exactly what text that is.

From my example text is "msg" in the line: String header = signer.signMail(msg);
I have tried passing the body content of the message (i.e. the same thing I pass to mimeMessage.setText()), but I get DkimException: "PERMFAIL: Broken Mail Header encountered, Message rejected." From the same example "msg" is defined as new FileInputStream("/home/mark/mymail.txt"); but then what is the content of mymail.txt? I thought it would have simple been the body content of the message, but apparently not.
Let me present my question another way. Given this segment of code, does it look accurate and what exactly do I pass to signMail?
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.MimeMessage;
import badpenguin.dkim.Signer;
	        	
Session session;  
Transport transport;
RSAPrivateKey privateKey;
 
...
    
MimeMessage mimeMessage = new MimeMessage(session);

mimeMessage.setFrom( new InternetAddress("fromEmail@myOrganization.com") );

mimeMessage.addRecipients(RecipientType.TO, new InternetAddress[] { new InternetAddress("toEmail@yourOrganization.com") } );
	
mimeMessage.setSubject( "The Subject" );
	
mimeMessage.setText( "The message content." );
	
Signer signer = Signer("mySelector", "myOrganization.com", "rsa-sha256", privateKey);
	
String signature = signer.signMail( ? WHAT GOES HERE ? );
	
mimeMessage.setHeader("DKIM-Signature", signature);
	
transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
 

Open in new window

I would have thought you'd use "The message content."
whats the stack trace for the exception?

as an alternative the following handles javamail integration
http://www.agitos.de/dkim-for-java-mail-open-source-library-2.html
>>String signature = signer.signMail( ? WHAT GOES HERE ? );

Try

InputStream in = mimeMessage.getInputStream();
String signature = signer.signMail(in);
in.close();

Open in new window

I have been testing with Agitos' DKIM for some time, but eecipients don't pass (fail body hash) some of its larger signed messages. That is why I am trying BadPenguin's JavaDKIM in the first place.

Using mimeMessage.getInputStream for the signMail parameter doesn't work. It is only the body content.

According to BadPenguin's documentation for Signer.signMail(msg), msg "must contain the raw email message: That means both the headers and the body as transfered in SMTP data, excluding the ending CRLFCRLF." So I am still trying to figure out how to get that from the MimeMessage.
Ok, I finally got BadPenguin's signer working but it too has a problem with less-than-simple body content causing the receiving end to fail the body hash. In either case (using BadPenguin or Agitos) you have to "simulate" the header + body content of the message that is going to be sent, sign it, and add a DKIM-Signature header.

So now I am wonder whether or not the whole concept of adding a DKIM-Signature to the MimeMessage *before* the Transport (or Provider? -- whoever is actually creating the SMTP message from the MimeMessage object) has ever created the actual SMTP message is a flawed idea in the first place?? How do I know that the Transport isn't going to create that different than my DKIMSigner? Instead don't I really need to somehow intercept what the Transport actually created, calculate the hash from that, and add the DKIM-Signature?
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Agitos converts a MimeMessage object to a text stream that it then then canonicalizes and then signs. BadPenguin wants to operate on text already intended to be the SMTP message, so the MimeMessage-to-text conversion I did myself (similar to what Agitos is doing internally). But both implementations, BadPenguin and Agitos, ARE doing header and body canonicalization as defined by the DKIM spec (supporting both simple and relaxed methods). My concern is even while following the canonicalization rules, whether there is still too much room for error at "guessing" what the SMTP message is going to look like BEFORE the Java SMTP Provider has ever produced a real, original SMTP message. I am suspicious because both BadPenguin and Agitos are incorrectly calculating the body hash on some (but not all) messages, but I remain open to the possibility that I am still doing something wrong.

With that in mind, is it possible to put a hook or listener that would canonicalize and sign the real SMTP message? I think such a hook would need to be AFTER the call to Transport.send(mimeMessage) but BEFORE it is actually sent out to the mail server.
>> I think such a hook would need to be AFTER the call to Transport.send(mimeMessage) but BEFORE it is actually sent out to the mail server.

Even then, and after it's gone on the first hop, headers can be, and are, set on the message such that when it arrives at its destination, the signature will only be partial
Yes but I am only signing a subset of headers (i.e. the required ones). I should add that when I don't sign with a Java solution and instead use the Perl add-on to our smtp server everything works just fine. The reason I want a Java solution is so that it won't matter which smtp server the message is originated through and I won't have to maintain DKIM as an add-on to our mail server. But so far the Java solutions have not worked.
>>Yes but I am only signing a subset of headers (i.e. the required ones).

Which ones are you using? They should normally appear if you use the approach i suggested at http:#33472952
At this point with the BadPenguin implementation I am signing the headers they sign by default, from:to:subject:messager-id. I believe Agitos signs more than that by default. The problem really isn't with the headers though; it is the body content that is failing the hash on the receiving end, and I emphasize only with some messages, and that those same messages failing with the Java signers pass when signed by the Perl signer.
>>it is the body content that is failing the hash on the receiving end

That could be a normalization issue
Ok, I reviewed #33472952. I am currently calling mimeMessage.getMatchingHeaderLines() and adding mimeMessage.getDataHandler().writeTo(byteArrayOutputstream) to obtain the content. Are you saying that mimeMessage.writeTo(byteArrayOutputStream) will obtain the headers plus the content as it should appear in the real SMTP message? I have not tried that call specifically yet.
>>Are you saying that mimeMessage.writeTo(byteArrayOutputStream) will obtain the headers plus the content as it should appear in the real SMTP message?

Yes
Ok, I will go back and try mimeMessage.writeTo(byteArrayOutputStream). Should I expect that to have applied any normalization, or would I need to do that before I set the text into the MimeMessage object? Incidentally, does the character set specified when the content is set into the MimeMessage have any influence on normalization? I have tried specifying various character sets (e.g. UTF-8, US-ASCII, ISO-8859-1) that don't affect the outcome of the messages failing body hash.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
At this point, it would be worth finding out what the Perl is doing right
>> At this point, it would be worth finding out what the Perl is doing right
Perl (or any implementation on the mail server itself) has the advantage of signing the real SMTP message. It wouldn't hurt to see if the Perl implemenation is doing anything differently. If I assume the BadPenguin (or Agitos) signer is accurate, the solution may be to figure out how to mirror any encoding the SMTP Provider is doing or, even better, making sure that content is encoded well enough before it is set into the MimeMessage so that the SMTP Provider won't perform any additional encoding on it.
So far I have been using the MimeUtillity.encode methods on the headers and content before signing with BadPenguin (and Agitos does pretty much the same thing internally), however I the SMTP Transport must be doing some additional or different encoding.
It sounds like mimeMessage.writeTo(byteArrayOutputStream) will NOT have applied any such encoding but that is unclear?
>>Perl (or any implementation on the mail server itself)

Sorry - i assumed you were using Perl on the client, as with Java

>>It sounds like mimeMessage.writeTo(byteArrayOutputStream) will NOT have applied any such encoding but that is unclear?

It will only use the encoding in force at the time it's called. It won't apply any encoding i don't think
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
That was a lot of work ;)
I should add that the body of the MimeMessage being signed either needs to have no lines longer than around a little less than 1000 characters (I would have said 998 but it appears to need to be a little shorter than this sometimes) or have the encoding set to base64 (i.e. mimeMessage.setHeader("Content-Transfer-Encoding", "base64") prior to signing). The character set (e.g. us-ascii, utf-8, etc.) does not seem to matter so long as the transfer encoding is 7-bit.

Why there are not more and better examples of using DKIM out there I don't understand. Why is it an add-on and not a standard feature on e-mail servers? Is e-mail signing technology really being applied that often? Is anyone really rejecting or classifying e-mail as spam because it isn't signed or fails the header or body hash? For example, however Yahoo decides to block, delay, or classify e-mail as spam doesn't seem to relate to whether or not e-mail is signed, but that's a topic for another discussion. Thanks everyone for the input on this mini R&D project. :-)
You probably should feed all this back to BadPenguin

>>Is e-mail signing technology really being applied that often?

afaik all outgoing GMail is DKIM-signed

>>Is anyone really rejecting or classifying e-mail as spam because it isn't signed

It will probably have to gain more adoption before that happens. If MUAs did that right now, nearly everything would be marked as spam