Back to Blog Archive

Using security features in Mule 4

Posted on: February 20, 2020
Author:
James

1. Introduction

The aim of this blog post is to showcase the use of a number of Mule 4 security features via a simple client-server case study. All the source code of this case study is freely available on this GitHub repository.

The case study makes use of features from the following three Mule 4 modules:

The client-server case study is explained in the next section. This is followed by the setup section, which contains all the details and explanations on how to fully set up both the client and the server applications from scratch. The blog concludes with some further points not included in the case study which you might want to consider in a production environment.

2. Client-Server Case Study

Two separate applications were developed for this case study: a Client and a Server. The whole process is triggered by you as a User via an HTTP call to the Client containing a plain text JSON message. In summary, the process will follow these steps:

  1. The user sends a plain-text message to the Client.
  2. The Client ensures that the message sent by the User is not null, otherwise, it will not process the message.
  3. The client calculates a Checksum on the message. Later on in step 10, the client will use this Checksum to ensure that the message hasn’t been corrupted in transit.
  4. The Client digitally signs the message.
  5. The Client encrypts the message.
  6. The encrypted message along with signature is sent to the Server.
  7. The Server decrypts the message.
  8. The Server validates the message using the digital signature sent by the Client.
  9. The decrypted message is returned (in plain-text) as a response to the Client.
  10. The Client validates the checksum it calculated earlier in step 3 on the Server’s response.
  11. The Client returns the encrypted message, the signature and the decrypted message to the User.

Moreover, on start-up, both applications will decrypt sensitive properties which are stored in an encrypted format. This will be explained later on in the blog post.


The following is a simple flow diagram illustrating the steps above:


Step 2 is straightforward to implement using the Validation module’s is-not-null operation. Step 3 can also be easily implemented using the Cryptography module’s calculate-checksum and validate-checksum operations.

Steps 4, 5, 7 and 8 (i.e. the signature, encryption, decryption and signature validation steps) require the use of public-key cryptography (aka asymmetric cryptography). The signature and signature validation operations require a public-private key pair: the Signature Public & Private key. The encryption and decryption operations require a separate public-private key pair of their own: the Encryption Public & Private Key. All of these operations are available in the Cryptography Module.

We would like the message sent by the Client to be attributed only to the Client. Moreover, we want anyone with access to the message and the digital signature to be able to verify that the message was truly sent by the Client. To achieve this, the Client needs to sign the message using the Signature Private key (which only the client should have access to) and anyone with access to the message, i.e. the Server, can verify (using the Signature Public key) that the message was truly sent by the client. Therefore, the Signature Private key will be the Client’s Private key and the Signature Public key will be the Server’s Public key.

We would also like any encrypted message received by the Server to be readable only by the Server. Moreover, we want that anyone wishing to communicate with the Server could be able to do so securely by encrypting the message. To achieve this, the Client (or anyone wishing to communicate with the Server) needs to encrypt the message using the Encryption Public key, so that only the Server with access to the Encryption Private key can decrypt the message. Therefore, the Encryption Private key will be the Server’s Private key and the Encryption Public key will be the Client’s Public key.

So in summary, the Client and the Server applications need 4 keys in total:

  1. Client Public key – made available to the Server, used to validate the Client’s digital signature.
  2. Client Private key – kept secret by the Client, used to generate digital signatures. This key is secured by a passphrase.
  3. Server Public key – made available to the Client, used to encrypt messages.
  4. Server Private key – kept secret by the Server, used to decrypt messages. This key is secured by a passphrase.

For Step 6, we opted for including the digital signature along with the message in the request body. Alternatively, the digital signature can be sent via an HTTP header. The important thing is that the receiver has access to the encrypted message and the digital signature separately, so that it can first decrypt the message and then validate the decrypted message with the signature.

Finally, to secure access to the Client and Server private keys’ passphrases, we stored these in an encrypted format in both the Client and Server property files. These are then being decrypted by both applications on start-up using the Secure Configuration Properties Module. This requires the generation and use of AES encryption keys. Further details are given in the Setup section below.

3. Setup

