Reading Time: 16 minutes

Pretty Good Privacy (PGP) is a well-known encryption system that MuleSoft supports

Steps for using MuleSoft with GnuPG 

There are many PGP implementations; in this guide, we plan to cover how to use Mule PGP (Pretty Good Privacy) Encryption with GnuGP (GPG). 

3 steps for Mule PGP Encryption

The following steps will allow users to encrypt Mule PGP with GnuPG.

1. Install GnuPG and generate keys

First, download GnuPG. Installation is straightforward. For this guide, we’ll be using version “GPG 2.2.32”.

GnuPG itself is mostly command line-based. If you use Windows, there is a UI tool you can use with GPG. If you use MacOS, the default GPG installation comes with a rudimentary UI with very limited functions. 

latest report
Learn why we are the Leaders in API management and iPaaS

Generate the public and private keys 

The default install path for GPG is /usr/local/bin on MacBook. Using the command window, simply execute:

gpg --gen-key 

Follow the prompt to enter your name and an email address. Name and email are used as the identifier for the generated keys. When finished, you will end up with something like this:

pub  rsa3072 2022-01-26 [SC] [expires: 2024-01-26]  A112FB858721314133D89D53CB5B317A61C3BB9F
uid  john.doe <john.doe@foo.com>
sub  rsa3072 2022-01-26 [E] [expires: 2024-01-26]

Please save a copy of the output text, and note that depending on the GPG version, the output format can vary slightly.

It’s incredibly important to note that the “pub” section shows the fingerprint for the public key “A112FB858721314133D89D53CB5B317A61C3BB9F”. In the next section, when using the “gpg –list,”  we’ll also see a subkey with a different fingerprint value.

Export the private and public keys:

%gpg --export-secret-keys --armor john.doe > "my-pri.asc" 
%gpg --export --armor "john.doe"  > "my-pub.asc" 

where the “--armor” option will output in ascii format.

As shown above, “my-pri.asc” is the private keyring file, and “my-pub.asc” is the public keyring file.

Viewing the keys:

%gpg --list-secret-keys --keyid-format LONG

You should see something like below:

/Users/john/.gnupg/pubring.kbx
-------------------------------
sec   rsa3072/CB5B317A61C3BB9F 2022-01-26 [SC] [expires: 2024-01-26]
      A112FB858721314133D89D53CB5B317A61C3BB9F
uid                 [ultimate] john doe<john.doe@mulesoft.com>
ssb   rsa3072/D69E938EF3F73BBE 2022-01-26 [E] [expires: 2024-01-26]

The output shows:

  • The main public key (“sec”) and a subkey (“ssb”). 
  • The main key shows the full fingerprint and the last 16 digits CB5B317A61C3BB9F
  • The subkey has a different fingerprint D69E938EF3F73BBE

The significance of these values will become clear when they are used inside the Mule project.

2. Encrypt and decrypt inside the Mule app 

Create a Mule project, import the “crypto” module if needed by clicking the “Search in Exchange” button in the studio. Copy both the public and private key files to the project resource folder. Drag a PGP encrypt or PGP encrypt binary icon to the mule flow.

Configure the settings as below:

Configuration settings
<crypto:pgp-config name="Encrypt-config"
doc:name="Crypto Pgp" doc:id="4bffef52-e115-4aba-9981-bcd56559fb8c"
publicKeyring="my-pub.asc" privateKeyring="my-pri.asc">
<crypto:pgp-key-infos>
<crypto:pgp-asymmetric-key-info
keyId="foo" fingerprint="A112F…A61C3BB9F"
passphrase="Testpgp123" />
</crypto:pgp-key-infos>
</crypto:pgp-config>

Please note the following: 

  “publicKeyRing” and “privateKeyRing”: 

are the paths pointing to the respective key files.

  “keyId”:  

This value will have more significance if there are multiple keys (subkeys) inside the key file. In the test case, we have only one main key and one subkey. You can use any random string in this field. For a real project, it’s best to pick a meaningful name for readability. 

  “fingerprint”: 

With the GnuPG and Mule PGP module, this field can be very tricky. The following descriptions are based on trial-and-error and observation. They are not published in the official Mule document:

  • If both encryption and decryption are done using the Mule application, then you can use the fingerprints of either the main key or the subkey. The mainkey can be either the full key or the last 16 digits.  
  • If the encryption is done using the GPG tool outside of the Mule app, then the Mule application can only use the subkey fingerprint to decrypt. Please see the next section for details.

