Tuesday, August 3, 2010

PGP Encryption with Mule ESB

Recently I encounter an encryption problem in Mule ESB that surprised me...I could not figure out how to encrypt a message?!?!  I wasn't able to find an example on how PGP "encryption" should be configured even after numerous google and mule forum searches.  I was getting a little concerned that the PGP support in mule may be for decryption only.  The PGP Security documentation shows how to setup a security filter to decrypt messages but no example for encrypting (that may change soon...more on that later).  I finally posted a question on the mule forum and got some help from the community.

First of all, I should of been looking at the transformers in mule to perform encryption...duh!  I was so focused on trying to get the security filter to encrypt messages that I forgot about transformers...I know it's hard to image :)  I found out the there is an <encrypt-transformer> and was a little embarrassed that I didn't find it before.  So, I configured the <encrypt-transformer> to use the <pgp:keybased-encryption-strategy> just like I did for the security filter per the PGP Security documentation.  I thought "wow, that was easy", ran my configuration and got a NullPointerException. :(

I could see in the stacktrace that the problem was in the KeyBasedEncryptionStrategy, which was surprising because I was referencing the same <pgp:keybased-encryption-strategy> configuration in the security filter and that was working fine.  Then after taking a closer look at the security filter configuration I noticed that I was missing a reference to the credentialsAccessor.  The problem I was faced with is how to configured the <encrypt-transformer> to use the credentialsAccessor I was using?  Spring to the rescue!  The solution is quite simple.  Basically, all I had to do was inject the credentialsAccessor into the KeyBasedEncryptionStrategy.  To do that I had to configure a new spring bean (id="keyBEStrategy" below) and inject my keyManager and credentialsAccessor.  The nice thing about this solution is that the <encrypt-transformer> can still be used as is with just referencing the newly created strategy (i.e., strategy-ref="keyBEStrategy").  Here's an example configuration:

<?xml version="1.0"?>
<mule xmlns="http://www.mulesource.org/schema/mule/core/2.2"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:spring="http://www.springframework.org/schema/beans"
  xmlns:file="http://www.mulesource.org/schema/mule/file/2.2"
  xmlns:pgp="http://www.mulesource.org/schema/mule/pgp/2.2"
  xmlns:stdio="http://www.mulesource.org/schema/mule/stdio/2.2"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    http://www.mulesource.org/schema/mule/core/2.2
       http://www.mulesource.org/schema/mule/core/2.2/mule.xsd
    http://www.mulesource.org/schema/mule/file/2.2
       http://www.mulesource.org/schema/mule/file/2.2/mule-file.xsd
    http://www.mulesource.org/schema/mule/pgp/2.2
       http://www.mulesource.org/schema/mule/pgp/2.2/mule-pgp.xsd
    http://www.mulesource.org/schema/mule/stdio/2.2
       http://www.mulesource.org/schema/mule/stdio/2.2/mule-stdio.xsd">

   <file:connector name="fileConnector" pollingFrequency="10000" streaming="false" autoDelete="true">
       <file:expression-filename-parser/>
   </file:connector>
 
  <spring:bean id="pgpKeyManager" class="org.mule.module.pgp.PGPKeyRingImpl" init-method="initialise">
      <spring:property name="publicKeyRingFileName" value="<path to public keyring>/pubring.gpg"/>
      <spring:property name="secretKeyRingFileName" value="<path to private keyring>/secring.gpg"/>
      <!-- secretAliasId is the public key -->
      <spring:property name="secretAliasId" value="<public key id"/>
      <spring:property name="secretPassphrase" value="<password>"/>
  </spring:bean>
 
  <spring:bean id="keyBEStrategy" class="org.mule.module.pgp.KeyBasedEncryptionStrategy"
    init-method="initialise">
    <spring:property name="keyManager" ref="pgpKeyManager"/>
    <spring:property name="credentialsAccessor" ref="credentialAccessor"/>
  </spring:bean>
 
  <pgp:security-manager>
      <pgp:security-provider name="pgpSecurityProvider" keyManager-ref="pgpKeyManager"/>
      <pgp:keybased-encryption-strategy name="keyBasedEncryptionStrategy"
        keyManager-ref="pgpKeyManager"/>
  </pgp:security-manager>

  <spring:bean id="credentialAccessor" class="org.mule.module.pgp.FakeCredentialAccessor"/>

  <model name="fileInboundModel">
    <service name="fileInboundService">
      <inbound>
        <file:inbound-endpoint connector-ref="fileConnector"
                 path="./in">
                
                 <pgp:security-filter strategyName="keyBasedEncryptionStrategy"
                                      signRequired="true"
                                      credentialsAccessor-ref="credentialAccessor"
                                      keyManager-ref="pgpKeyManager"/>
                                     
        </file:inbound-endpoint>
      </inbound>
      <echo-component/>
      <outbound>
        <pass-through-router>
          <file:outbound-endpoint connector-ref="fileConnector" path="./encrypted"
            outputPattern="#[header:originalFilename]-#[function:datestamp].gpg">
            <encrypt-transformer name="pgpEncrypt" strategy-ref="keyBEStrategy"/>
          </file:outbound-endpoint>
        </pass-through-router>     
      </outbound>
    </service>
  </model>

</mule>

This workaround is pretty clean.  MuleSoft is going to review the solution and possibly update their documentation accordingly.

Cheers!