These are the main steps involved in the setup of the client-server case study described above:

  1. Generating Public-Private key pairs
  2. Mule configuration of the Cryptography Module operations
  3. Securing configuration properties

The next sections contain detailed explanations on how to perform each of these setup steps. In conclusion, we give a consolidated ordered break-down of all the steps you need to follow to set up your own keys from scratch.

3. 1. Generating Public-Private Key Pairs

We’ll use GnuPG to generate PGP Public-Private key pairs. GnuPG will store the generated key pairs in its home path defined by the environment variable GNUPGHOME. By default, GNUPGHOME is set to ~/.gnupg

Use this command to generate key pairs:

gpg --gen-key

You will be prompted to name your key pair and to enter an email address. Finally, you’re prompted to enter a passphrase.

Note: it is important to remember this passphrase because it is one of the details required to configure message signing and encryption.

If you’re using GnuPG 2.1 or a newer version, a pubring.kbx file will then be generated in GNUPGHOME directory containing all your Public and Private keys. For older GnuPG versions, two different .gpg files for Public and Private keys are generated. Mule configuration expects to read two keys from separate files, as generated by the older GnuPG version.

So, if you’re using GnuPG 2.1 or a newer version, you can extract the Private key file privateKey.gpg from pubring.kbx as follows:

gpg --export-secret-keys <email address provided> > privateKey.gpg

And you can extract the Public key file publicKey.gpg from pubring.kbx as follows:

gpg --output publicKey.gpg --export <email address provided>

If the --gen-key command does not generate any files, try to restart the GPG agent first as follows:

gpgconf --kill gpg-agent

If you want to set further details on the generated key (e.g. the expiration date of the key, more user details and comments) you can use the following command instead of gen-key:

gpg --full-generate-key

When configuring the cryptography module operations (i.e. signature, encryption, decryption and signature validation), apart from specifying the path to these generated keys, you also need to provide the key ID, the fingerprint (which is a hash value of the key) and the passphrase entered during --full-generate-key.

The key ID and the fingerprint can both be obtained from the response of the following command:

gpg --list-keys --keyid-format LONG <email address provided>

The response is as follows:

The key ID is the last 8 characters of sub value after rsa2048/. So, in the example response above, the key ID is 0EDCE334. The fingerprint is the whole sub value after rsa2048/. So, in the example response above, the fingerprint is 52746B930EDCE334.

3. 2. Mule Configuration of the Cryptography Module operations

The signature operation on the Client’s side depends on the Client’s Private key, whereas the validation of the signature on the Server’s side depends on the Client’s public key. The Client signs its message to the Server with its private key and promotes its Public key. The Server can then use the Client’s Public key to confirm that the message has truly been sent by the entity promoting this Public key i.e. the Client.

Client Signature Global Element:

Client Signature Operation:

Server Signature Validation Global Element:

Server Signature Validation Operation:

The encryption operation on the Client’s side depends on the Server’s Public key, whereas the decryption operation on the Server’s side depends on the Server’s Private key. The Client encrypts its message to the Server using the Publicly available Server’s Public key. Therefore, only the Server holding the corresponding Private key is able to decrypt the message, thus making the plain text message unreadable by other parties that are not holding the Server’s Private key.

Client Encryption Global Element:

Client Encryption Operation:

Server Decryption Global Element:

Server Decryption Operation:

3. 3. Securing Configuration Properties

First, generate a random 32-character (256-bit) string to be used as an AES key.

Then, encrypt properties using secure properties tool provided for free by Mulesoft as follows:

java -jar secure-properties-tool.jar string encrypt <algorithm> <mode> <key> <value>

There, <key> is the random 32 characters, and <value> is the plain text value to be encrypted. See here for the list of supported algorithms and modes. By default, AES is used as the encryption algorithm and CBC is used as the mode.

To decrypt a value, use the following command, where this time value is the encrypted message:

java -jar secure-properties-tool.jar string decrypt <algorithm> <mode> <key> <value>

You can also provide this tool with a YAML configuration file populated with plain text properties, and it will return the same file with the properties encrypted.

This is done by using the file parameter instead of string as follows:

java -jar secure-properties-tool.jar file encrypt
<algorithm> <mode> <key> <input file> <output file>

