Tuesday, October 9, 2012

Direct Transmission in Java : The Go-Cart Version

Over the weekend, John Moehrke wrote this post about View, Download and TRANSMIT, focusing specifically on the Transmit part, and how Direct plays a role.  He notes that Transmit can be implemented without a HISP.  I was curious to see how easy it would be to generate a fully Direct compliant message with as little work as possible in Java.  I've already got an e-mail certificate and key, and I already have John's public key and e-mail address.

So, I want to send him an Direct message.  What do I have to do to make that work?  Fortunately, a good deal of code to support this has already been created in the Direct Bare Metal Project.  You can download the necessary components from Google Code here.  The API Documentation on the Direct Wiki seems to be pretty decent, and if you dig around long enough, eventually, you can find the JavaDoc.

As I read it, what I need to be able to do is first construct a DefaultNHINDAgent, giving it a way to resolve my private certificate and key, and John's public certificate, and a way to understand that John's certificate is one I trust (comes from a Trust Anchor).  For simplicity sake, I'll put my certificate in a key store, John's in a trust store, and the CA issuing John's certificate in Trust Anchor.

The first challenge is finding everything I need.  I'm building a go-cart here.  I don't need (or want) James, Tomcat, and a bunch of other stuff.  This isn't Bare Metal.  It's a Soap box, some twine and bailing wire, and hope that it runs downhill.

What do you need:
  1. agent-1.5.2.jar The Direct Agent Library
  2. direct-common-1.1.1.jar The Direct Comment Library
  3. Apache Commons Logging (General Logging during Message Processing)
  4. Apache Commons IO 
  5. Apache Commons Codec (Base 64 Encoding)
  6. Bouncy Castle SMIME Library (Crypto)  [Release 140 for JDK 1.5]
  7. Bouncy Castle JCE Provider (Crypto) [Release 140 for JDK 1.5]
  8. mail.jar from the Java Mail Library
OK, now, where to get BC 140?  Well, where I got it from was from the version of James that comes with the Bare-Metal non-HISP Java Project.  You can find the necessary libraries in the direct/james/2.3.2/apps/james/SAR-INF/lib folder in this tarball that is in the Direct Google Code Library.  Download the tarball, unzip/tar it, and extract bcmail-jdk15-140.jar and bcprov-jdk15-140.jar to where you are putting the rest of your libraries.

Next, you'll need to follow the instructions to install the JCE (Java Cryptographic Extensions) for whichever JDK you are using.  Download them from here.  Then follow these instructions to install them.

Once you have all of that, you should be good to go.

We're going to do this in five steps.  

1: Configure JavaMail

The first step is to configure JavaMail to send from Google Mail, or another SMTP server you have access to.

Properties properties = new Properties();
properties.put("mail.smtp.host", "smtp.gmail.com");
properties.put("mail.smtp.auth", "true");
properties.setProperty( "mail.smtp.port", "587" );
properties.put("mail.smtp.starttls.enable", "true");
Session session = Session.getDefaultInstance(properties,
  new Authenticator() {
    public PasswordAuthentication getPasswordAuthentication() 
    {
return new PasswordAuthentication("account", "password");
    }  
  }
);

2: Configure the DirectAgent

The next step is to configure an NHINDirectAgent to do all of the work for us.

CertificateResolver certResolver = 
  new KeyStoreCertificateStore("private.jks", "password", "password");
KeyStoreCertificateStore certResolver2 = 
  new KeyStoreCertificateStore("public.jks", "password", "password");
TrustAnchorResolver trustAnchor = 
  new DefaultTrustAnchorResolver(certResolver2.getAllCertificates());
NHINDAgent agent = 
  new DefaultNHINDAgent("gmail.com", certResolver, certResolver2, trustAnchor);

I've created two files to work with this bit of code.  The first, private.jks contains the certificate and private key I use with my GMAIL Account to sign e-mails.  I got mine here for free.  I also created public.jks which contains the just the certificate for my G-mail account, and the certificates for mine and John Moehrke's e-mail accounts.  If I've ever sent you a signed e-mail, you have that too.  And if John's ever sent you signed e-mail, you also have that.  I exported my signing certificate and key from Outlook in PKCS12 format, and then converted to JKS using this little trick (as it turns out, I still had jetty in the same location).  I used keytool to import the various other certificates that I exported from Outlook into Base64 encoded format.  Oh, and to create public.jks from those files, just use keytool the same way you would to create a trust store.

For what I need, the trust method is this: If I have the certificate's anchor in my list of public keys, then it's trusted.  Otherwise it isn't. I had to dabble a bit with the right way to create the DefaultTrustAnchorResolver, but the code you see is what finally did it for me.

3: Create the Message

The next step is to create the message:

MimeMessage mx = new MimeMessage(session);
mx.addHeader("To", "keith.boone@ge.com");
mx.addHeader("From", "kwboone@gmail.com");
mx.addHeader("Date", new Date().toString());
mx.addHeader("Subject", "Hello");
mx.setText("Hello Again!");

4: Process Message

This step performs certificate resolution, trust resolution, and encryption, according to the Direct protocol.
Message msg = new Message(mx);
OutgoingMessage oMessage = new OutgoingMessage(msg);
OutgoingMessage processedMessage = agent.processOutgoing(oMessage);

5: Send the Message

The last step below grabs the content into a new MimeMessage configured to use the JavaMail session we started with in Step 1, and sends it.

MimeMessage mxo = new MimeMessage(session,
  new ByteArrayInputStream(

    processedMessage.serializeMessage().getBytes("UTF-8")));
Transport.send(mxo);

You'll note a couple of things:
  1. No HISPs were consulted in the construction or transmission of this message.
  2. Certificate lookup was very simple.  A more complex mechanism using LDAP could have been implemented.  You may need to consult with a number of locations to locate recipient certificates.
  3. Trust was also simple.  These are outbound messages only.  So long as they are, and the certificate is signed somewhere along the way by a trust anchor I care about, then it goes.
For John's discussion about what you need for the Transmit portion of VDT, this Go-Cart could use a few more things to soup it up:  A motor (LDAP Certificate lookup), and a Roll-Cage (A little bit better trust model than a certificate from John or I, perhaps just list the common e-mail certificate signer anchors), and away you go.

  -- Keith

P.S.  And the short answer to what I need to do to make that work?  About a day of coding.



3 comments:

  1. The agent developer's guide was put together for a scenario like this. Glad to see it was useful.

    -g

    ReplyDelete
  2. If one implemented the same functionality without using the Direct Bare Metal Project, would it still be Direct compliant? The reason I ask is because the Applicability Statement for Secure Health Transport seems a bit confusing on whether a Direct-compliant implementation is required to implement both DNS and LDAP certificate-discovery mechanisms. Section 2.3 of the standard first says that a compliant implementation must have *a method* for discovering message recipients' certificates. It goes on to say that a compliant implementation must be able to discover certificates via DNS and LDAP, but the wording implies (to me, at least) that this only implies to *universal* certificate discovery. It seems unclear to me whether such universal certificate discovery is itself required by the Applicability Statement. Maybe that's my real question - must a Direct-compliant implementation be capable of universal certificate discovery (and therefore be capable of doing so via both DNS and LDAP)?

    ReplyDelete