/**
* 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.component.crypto;
import java.io.InputStream;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.util.Collections;
import java.util.Map;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.impl.JndiRegistry;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.apache.camel.util.jsse.KeyStoreParameters;
import org.junit.Before;
import org.junit.Test;
import static org.apache.camel.component.crypto.DigitalSignatureConstants.KEYSTORE_ALIAS;
import static org.apache.camel.component.crypto.DigitalSignatureConstants.SIGNATURE_PRIVATE_KEY;
import static org.apache.camel.component.crypto.DigitalSignatureConstants.SIGNATURE_PUBLIC_KEY_OR_CERT;
public class SignatureTests extends CamelTestSupport {
private KeyPair keyPair;
private String payload = "Dear Alice, Rest assured it's me, signed Bob";
@Override
protected JndiRegistry createRegistry() throws Exception {
JndiRegistry registry = super.createRegistry();
KeyStore keystore = loadKeystore();
Certificate cert = keystore.getCertificate("bob");
KeyStoreParameters keystoreParameters = new KeyStoreParameters();
keystoreParameters.setPassword("letmein");
keystoreParameters.setResource("./ks.keystore");
registry.bind("signatureParams", keystoreParameters);
registry.bind("keystore", keystore);
registry.bind("myPublicKey", cert.getPublicKey());
registry.bind("myCert", cert);
registry.bind("myPrivateKey", keystore.getKey("bob", "letmein".toCharArray()));
return registry;
}
@Override
protected RouteBuilder[] createRouteBuilders() throws Exception {
return new RouteBuilder[] {new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: basic
from("direct:keypair").to("crypto:sign:basic?privateKey=#myPrivateKey", "crypto:verify:basic?publicKey=#myPublicKey", "mock:result");
// END SNIPPET: basic
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: algorithm
keyPair = getKeyPair("RSA");
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
// we can set the keys explicitly on the endpoint instances.
context.getEndpoint("crypto:sign:rsa?algorithm=MD5withRSA", DigitalSignatureEndpoint.class).setPrivateKey(privateKey);
context.getEndpoint("crypto:verify:rsa?algorithm=MD5withRSA", DigitalSignatureEndpoint.class).setPublicKey(publicKey);
from("direct:algorithm").to("crypto:sign:rsa?algorithm=MD5withRSA", "crypto:verify:rsa?algorithm=MD5withRSA", "mock:result");
// END SNIPPET: algorithm
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: rsa-sha1
keyPair = getKeyPair("RSA");
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
// we can set the keys explicitly on the endpoint instances.
context.getEndpoint("crypto:sign:rsa?algorithm=SHA1withRSA", DigitalSignatureEndpoint.class).setPrivateKey(privateKey);
context.getEndpoint("crypto:verify:rsa?algorithm=SHA1withRSA", DigitalSignatureEndpoint.class).setPublicKey(publicKey);
from("direct:rsa-sha1").to("crypto:sign:rsa?algorithm=SHA1withRSA", "crypto:verify:rsa?algorithm=SHA1withRSA", "mock:result");
// END SNIPPET: rsa-sha1
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: rsa-sha256
keyPair = getKeyPair("RSA");
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
// we can set the keys explicitly on the endpoint instances.
context.getEndpoint("crypto:sign:rsa?algorithm=SHA256withRSA", DigitalSignatureEndpoint.class).setPrivateKey(privateKey);
context.getEndpoint("crypto:verify:rsa?algorithm=SHA256withRSA", DigitalSignatureEndpoint.class).setPublicKey(publicKey);
from("direct:rsa-sha256").to("crypto:sign:rsa?algorithm=SHA256withRSA", "crypto:verify:rsa?algorithm=SHA256withRSA", "mock:result");
// END SNIPPET: rsa-sha256
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: buffersize
from("direct:buffersize").to("crypto:sign:buffer?privateKey=#myPrivateKey&buffersize=1024", "crypto:verify:buffer?publicKey=#myPublicKey&buffersize=1024", "mock:result");
// END SNIPPET: buffersize
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: provider
from("direct:provider").to("crypto:sign:provider?privateKey=#myPrivateKey&provider=SUN", "crypto:verify:provider?publicKey=#myPublicKey&provider=SUN", "mock:result");
// END SNIPPET: provider
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: certificate
from("direct:certificate").to("crypto:sign:withcert?privateKey=#myPrivateKey", "crypto:verify:withcert?certificate=#myCert", "mock:result");
// END SNIPPET: certificate
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: keystore
from("direct:keystore").to("crypto:sign:keystore?keystore=#keystore&alias=bob&password=letmein", "crypto:verify:keystore?keystore=#keystore&alias=bob", "mock:result");
// END SNIPPET: keystore
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: keystore
from("direct:keystoreParameters").to("crypto:sign:keyStoreParameters?keyStoreParameters=#signatureParams&alias=bob&password=letmein",
"crypto:verify:keyStoreParameters?keyStoreParameters=#signatureParams&alias=bob", "mock:result");
// END SNIPPET: keystore
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: signature-header
from("direct:signature-header").to("crypto:sign:another?privateKey=#myPrivateKey&signatureHeader=AnotherDigitalSignature",
"crypto:verify:another?publicKey=#myPublicKey&signatureHeader=AnotherDigitalSignature", "mock:result");
// END SNIPPET: signature-header
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: random
from("direct:random").to("crypto:sign:another?privateKey=#myPrivateKey&secureRandom=#someRandom", "crypto:verify:another?publicKey=#myPublicKey&secureRandom=#someRandom",
"mock:result");
// END SNIPPET: random
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: alias
from("direct:alias-sign").to("crypto:sign:alias?keystore=#keystore");
from("direct:alias-verify").to("crypto:verify:alias?keystore=#keystore", "mock:result");
// END SNIPPET: alias
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: headerkey
from("direct:headerkey-sign").to("crypto:sign:alias");
from("direct:headerkey-verify").to("crypto:verify:alias", "mock:result");
// END SNIPPET: headerkey
}
}, new RouteBuilder() {
public void configure() throws Exception {
// START SNIPPET: clearheaders
from("direct:headers").to("crypto:sign:headers?privateKey=#myPrivateKey", "crypto:verify:headers?publicKey=#myPublicKey&clearHeaders=false", "mock:result");
// END SNIPPET: clearheaders
}
}};
}
@Test
public void testBasicSignatureRoute() throws Exception {
setupMock();
sendBody("direct:keypair", payload);
assertMockEndpointsSatisfied();
MockEndpoint mock = getMockEndpoint("mock:result");
Exchange e = mock.getExchanges().get(0);
Message result = e == null ? null : e.hasOut() ? e.getOut() : e.getIn();
assertNull(result.getHeader(DigitalSignatureConstants.SIGNATURE));
}
@Test
public void testSetAlgorithmInRouteDefinition() throws Exception {
setupMock();
sendBody("direct:algorithm", payload);
assertMockEndpointsSatisfied();
}
@Test
public void testRSASHA1() throws Exception {
setupMock();
sendBody("direct:rsa-sha1", payload);
assertMockEndpointsSatisfied();
}
@Test
public void testRSASHA256() throws Exception {
setupMock();
sendBody("direct:rsa-sha256", payload);
assertMockEndpointsSatisfied();
}
@Test
public void testSetBufferInRouteDefinition() throws Exception {
setupMock();
sendBody("direct:buffersize", payload);
assertMockEndpointsSatisfied();
}
@Test
public void testSetRandomInRouteDefinition() throws Exception {
setupMock();
sendBody("direct:random", payload);
assertMockEndpointsSatisfied();
}
@Test
public void testSetProviderInRouteDefinition() throws Exception {
if (isJavaVendor("ibm")) {
return;
}
// can only be run on SUN JDK
setupMock();
sendBody("direct:provider", payload);
assertMockEndpointsSatisfied();
}
@Test
public void testSetCertificateInRouteDefinition() throws Exception {
setupMock();
sendBody("direct:certificate", payload);
assertMockEndpointsSatisfied();
}
@Test
public void testSetKeystoreInRouteDefinition() throws Exception {
setupMock();
sendBody("direct:keystore", payload);
assertMockEndpointsSatisfied();
}
@Test
public void testSetKeystoreParametersInRouteDefinition() throws Exception {
setupMock();
sendBody("direct:keystoreParameters", payload);
assertMockEndpointsSatisfied();
}
@Test
public void testSignatureHeaderInRouteDefinition() throws Exception {
setupMock();
Exchange signed = getMandatoryEndpoint("direct:signature-header").createExchange();
signed.getIn().setBody(payload);
template.send("direct:signature-header", signed);
assertNotNull(signed.getIn().getHeader("AnotherDigitalSignature"));
assertMockEndpointsSatisfied();
}
@Test
public void testProvideAliasInHeader() throws Exception {
setupMock();
// START SNIPPET: alias-send
Exchange unsigned = getMandatoryEndpoint("direct:alias-sign").createExchange();
unsigned.getIn().setBody(payload);
unsigned.getIn().setHeader(DigitalSignatureConstants.KEYSTORE_ALIAS, "bob");
unsigned.getIn().setHeader(DigitalSignatureConstants.KEYSTORE_PASSWORD, "letmein".toCharArray());
template.send("direct:alias-sign", unsigned);
Exchange signed = getMandatoryEndpoint("direct:alias-sign").createExchange();
signed.getIn().copyFrom(unsigned.getOut());
signed.getIn().setHeader(KEYSTORE_ALIAS, "bob");
template.send("direct:alias-verify", signed);
// START SNIPPET: alias-send
assertMockEndpointsSatisfied();
}
@Test
public void testProvideKeysInHeader() throws Exception {
setupMock();
Exchange unsigned = getMandatoryEndpoint("direct:headerkey-sign").createExchange();
unsigned.getIn().setBody(payload);
// create a keypair
KeyPair pair = getKeyPair("DSA");
// sign with the private key
unsigned.getIn().setHeader(SIGNATURE_PRIVATE_KEY, pair.getPrivate());
template.send("direct:headerkey-sign", unsigned);
// verify with the public key
Exchange signed = getMandatoryEndpoint("direct:alias-sign").createExchange();
signed.getIn().copyFrom(unsigned.getOut());
signed.getIn().setHeader(SIGNATURE_PUBLIC_KEY_OR_CERT, pair.getPublic());
template.send("direct:headerkey-verify", signed);
assertMockEndpointsSatisfied();
}
@Test
public void testProvideCertificateInHeader() throws Exception {
setupMock();
Exchange unsigned = getMandatoryEndpoint("direct:signature-property").createExchange();
unsigned.getIn().setBody(payload);
// create a keypair
KeyStore keystore = loadKeystore();
Certificate certificate = keystore.getCertificate("bob");
PrivateKey pk = (PrivateKey)keystore.getKey("bob", "letmein".toCharArray());
// sign with the private key
unsigned.getIn().setHeader(SIGNATURE_PRIVATE_KEY, pk);
template.send("direct:headerkey-sign", unsigned);
// verify with the public key
Exchange signed = getMandatoryEndpoint("direct:alias-sign").createExchange();
signed.getIn().copyFrom(unsigned.getOut());
signed.getIn().setHeader(SIGNATURE_PUBLIC_KEY_OR_CERT, certificate);
template.send("direct:headerkey-verify", signed);
assertMockEndpointsSatisfied();
}
@Test
public void testVerifyHeadersNotCleared() throws Exception {
setupMock();
template.requestBody("direct:headers", payload);
assertMockEndpointsSatisfied();
assertMockEndpointsSatisfied();
MockEndpoint mock = getMockEndpoint("mock:result");
Exchange e = mock.getExchanges().get(0);
Message result = e == null ? null : e.hasOut() ? e.getOut() : e.getIn();
assertNotNull(result.getHeader(DigitalSignatureConstants.SIGNATURE));
}
private MockEndpoint setupMock() {
MockEndpoint mock = getMockEndpoint("mock:result");
mock.expectedBodiesReceived(payload);
return mock;
}
public Exchange doTestSignatureRoute(RouteBuilder builder) throws Exception {
return doSignatureRouteTest(builder, null, Collections.<String, Object>emptyMap());
}
public Exchange doSignatureRouteTest(RouteBuilder builder, Exchange e, Map<String, Object> headers) throws Exception {
CamelContext context = new DefaultCamelContext();
try {
context.addRoutes(builder);
context.start();
MockEndpoint mock = context.getEndpoint("mock:result", MockEndpoint.class);
mock.setExpectedMessageCount(1);
ProducerTemplate template = context.createProducerTemplate();
if (e != null) {
template.send("direct:in", e);
} else {
template.sendBodyAndHeaders("direct:in", payload, headers);
}
assertMockEndpointsSatisfied();
return mock.getReceivedExchanges().get(0);
} finally {
context.stop();
}
}
@Before
public void setUp() throws Exception {
setUpKeys("DSA");
disableJMX();
super.setUp();
}
public void setUpKeys(String algorithm) throws Exception {
keyPair = getKeyPair(algorithm);
}
public KeyPair getKeyPair(String algorithm) throws NoSuchAlgorithmException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(algorithm);
keyGen.initialize(512, new SecureRandom());
return keyGen.generateKeyPair();
}
public static KeyStore loadKeystore() throws Exception {
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream in = SignatureTests.class.getResourceAsStream("/ks.keystore");
keystore.load(in, "letmein".toCharArray());
return keystore;
}
public Certificate getCertificateFromKeyStore() throws Exception {
Certificate c = loadKeystore().getCertificate("bob");
return c;
}
public PrivateKey getKeyFromKeystore() throws Exception {
return (PrivateKey)loadKeystore().getKey("bob", "letmein".toCharArray());
}
}