/** * 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()); } }