Moreover, if you wish, you can encrypt the whole file by providing a file-level parameter as follows:

java -jar secure-properties-tool.jar file-level encrypt
<algorithm> <mode> <key> <input file> <output file>

Any configuration property set with an encrypted value must be enclosed within ![ ]. In order to refer to secured property, prefix the property name with secure::, for example, ${secure::my.secured.property.name}. On start-up, Mule will decrypt every configuration enclosed within ![ ] using the provided AES key in the <secureproperties:config>.

Obviously, we want to avoid setting this key in plain-text inside the Mule config. Therefore, this key should instead be set as an environment variable on the server.

When running applications in Anypoint Studio, you can easily set an environment variable via Run > Run configurations… > Environment Tab > New…

3. 4. Setup breakdown

The following is an ordered break-down of all the steps you need to follow to set up your own keys from scratch:

  1. Generate two random 32-character strings to serve as AES keys for securing Private key passphrases.
  2. Generate two pairs of PGP Public-Private keys, using a tool such as GnuPG as explained above.
  3. Take note of all the Public-Private key details (i.e. key ID, fingerprint and passphrase).
  4. Store the Client Private key in the Client application, for message signature.
  5. Store the Server Public key in the Client application, for message encryption.
  6. Store the Server Private key the Server application, for message decryption.
  7. Store the Client Public key in the Server application, for signature validation.
  8. Encrypt the Client Private key passphrase with the Client AES key using the secure properties tool, as explained above.
  9. Set this encrypted value in the Client’s configuration file, along with the key ID and fingerprint.
  10. Encrypt the Server Private key passphrase with the Server AES key using the secure properties tool, as explained above.
  11. Set this encrypted value in the Server’s configuration file, along with the key ID and fingerprint.
  12. Set the Client and Server key ID and fingerprint in the Client’s properties file.
  13. Set the Client and Server key ID and fingerprint in the Server’s properties file.
  14. On start-up of the Client application, set the Client AES key as an environment variable. This allows the application to decrypt the Client Private key passphrase.
  15. On start-up of the Server application, set the Server AES key as an environment variable. This allows the application to decrypt the Server Private key passphrase.

4. Other Considerations

Both the Client and the Server applications need to somehow have access to the plain text AES key required to decrypt the Client and the Server Private key passphrases. In the setup, we have stored this key as an environment variable on the server hosting both applications. Therefore, any user/application on the server which has the same or more access rights (e.g. root user) than the Mule application will also be able to access these credentials.

This is a problem you cannot avoid. You can make it a bit “harder” to access the credentials by storing them in a separate third-party secrets management server, but the same problem remains because the Mule application has to be able to access the credentials needed to connect to this third-party server.

Also, anyone who can run a ps command or view a Java console will be able to see the decrypted values that are stored in the Mule app’s memory.

What I can suggest is the following:

  1. Secure as much as possible access to the server. If an unauthorised user already has access to the server, then a lot more damage can be done apart from access to sensitive credentials.
  2. The Mule application should be assigned its own user account on the server. This way, we can make any file or environment variable needed to access sensitive credentials readable only by users logged in with Mule’s account. This will at least block users who are not logged in on Mule or root account.
  3. Some third-party secrets management servers allow you to connect to them via one time tokens. This way, all secrets will be accessible only once using a particular token. Using this feature, we can set this token in the application, which is usable only once to load all properties in memory on start-up (after which the token will expire).
    This at least does away with the need for securing this token, but still, the properties can be accessed from memory by a root user. Moreover, with this approach, the application cannot be simply restarted, given that a new token needs to be generated each time. This means that a re-deployment is needed each time an application needs to restart.

Note that for the purpose of this simple client-server setup, we haven’t registered the Client and the Server public keys with a Certificate Authority (CA). In a production environment, public keys should always be registered with a CA in order to avoid the man-in -the-middle attacks.

Author:
James

Comments

Contact Us

Ricston Ltd.
Triq G.F. Agius De Soldanis,
Birkirkara, BKR 4850,
Malta
MT: +356 2133 4457
UK: +44 (0)2071935107

Send our experts a message

Need Help?
Ask our Experts!