Encrypting and decrypting a message

Camel's Crypto Component is used when you need to encrypt and decrypt an entire message. It provides a Camel Data Format that allows you to marshal (encrypt) or unmarshal (decrypt) your data.

The Crypto Component supports both symmetric (using a shared password) and asymmetric (using public key of recipient) encryption—the latter through PGP.

This recipe will show you how to configure basic symmetric encryption. It will show both marshaling (encrypting) and unmarshaling (decrypting) data. These actions would normally be done in different Camel routes on different systems.

Getting ready

The Java code for this recipe is located in the org.camelcookbook.security.encryption package. The Spring XML files are located under src/main/resources/META-INF/spring and prefixed with encryption.

To use Camel's Crypto Component, add the following to the dependencies section of your Maven POM:

<dependency>
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-crypto</artifactId>
  <version>${camel-version}</version>
</dependency>

To make life easier around accessing keystores in Spring, you should use the Spring Crypto Utils library by adding the following dependency:

<dependency>
  <groupId>com.google.code.spring-crypto-utils</groupId>
  <artifactId>spring-crypto-utils</artifactId>
  <version>1.3.0</version>
</dependency>

Before using the cryptographic capabilities available in Camel to encrypt and decrypt a message you will require a keystore, which is a repository of keys.

For the purposes of this example we will use the keytool utility that is included as part of the Java Development Kit (JDK) to generate the keystore.

Generate a key into a new store that will be shared between the encrypting and decrypting systems as follows:

# keytool -genseckey -alias shared -keypass sharedKeyPassword -keystore shared.jceks -storepass sharedKeystorePassword -v -storetype JCEKS

This process will generate a key using the DES algorithm.

Note

The keystore type JCEKS is a particular type of store used for storing private keys. It is different from the default JKS store that is used in the Digitally signing and verifying messages recipe, which only stores key pairs.

Give this keystore to the people maintaining the system that will be the counterparty to the encryption process. Both systems involved in the encryption/decryption process need access to the same key.

How to do it...

To symmetrically encrypt and decrypt an entire message, perform the following steps:

  1. If using Spring, add the crypt namespace from Spring Crypto Utils to the beans definition:
    <beans ...
      xmlns:crypt="http://springcryptoutils.com/schema/crypt"
      xsi:schemaLocation="...
        http://springcryptoutils.com/schema/crypt
          http://springcryptoutils.com/schema/crypt.xsd">

    Next, load the keystore from the specified location, and fetch the shared key from it:

    <crypt:keystore id="keyStore" 
                    location="classpath:shared.jceks"
                    password="sharedKeystorePassword"
                    type="JCEKS"/>
    <crypt:secretKey id="secretKey"
                     keystore-ref="keyStore"
                     alias="shared"
                     password="sharedKeyPassword"/>

    If using Java only, load the shared key using a java.security.Keystore instance as follows:

    KeyStore keyStore = KeyStore.getInstance("JCEKS");
    
    // here we are loading a keystore stored in our JAR
    ClassLoader classLoader = getClass().getClassLoader();
    keyStore.load(classLoader.getResourceAsStream("shared.jceks"), "sharedKeystorePassword".toCharArray());
    
    Key sharedKey = keyStore.getKey("shared", 
        "sharedKeyPassword".toCharArray());
  2. If using the XML DSL, define the Crypto Data Format within the Camel context, referring to the shared key in use by the id set in the previous step:
    <camelContext xmlns="http://camel.apache.org/schema/spring">
      <dataFormats>
        <crypto id="sharedKeyCrypto"
                algorithm="DES" 
                keyRef="secretKey"/>
      </dataFormats>
      <!-- ... -->
    </camelContext>

    If using Java, instantiate the CryptoDataFormat inside your RouteBuilder implementation instead:

    CryptoDataFormat sharedKeyCrypto = 
        new CryptoDataFormat("DES", sharedKey);
  3. Add a marshal step to your route in order to encrypt the body of the message.

    In the XML DSL, this is done through referring to the data format by its id:

    <marshal ref="sharedKeyCrypto"/>

    In the Java DSL, you pass in the data format directly:

    .marshal(sharedKeyCrypto)
  4. To decrypt the encrypted body of a message, add an unmarshal step to your route that refers to the data format.

    In the XML DSL, this is done as follows:

    <unmarshal ref="sharedKeyCrypto"/>

    The same thing is expressed in the Java DSL through:

    .unmarshal(sharedKeyCrypto)

How it works...

The camel-crypto library uses the Camel data format mechanism to take an unencrypted representation of the data and marshal it. This produces an encrypted version of the data using the standard java.security APIs without exposing you to any of the gory details of that API.

Unmarshaling using a CryptoDataFormat initialized with the same key will give you the original message.

There's more...

There are numerous properties available on the CryptoDataFormat class that allow you to customize the algorithm used, the buffer size, the initialization vector, the message authentication algorithm (message authentication code or MAC), and whether or not a digital signature (keyed-hash message authentication code or HMAC)) should be appended to the encrypted data. As such, none of the flexibility of the Java Cryptography Extensions is abstracted away should you need to make use of it.

Note

Algorithm names are defined in the JCE Standard Algorithm Names Documentation. The algorithm used has to be supported by the JCE providers on both the system that you encrypt the message on, and the one that decrypts it. Not all algorithms are available on all JVMs due to export restrictions. For most purposes the default algorithm is perfectly adequate.

If you are sending messages to or receiving messages from a number of systems, each of which encrypts its messages differently, it is possible to dynamically determine the key to be used by the data format. To do this, you need to instantiate the CryptoDataFormat without referring to a java.security.Key:

CryptoDataFormat crypto = new CryptoDataFormat("DES", null);

In the XML DSL, the instantiation of the data format is performed as follows:

<crypto id="sharedKeyCrypto" algorithm="DES"/>

Within your route, you then set the CamelCryptoKey (CryptoDataFormat.KEY) header to a Key instance that the data format should use, before invoking the marshal or unmarshal step.

In the following Java DSL example, the encrypt route fetches a Key from the Camel Registry and sets it on this header. The Key is fetched by name depending on the value of the system header, which is set on the message:

from("direct:encrypt").id("encrypt")
  .process(new Processor() {
    @Override
    public void process(Exchange exchange) throws Exception {
      Registry registry = exchange.getContext().getRegistry();
      Message in = exchange.getIn();
      Key key = registry.lookupByNameAndType(
          "shared_" + in.getHeader("system"), 
          Key.class);
      in.setHeader(CryptoDataFormat.KEY, key);
    }
  })
  .marshal(crypto)
  .log("Message encrypted: ${body}")
  .to("direct:decrypt");

from("direct:decrypt").id("decrypt")
  .unmarshal(crypto)
  .log("Message decrypted: ${body}")
  .to("mock:decrypted");

Note

The camel-crypto library also provides a PGPDataFormat for asymmetric (public key) cryptography. This uses the PGP (Pretty Good Privacy) format as implemented by the Bouncy Castle Java Cryptography APIs.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.97.170