More MIME Tricks

Several days and a lot of testing later, I think I have the answer: don’t try to build a play-by-email service in Google AppEngine. The limitations imposed by the environment make it a bad place to do a lot of email traffic that requires anything other than text/plain or text/html. Because Google has defined javax.mail.Transport to use the AppEngine mail service, you can’t choose to connect to a different SMTP host; if you specify an external SMTP server (such as SendGrid or Pobox) in the Session, Google’s Transport implementation ignores it and still uses the AppEngine mail service. When you try to use a web API to send your message through SendGrid, that screws up the MIME headers and your message won’t validate (if it’s even readable) at the receiver’s end.

That said, I have finally figured out how to prepare and send a PGP signed MIME message such that it arrives and validates properly. Since nobody else has written that down on the web (that I could find), I’ll detail the recipe here.

While it is true that one may use the bouncycastle PGP libraries for free, it is also true that using them correctly is hard, unless you already know how to use them correctly. Their documentation and examples suck, unless what you really want to do is manage keyrings and decide what order you’re going to process packets in. For way less money than my time is worth, a company called DidiSoft is happy to sell you a library that does the low-level using bouncycastle correctly part and provides a straightforward high level API for doing the things you actually want to do, like encrypt and sign email. Here’s an interesting side note: I used Google to search for PGP libraries and tutorials and sifted through a lot of Stack Overflow posts and useless bouncycastle pages; then I used DuckDuckGo to do the same search and the first thing that popped up was DidiSoft. Google, apparently, wants to waste my time.

The other thing that nobody on the web will tell you but which I feel like an idiot for not figuring out sooner is that the Apache Commons project offers an email library that makes proper formatting of MIME headers a cinch.

The final bit that wasn’t in the DidiSoft example or documentation but which unit testing (and here’s a plug for my favorite SMTP and POP testing server) revealed is that the MimeMessage that the DidiSoft library gives back from its signing call can’t be sent directly but should be embedded within another MimeMessage.

So, with all that said, here’s some code:

public void testSendPGPMIME() throws Exception {
    Session session = Session.getDefaultInstance(new Properties());
    MimeMessage email = new MimeMessage(session);
    email.setContent("This message has been signed", "text/plain");

    PGPMailLib mailUtil = new PGPMailLib(); // DidiSoft library
    KeyStore serverSecretKey = new KeyStore();
    serverSecretKey.loadFromStream(new ByteArrayInputStream(/* ASCII-armored secret key */),
                                   /* secret key password */);
    email = mailUtil.signMessage(email, serverSecretKey, /* signing key ID */, /* secret key password */);

    // This is the part nobody tells you
    MimeMultipart messageParts = (MimeMultipart) email.getContent();

    MultiPartEmail mpe = new MultiPartEmail();
    mpe.setHostName("localhost"); // SMTP server
    mpe.setSmtpPort(1081); // SMTP port
    mpe.setFrom("unit_test@localhost");
    mpe.addTo("test_recipient@dumbster");
    mpe.setSubject("Test sending signed text");
    mpe.addPart(messageParts);
    mpe.send();
}

 

Published by pirateguillermo

I play the bagpipes. I program computers. I support my family in their various endeavors, and I enjoy my wonderful life.

Leave a Reply