“Passphrase” – this is what you entered when generating the key with the GPG tool.

The code snippet shows a flow to both encrypt and decrypt a test input. Either CURL or Postman can be used to test the app: 

<flow name="pgp-external-decrypt"
doc:id="c6380382-4eda-4478-8bce-8301006f2ffb">
<http:listener doc:name="Listener"
doc:id="131594a2-71e3-4dd0-9a88-db28f904a3c9"
config-ref="HTTP_Listener_config" path="/dec" />
<logger level="INFO" doc:name="input-log"
doc:id="f970a76f-1857-4b50-a517-3d7dfc170093"
message="#[{"key": p('secure::mysecret')}]" />
<crypto:pgp-decrypt doc:name="Pgp decrypt"
doc:id="a1e4c89e-b74e-4e51-8b53-f41d6f4206ef"
config-ref="DECRYPT-config" />
<logger level="INFO" doc:name="output-log"
doc:id="61c50bbb-d055-4cac-b1f6-0b92c4384f73" message="#[payload]" />
</flow>

3. Encrypt with GPG tool; decrypt with a Mule app

Using the Mule app to both encrypt and decrypt data is not very widely used. A more practical scenario would be to use the GPG tool to independently encrypt a data file, then using the Mule app to decrypt the data.

As it turns out, if you use the GPG tool to encrypt data outside of the Mule app, then you must use the subkey fingerprint to decrypt a payload. 

In the sample configuration below, very importantly, the subkey fingerprint “D69E938EF3F73BBE” is used instead of the main key fingerprint. Also only the private key “my-pri.asc” is used because we’re only doing the decryption.

<crypto:pgp-config name="DECRYPT-config"
doc:name="Crypto Pgp" doc:id="1bd72dd6-867b-431c-9da6-fb1928113b45"
privateKeyring="my-pri.asc">
<crypto:pgp-key-infos>
<crypto:pgp-asymmetric-key-info
keyId="foo" fingerprint="D69E938EF3F73BBE" passphrase="TDTestpgp123" />
</crypto:pgp-key-infos>
</crypto:pgp-config>

Test step 1 – Encrypt with GPG command line tool:

gpg --encrypt --cipher-algo AES256 --armor test.txt > outputmsg.txt

 Please note: “--armor” option is for the ascii output. 

Test step 2 – To decrypt using Mule app is straightforward, be sure the crypto config uses the subkey fingerprint as shown above.

<flow name="pgp-external-decrypt"
doc:id="c6380382-4eda-4478-8bce-8301006f2ffb">
<http:listener doc:name="Listener"  doc:id="131594a2-71e3-4dd0-9a88-db28f904a3c9"
config-ref="HTTP_Listener_config" path="/dec" />
<crypto:pgp-decrypt doc:name="Pgp decrypt" doc:id="a1e4c89e-b74e-4e51-8b53-f41d6f4206ef"
config-ref="DECRYPT-config" />
<logger level="INFO" doc:name="output-log"
doc:id="61c50bbb-d055-4cac-b1f6-0b92c4384f73" message="#[payload]" />
</flow>

CURL script or Postman can be used to test this flow, when submitting the encrypted content, please set the content-type to “text/plain”.

How to protect the private key file

To decrypt a PGP message in a Mule app, the private key file must be attached to the project as shown in the above example. 

A passphrase is required to decrypt a message. This passphrase can be encrypted as standard Mule properties. This is the commonly used security practice.. However, some users may insist the private key file be encrypted. 

There is no official solution to encrypt the key file. In this post, we’ll provide a solution to encrypt the keyring file. This is an undocumented solution. The solution hinges on an important behavior of how Mule runtime handles the app deployment that uses the PGP encryption. Users should evaluate the solution to determine whether the benefits warrant the extra work to encrypt the private keyring.

By default, the “privateKeyring” attribute is a file path. It needs to point to the correct keyring file. During the app deployment, the Mule runtime will validate the configuration. If any of the configuration fields or the file is incorrect, the deployment will fail. However, if a variable is used in the “privateKeyring” file, the deployment will skip the validation. We’ll exploit this behavior and use a “bait-and-switch” trick to build a solution. 

