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.
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.
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.
To symmetrically encrypt and decrypt an entire message, perform the following steps:
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());
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);
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)
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)
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 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.
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");
keytool
): http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/keytool.html3.145.97.170