blob: 180af28131b429fbd4002887056be8a5ef8c2da0 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.converter.crypto;
import java.io.ByteArrayInputStream;
import java.security.Key;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;
public class CryptoDataFormatTest extends CamelTestSupport {
@Test
public void testBasicSymmetric() throws Exception {
doRoundTripEncryptionTests("direct:basic-encryption");
}
@Test
public void testSymmetricWithInitVector() throws Exception {
doRoundTripEncryptionTests("direct:init-vector");
}
@Test
public void testSymmetricWithInlineInitVector() throws Exception {
doRoundTripEncryptionTests("direct:inline");
}
@Test
public void testSymmetricWithHMAC() throws Exception {
doRoundTripEncryptionTests("direct:hmac");
}
@Test
public void testSymmetricWithMD5HMAC() throws Exception {
doRoundTripEncryptionTests("direct:hmac-algorithm");
}
@Test
public void testSymmetricWithSHA256HMAC() throws Exception {
doRoundTripEncryptionTests("direct:hmac-sha-256-algorithm");
}
@Test
public void testKeySuppliedAsHeader() throws Exception {
KeyGenerator generator = KeyGenerator.getInstance("DES");
Key key = generator.generateKey();
Exchange unecrypted = getMandatoryEndpoint("direct:key-in-header-encrypt").createExchange();
unecrypted.getIn().setBody("Hi Alice, Be careful Eve is listening, signed Bob");
unecrypted.getIn().setHeader(CryptoDataFormat.KEY, key);
unecrypted = template.send("direct:key-in-header-encrypt", unecrypted);
validateHeaderIsCleared(unecrypted);
MockEndpoint mock = setupExpectations(context, 1, "mock:unencrypted");
Exchange encrypted = getMandatoryEndpoint("direct:key-in-header-decrypt").createExchange();
encrypted.getIn().copyFrom(unecrypted.getIn());
encrypted.getIn().setHeader(CryptoDataFormat.KEY, key);
template.send("direct:key-in-header-decrypt", encrypted);
assertMockEndpointsSatisfied();
Exchange received = mock.getReceivedExchanges().get(0);
validateHeaderIsCleared(received);
}
@Test
public void test3DESECBSymmetric() throws Exception {
doRoundTripEncryptionTests("direct:3des-ecb-encryption");
}
@Test
public void test3DESCBCSymmetric() throws Exception {
doRoundTripEncryptionTests("direct:3des-cbc-encryption");
}
@Test
public void testAES128ECBSymmetric() throws Exception {
if (checkUnrestrictedPoliciesInstalled()) {
doRoundTripEncryptionTests("direct:aes-128-ecb-encryption");
}
}
private void validateHeaderIsCleared(Exchange ex) {
Object header = ex.getIn().getHeader(CryptoDataFormat.KEY);
assertTrue(!ex.getIn().getHeaders().containsKey(CryptoDataFormat.KEY) || "".equals(header) || header == null);
}
private void doRoundTripEncryptionTests(String endpointUri) throws Exception {
doRoundTripEncryptionTests(endpointUri, Collections.<String, Object>emptyMap());
}
private void doRoundTripEncryptionTests(String endpoint, Map<String, Object> headers) throws Exception {
MockEndpoint encrypted = setupExpectations(context, 3, "mock:encrypted");
MockEndpoint unencrypted = setupExpectations(context, 3, "mock:unencrypted");
String payload = "Hi Alice, Be careful Eve is listening, signed Bob";
template.sendBodyAndHeaders(endpoint, payload, headers);
template.sendBodyAndHeaders(endpoint, payload.getBytes(), headers);
template.sendBodyAndHeaders(endpoint, new ByteArrayInputStream(payload.getBytes()), headers);
assertMocksSatisfied(encrypted, unencrypted, payload);
}
private void assertMocksSatisfied(MockEndpoint encrypted, MockEndpoint unencrypted, String payload) throws Exception {
awaitAndAssert(unencrypted);
awaitAndAssert(encrypted);
for (Exchange e : unencrypted.getReceivedExchanges()) {
assertEquals(payload, e.getIn().getMandatoryBody(String.class));
}
for (Exchange e : encrypted.getReceivedExchanges()) {
byte[] ciphertext = e.getIn().getMandatoryBody(byte[].class);
assertNotSame(payload, new String(ciphertext));
}
}
protected RouteBuilder[] createRouteBuilders() throws Exception {
return new RouteBuilder[] {new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: basic
KeyGenerator generator = KeyGenerator.getInstance("DES");
CryptoDataFormat cryptoFormat = new CryptoDataFormat("DES", generator.generateKey());
from("direct:basic-encryption")
.marshal(cryptoFormat)
.to("mock:encrypted")
.unmarshal(cryptoFormat)
.to("mock:unencrypted");
// END SNIPPET: basic
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: init-vector
KeyGenerator generator = KeyGenerator.getInstance("DES");
byte[] initializationVector = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
CryptoDataFormat cryptoFormat = new CryptoDataFormat("DES/CBC/PKCS5Padding", generator.generateKey());
cryptoFormat.setInitializationVector(initializationVector);
from("direct:init-vector")
.marshal(cryptoFormat)
.to("mock:encrypted")
.unmarshal(cryptoFormat)
.to("mock:unencrypted");
// END SNIPPET: init-vector
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: inline-init-vector
KeyGenerator generator = KeyGenerator.getInstance("DES");
byte[] initializationVector = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
SecretKey key = generator.generateKey();
CryptoDataFormat cryptoFormat = new CryptoDataFormat("DES/CBC/PKCS5Padding", key);
cryptoFormat.setInitializationVector(initializationVector);
cryptoFormat.setShouldInlineInitializationVector(true);
CryptoDataFormat decryptFormat = new CryptoDataFormat("DES/CBC/PKCS5Padding", key);
decryptFormat.setShouldInlineInitializationVector(true);
from("direct:inline")
.marshal(cryptoFormat)
.to("mock:encrypted")
.unmarshal(decryptFormat)
.to("mock:unencrypted");
// END SNIPPET: inline-init-vector
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: hmac
KeyGenerator generator = KeyGenerator.getInstance("DES");
CryptoDataFormat cryptoFormat = new CryptoDataFormat("DES", generator.generateKey());
cryptoFormat.setShouldAppendHMAC(true);
from("direct:hmac")
.marshal(cryptoFormat)
.to("mock:encrypted")
.unmarshal(cryptoFormat)
.to("mock:unencrypted");
// END SNIPPET: hmac
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: hmac-algorithm
KeyGenerator generator = KeyGenerator.getInstance("DES");
CryptoDataFormat cryptoFormat = new CryptoDataFormat("DES", generator.generateKey());
cryptoFormat.setShouldAppendHMAC(true);
cryptoFormat.setMacAlgorithm("HmacMD5");
from("direct:hmac-algorithm")
.marshal(cryptoFormat)
.to("mock:encrypted")
.unmarshal(cryptoFormat)
.to("mock:unencrypted");
// END SNIPPET: hmac-algorithm
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: hmac-sha256-algorithm
KeyGenerator generator = KeyGenerator.getInstance("DES");
CryptoDataFormat cryptoFormat = new CryptoDataFormat("DES", generator.generateKey());
cryptoFormat.setShouldAppendHMAC(true);
cryptoFormat.setMacAlgorithm("HmacSHA256");
from("direct:hmac-sha-256-algorithm")
.marshal(cryptoFormat)
.to("mock:encrypted")
.unmarshal(cryptoFormat)
.to("mock:unencrypted");
// END SNIPPET: hmac-sha256-algorithm
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: key-in-header
CryptoDataFormat cryptoFormat = new CryptoDataFormat("DES", null);
/**
* Note: the header containing the key should be cleared after
* marshalling to stop it from leaking by accident and
* potentially being compromised. The processor version below is
* arguably better as the key is left in the header when you use
* the DSL leaks the fact that camel encryption was used.
*/
from("direct:key-in-header-encrypt")
.marshal(cryptoFormat)
.removeHeader(CryptoDataFormat.KEY)
.to("mock:encrypted");
from("direct:key-in-header-decrypt").unmarshal(cryptoFormat).process(new Processor() {
public void process(Exchange exchange) throws Exception {
exchange.getIn().getHeaders().remove(CryptoDataFormat.KEY);
exchange.getOut().copyFrom(exchange.getIn());
}
}).to("mock:unencrypted");
// END SNIPPET: key-in-header
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: 3DES-ECB
KeyGenerator generator = KeyGenerator.getInstance("DESede");
CryptoDataFormat cryptoFormat = new CryptoDataFormat("DESede/ECB/PKCS5Padding", generator.generateKey());
from("direct:3des-ecb-encryption")
.marshal(cryptoFormat)
.to("mock:encrypted")
.unmarshal(cryptoFormat)
.to("mock:unencrypted");
// END SNIPPET: 3DES-ECB
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: 3DES-CBC
KeyGenerator generator = KeyGenerator.getInstance("DES");
byte[] iv = new byte[8];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
Key key = generator.generateKey();
CryptoDataFormat encCryptoFormat = new CryptoDataFormat("DES/CBC/PKCS5Padding", key);
encCryptoFormat.setInitializationVector(iv);
encCryptoFormat.setShouldInlineInitializationVector(true);
CryptoDataFormat decCryptoFormat = new CryptoDataFormat("DES/CBC/PKCS5Padding", key);
decCryptoFormat.setShouldInlineInitializationVector(true);
from("direct:3des-cbc-encryption")
.marshal(encCryptoFormat)
.to("mock:encrypted")
.unmarshal(decCryptoFormat)
.to("mock:unencrypted");
// END SNIPPET: 3DES-CBC
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: AES-128-ECB
KeyGenerator generator = KeyGenerator.getInstance("AES");
CryptoDataFormat cryptoFormat = new CryptoDataFormat("AES/ECB/PKCS5Padding", generator.generateKey());
from("direct:aes-128-ecb-encryption")
.marshal(cryptoFormat)
.to("mock:encrypted")
.unmarshal(cryptoFormat)
.to("mock:unencrypted");
// END SNIPPET: AES-128-ECB
}
}};
}
private void awaitAndAssert(MockEndpoint mock) throws InterruptedException {
mock.assertIsSatisfied();
}
public MockEndpoint setupExpectations(CamelContext context, int expected, String mock) {
MockEndpoint mockEp = context.getEndpoint(mock, MockEndpoint.class);
mockEp.expectedMessageCount(expected);
return mockEp;
}
public static boolean checkUnrestrictedPoliciesInstalled() {
try {
byte[] data = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
SecretKey key192 = new SecretKeySpec(
new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17},
"AES");
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, key192);
c.doFinal(data);
return true;
} catch (Exception e) {
//
}
return false;
}
}