Here is the summary of the solution:

  • Generate the private key file in ascii format: “my-pri.asc”
  • Use Mule JCE encryption to encrypt this file: “my-pri-jce.enc”, copy this file to the project resource folder. This is a one time operation. We’ll use JCE to decrypt this file to “my-pri-jce.as”, which will be the original private keyring. There are other encryption options, we selected JCE for the convenience.
  • In PGP crypto-config, set “privateKeyring” to a variable: “priKeyPath”
  • Set the variable priKeyPath to “p(‘mule.home’) ++ ‘/apps/’ ++ p(‘app.name’) ++ ‘/my-pri-jce.asc’” before the PGP decryption is invoked. This path resolves to the physical path where the Mule app is running. Also note the file ‘my-pri-jce.asc’ still needs to be generated when the app starts to run.
  • Add a file read to load “my-pri-jce.enc” before PGP decryption. Use JCE to decrypt the file, which will result in the original keyring, save the payload to “p(‘mule.home’) ++ ‘/apps/’ ++ p(‘app.name’) ++ ‘/my-pri-jce.asc’”. 
  • Now PGP decryption will be able to use this newly generated keyring to decrypt the payload. If you have followed along, you may notice this solution is essentially a bait-and-switch. We first set “privateKeyring” to a file, but we only generate this file after the app has started running.

One obvious question is why not use Mule secure properties to encrypt the keyring file? The reason is the keyring file contains many lines and the isecure-properties-tool.jar is unable to encrypt a property value with multiple lines. Also, if you manually merge the keyring file to a single line, the key becomes invalid. 

Configuration Java keystore
<crypto:pgp-config name="DECRYPT-config" doc:name="Crypto Pgp" doc:id="1bd7…b45"
privateKeyring="#[vars.priKeyPath]" publicKeyring="my-pub.asc">
  <crypto:pgp-key-infos>
<crypto:pgp-asymmetric-key-info keyId="foo" fingerprint="D69E938EF3F73BBE" 
passphrase="TDTestpgp123" />
</crypto:pgp-key-infos>
</crypto:pgp-config>
<crypto:jce-config name="Crypto_Jce" doc:name="Crypto Jce" doc:id="7fe0…6" keystore="#[p('mule.home') ++ '/apps/' ++ p('app.name') ++ '/sample.jceks']" password="password" type="JCEKS">
<crypto:jce-key-infos >
<crypto:jce-symmetric-key-info keyId="sample" alias="sample" password="password" />
</crypto:jce-key-infos>
</crypto:jce-config>
<flow name="pgp-encrypted-private-key" doc:id="c…fb" >
<http:listener doc:name="Listener" doc:id="1…c9" config-ref="HTTP_Listener_config" path="/dec"/>
<set-variable value="#[payload]" doc:name="save-inbound-payload" doc:id="05…8e7" variableName="origPayload"/>
<file:read doc:name="read-jec-encrypted-keyring" doc:id="f…c31a" path="#[p('mule.home') ++ '/apps/' ++ p('app.name') ++ '/my-pri-jce.enc']" />
<crypto:jce-decrypt doc:name="Jce decrypt" doc:id="c9…10" config-ref="Crypto_Jce" algorithm="AES" keyId="sample"/>
<set-variable value="#[p('mule.home') ++ '/apps/' ++ p('app.name') ++ '/my-pri-jce.asc']" doc:name="priKeyPath" doc:id="ea0…92" variableName="priKeyPath" />
<file:write doc:name="Write-unencrypted-keyring" doc:id="d6…57" path="#[vars.priKeyPath]"/>
<logger level="INFO" doc:name="show-payload" doc:id="fa7…a6" message="#[payload]"/>
<set-payload value="#[vars.origPayload]" doc:name="restore-original-Payload" doc:id="64…2c6b" />
<logger level="INFO" doc:name="show-payload" doc:id="f9…93" message="#[payload]" />
<crypto:pgp-decrypt doc:name="Pgp decrypt" doc:id="a…6ef" config-ref="DECRYPT-config" />
<logger level="INFO" doc:name="show-payload" doc:id="61…73" message="#[payload]" />
</flow>
my-pri.asc

This image still shows my-pri.asc in the resource folder. If you encrypt the private keyring, you can delete this file.