| /* |
| * 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.dataformat.xmlsecurity; |
| |
| import java.nio.charset.Charset; |
| import java.security.Key; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import javax.crypto.KeyGenerator; |
| import javax.xml.transform.OutputKeys; |
| |
| import org.w3c.dom.Document; |
| import org.w3c.dom.NodeList; |
| |
| import org.apache.camel.builder.RouteBuilder; |
| import org.apache.camel.component.mock.MockEndpoint; |
| import org.apache.camel.converter.jaxp.XmlConverter; |
| import org.apache.camel.support.jsse.KeyStoreParameters; |
| import org.apache.camel.test.junit4.CamelTestSupport; |
| import org.apache.commons.codec.Charsets; |
| import org.apache.xml.security.encryption.XMLCipher; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| /** |
| * Unit test of the encryptXML data format. |
| */ |
| public class XMLSecurityDataFormatTest extends CamelTestSupport { |
| |
| protected static String testCypherAlgorithm = XMLCipher.AES_128; |
| |
| TestHelper xmlsecTestHelper = new TestHelper(); |
| |
| private Key defaultKey; |
| |
| @Override |
| public boolean isUseRouteBuilder() { |
| return false; |
| } |
| |
| @Override |
| @Before |
| public void setUp() throws Exception { |
| super.setUp(); |
| context.getGlobalOptions().put(XmlConverter.OUTPUT_PROPERTIES_PREFIX + OutputKeys.ENCODING, "UTF-8"); |
| KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); |
| keyGenerator.init(256); |
| defaultKey = keyGenerator.generateKey(); |
| } |
| |
| |
| /* |
| * Encryption Tests |
| */ |
| |
| @Test |
| public void testFullPayloadXMLEncryption() throws Exception { |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal().secureXML(defaultKey.getEncoded()) |
| .to("mock:encrypted"); |
| } |
| }); |
| xmlsecTestHelper.testEncryption(context); |
| } |
| |
| @Test |
| public void testPartialPayloadXMLContentEncryption() throws Exception { |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal().secureXML("//cheesesites/italy/cheese", true, defaultKey.getEncoded()) |
| .to("mock:encrypted"); |
| } |
| }); |
| xmlsecTestHelper.testEncryption(context); |
| } |
| |
| @Test |
| public void testPartialPayloadMultiNodeXMLContentEncryption() throws Exception { |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal().secureXML("//cheesesites/*/cheese", true, defaultKey.getEncoded()) |
| .to("mock:encrypted"); |
| } |
| }); |
| xmlsecTestHelper.testEncryption(context); |
| } |
| |
| @Test |
| public void testPartialPayloadXMLElementEncryptionWithKeyAndAlgorithm() throws Exception { |
| final byte[] bits128 = { |
| (byte) 0x08, (byte) 0x09, (byte) 0x0A, (byte) 0x0B, |
| (byte) 0x0C, (byte) 0x0D, (byte) 0x0E, (byte) 0x0F, |
| (byte) 0x10, (byte) 0x11, (byte) 0x12, (byte) 0x13, |
| (byte) 0x14, (byte) 0x15, (byte) 0x16, (byte) 0x17}; |
| |
| final String passCode = new String(bits128); |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal().secureXML("//cheesesites/netherlands", false, passCode, XMLCipher.AES_128) |
| .to("mock:encrypted"); |
| } |
| }); |
| xmlsecTestHelper.testEncryption(context); |
| } |
| |
| @Test |
| public void testPartialPayloadXMLElementEncryptionWithByteKeyAndAlgorithm() throws Exception { |
| final byte[] bits192 = { |
| (byte)0x24, (byte)0xf2, (byte)0xd3, (byte)0x45, |
| (byte)0xc0, (byte)0x75, (byte)0xb1, (byte)0x00, |
| (byte)0x30, (byte)0xd4, (byte)0x3d, (byte)0xf5, |
| (byte)0x6d, (byte)0xaa, (byte)0x7d, (byte)0xc2, |
| (byte)0x85, (byte)0x32, (byte)0x2a, (byte)0xb6, |
| (byte)0xfe, (byte)0xed, (byte)0xbe, (byte)0xef}; |
| |
| final Charset passCodeCharset = Charsets.UTF_8; |
| final String passCode = new String(bits192, passCodeCharset); |
| byte[] bytes = passCode.getBytes(passCodeCharset); |
| assertTrue(bits192.length != bytes.length); |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal().secureXML("//cheesesites/netherlands", false, bits192, XMLCipher.AES_192) |
| .to("mock:encrypted"); |
| } |
| }); |
| xmlsecTestHelper.testEncryption(context); |
| } |
| |
| |
| @Test |
| public void testFullPayloadAsymmetricKeyEncryption() throws Exception { |
| KeyStoreParameters tsParameters = new KeyStoreParameters(); |
| tsParameters.setPassword("password"); |
| tsParameters.setResource("sender.ts"); |
| |
| final XMLSecurityDataFormat xmlEncDataFormat = new XMLSecurityDataFormat(); |
| xmlEncDataFormat.setKeyCipherAlgorithm(XMLCipher.RSA_v1dot5); |
| xmlEncDataFormat.setKeyOrTrustStoreParameters(tsParameters); |
| xmlEncDataFormat.setXmlCipherAlgorithm(testCypherAlgorithm); |
| xmlEncDataFormat.setRecipientKeyAlias("recipient"); |
| |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal(xmlEncDataFormat).to("mock:encrypted"); |
| } |
| }); |
| xmlsecTestHelper.testEncryption(context); |
| } |
| |
| @Test |
| public void testPartialPayloadAsymmetricKeyEncryptionWithContextTruststoreProperties() throws Exception { |
| final KeyStoreParameters tsParameters = new KeyStoreParameters(); |
| tsParameters.setPassword("password"); |
| tsParameters.setResource("sender.ts"); |
| |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal().secureXML("//cheesesites/italy/cheese", true, "recipient", testCypherAlgorithm, XMLCipher.RSA_v1dot5, tsParameters) |
| .to("mock:encrypted"); |
| } |
| }); |
| xmlsecTestHelper.testEncryption(context); |
| } |
| |
| @Test |
| public void testAsymmetricEncryptionAddKeyValue() throws Exception { |
| KeyStoreParameters tsParameters = new KeyStoreParameters(); |
| tsParameters.setPassword("password"); |
| tsParameters.setResource("sender.ts"); |
| |
| final XMLSecurityDataFormat xmlEncDataFormat = new XMLSecurityDataFormat(); |
| xmlEncDataFormat.setKeyOrTrustStoreParameters(tsParameters); |
| xmlEncDataFormat.setXmlCipherAlgorithm(testCypherAlgorithm); |
| xmlEncDataFormat.setRecipientKeyAlias("recipient"); |
| xmlEncDataFormat.setAddKeyValueForEncryptedKey(true); |
| |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal(xmlEncDataFormat).to("mock:encrypted"); |
| } |
| }); |
| Document doc = xmlsecTestHelper.testEncryption(TestHelper.XML_FRAGMENT, context); |
| NodeList nodeList = |
| doc.getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "RSAKeyValue"); |
| Assert.assertTrue(nodeList.getLength() > 0); |
| } |
| |
| @Test |
| public void testAsymmetricEncryptionNoKeyValue() throws Exception { |
| KeyStoreParameters tsParameters = new KeyStoreParameters(); |
| tsParameters.setPassword("password"); |
| tsParameters.setResource("sender.ts"); |
| |
| final XMLSecurityDataFormat xmlEncDataFormat = new XMLSecurityDataFormat(); |
| xmlEncDataFormat.setKeyOrTrustStoreParameters(tsParameters); |
| xmlEncDataFormat.setXmlCipherAlgorithm(testCypherAlgorithm); |
| xmlEncDataFormat.setRecipientKeyAlias("recipient"); |
| xmlEncDataFormat.setAddKeyValueForEncryptedKey(false); |
| |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal(xmlEncDataFormat).to("mock:encrypted"); |
| } |
| }); |
| Document doc = xmlsecTestHelper.testEncryption(TestHelper.XML_FRAGMENT, context); |
| NodeList nodeList = |
| doc.getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "RSAKeyValue"); |
| Assert.assertTrue(nodeList.getLength() == 0); |
| } |
| |
| /* |
| * Decryption Tests |
| */ |
| @Test |
| public void testFullPayloadXMLDecryption() throws Exception { |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal().secureXML(defaultKey.getEncoded()).to("mock:encrypted") |
| .unmarshal().secureXML(defaultKey.getEncoded()).to("mock:decrypted"); |
| } |
| }); |
| xmlsecTestHelper.testDecryption(context); |
| } |
| |
| @Test |
| public void testPartialPayloadXMLContentDecryption() throws Exception { |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal().secureXML("//cheesesites/italy/cheese", true, defaultKey.getEncoded()).to("mock:encrypted") |
| .unmarshal().secureXML("//cheesesites/italy/cheese", true, defaultKey.getEncoded()).to("mock:decrypted"); |
| } |
| }); |
| xmlsecTestHelper.testDecryption(context); |
| } |
| |
| @Test |
| public void testPartialPayloadMultiNodeXMLContentDecryption() throws Exception { |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal().secureXML("//cheesesites/*/cheese", true, defaultKey.getEncoded()).to("mock:encrypted") |
| .unmarshal().secureXML("//cheesesites/*/cheese", true, defaultKey.getEncoded()).to("mock:decrypted"); |
| } |
| }); |
| xmlsecTestHelper.testDecryption(context); |
| } |
| |
| @Test |
| public void testPartialPayloadXMLElementDecryptionWithKey() throws Exception { |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal().secureXML("//cheesesites/france/cheese", false, defaultKey.getEncoded()).to("mock:encrypted") |
| .unmarshal().secureXML("//cheesesites/france", false, defaultKey.getEncoded()).to("mock:decrypted"); |
| } |
| }); |
| xmlsecTestHelper.testDecryption(context); |
| } |
| |
| @Test |
| public void testXMLElementDecryptionWithoutEncryptedKey() throws Exception { |
| if (!TestHelper.HAS_3DES) { |
| return; |
| } |
| String passPhrase = "this is a test passphrase"; |
| |
| byte[] bytes = passPhrase.getBytes(); |
| final byte[] keyBytes = Arrays.copyOf(bytes, 24); |
| for (int j = 0, k = 16; j < 8;) { |
| keyBytes[k++] = keyBytes[j++]; |
| } |
| |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("timer://foo?period=5000&repeatCount=1"). |
| to("language:constant:resource:classpath:org/apache/camel/component/xmlsecurity/EncryptedMessage.xml") |
| .unmarshal() |
| .secureXML("/*[local-name()='Envelope']/*[local-name()='Body']", |
| true, keyBytes, XMLCipher.TRIPLEDES) |
| .to("mock:decrypted"); |
| } |
| }); |
| xmlsecTestHelper.testDecryptionNoEncryptedKey(context); |
| } |
| |
| @Test |
| public void testPartialPayloadXMLContentDecryptionWithKeyAndAlgorithm() throws Exception { |
| final byte[] bits128 = { |
| (byte) 0x08, (byte) 0x09, (byte) 0x0A, (byte) 0x0B, |
| (byte) 0x0C, (byte) 0x0D, (byte) 0x0E, (byte) 0x0F, |
| (byte) 0x10, (byte) 0x11, (byte) 0x12, (byte) 0x13, |
| (byte) 0x14, (byte) 0x15, (byte) 0x16, (byte) 0x17}; |
| final String passCode = new String(bits128); |
| |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal().secureXML("//cheesesites/italy", true, passCode, XMLCipher.AES_128).to("mock:encrypted") |
| .unmarshal().secureXML("//cheesesites/italy", true, passCode, XMLCipher.AES_128).to("mock:decrypted"); |
| } |
| }); |
| xmlsecTestHelper.testDecryption(context); |
| } |
| |
| @Test |
| public void testFullPayloadAsymmetricKeyDecryption() throws Exception { |
| |
| final KeyStoreParameters tsParameters = new KeyStoreParameters(); |
| tsParameters.setPassword("password"); |
| tsParameters.setResource("sender.ts"); |
| |
| final KeyStoreParameters ksParameters = new KeyStoreParameters(); |
| ksParameters.setPassword("password"); |
| ksParameters.setResource("recipient.ks"); |
| |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal().secureXML("", true, "recipient", testCypherAlgorithm, XMLCipher.RSA_v1dot5, tsParameters).to("mock:encrypted") |
| .unmarshal().secureXML("", true, "recipient", testCypherAlgorithm, XMLCipher.RSA_v1dot5, ksParameters).to("mock:decrypted"); |
| } |
| }); |
| xmlsecTestHelper.testDecryption(context); |
| } |
| |
| @Test |
| public void testFullPayloadAsymmetricKeyDecryptionWithKeyPassword() throws Exception { |
| |
| final KeyStoreParameters tsParameters = new KeyStoreParameters(); |
| tsParameters.setPassword("password"); |
| tsParameters.setResource("sender.ts"); |
| |
| final KeyStoreParameters ksParameters = new KeyStoreParameters(); |
| ksParameters.setPassword("password"); |
| ksParameters.setResource("recipient-with-key-pass.ks"); |
| |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal().secureXML("", true, "recipient", testCypherAlgorithm, XMLCipher.RSA_v1dot5, tsParameters).to("mock:encrypted") |
| .unmarshal().secureXML("", true, "recipient", testCypherAlgorithm, XMLCipher.RSA_v1dot5, ksParameters, "keyPassword").to("mock:decrypted"); |
| } |
| }); |
| xmlsecTestHelper.testDecryption(context); |
| } |
| |
| @Test |
| public void testPartialPayloadAsymmetricKeyDecryption() throws Exception { |
| final Map<String, String> namespaces = new HashMap<>(); |
| namespaces.put("ns1", "http://cheese.xmlsecurity.camel.apache.org/"); |
| |
| final KeyStoreParameters tsParameters = new KeyStoreParameters(); |
| tsParameters.setPassword("password"); |
| tsParameters.setResource("sender.ts"); |
| |
| final KeyStoreParameters ksParameters = new KeyStoreParameters(); |
| ksParameters.setPassword("password"); |
| ksParameters.setResource("recipient.ks"); |
| |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal().secureXML("//ns1:cheesesites/italy", namespaces, true, "recipient", testCypherAlgorithm, XMLCipher.RSA_v1dot5, tsParameters).to("mock:encrypted") |
| .unmarshal().secureXML("//ns1:cheesesites/italy", namespaces, true, "recipient", testCypherAlgorithm, XMLCipher.RSA_v1dot5, ksParameters).to("mock:decrypted"); |
| } |
| }); |
| xmlsecTestHelper.testDecryption(TestHelper.NS_XML_FRAGMENT, context); |
| } |
| |
| @Test |
| public void testPartialPayloadAsymmetricKeyDecryptionCustomNS() throws Exception { |
| final KeyStoreParameters tsParameters = new KeyStoreParameters(); |
| tsParameters.setPassword("password"); |
| tsParameters.setResource("sender.ts"); |
| |
| final KeyStoreParameters ksParameters = new KeyStoreParameters(); |
| ksParameters.setPassword("password"); |
| ksParameters.setResource("recipient.ks"); |
| |
| |
| final Map<String, String> namespaces = new HashMap<>(); |
| namespaces.put("cust", "http://cheese.xmlsecurity.camel.apache.org/"); |
| |
| |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal().secureXML("//cust:cheesesites/italy", namespaces, true, "recipient", testCypherAlgorithm, XMLCipher.RSA_v1dot5, tsParameters).to("mock:encrypted") |
| .unmarshal().secureXML("//cust:cheesesites/italy", namespaces, true, "recipient", testCypherAlgorithm, XMLCipher.RSA_v1dot5, ksParameters).to("mock:decrypted"); |
| } |
| }); |
| xmlsecTestHelper.testDecryption(TestHelper.NS_XML_FRAGMENT, context); |
| } |
| |
| @Test |
| public void testAsymmetricEncryptionAlgorithmFullPayload() throws Exception { |
| |
| final KeyStoreParameters tsParameters = new KeyStoreParameters(); |
| tsParameters.setPassword("password"); |
| tsParameters.setResource("sender.ts"); |
| |
| final KeyStoreParameters ksParameters = new KeyStoreParameters(); |
| ksParameters.setPassword("password"); |
| ksParameters.setResource("recipient.ks"); |
| |
| // RSA v1.5 is not allowed unless explicitly configured |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal().secureXML("", true, "recipient", testCypherAlgorithm, XMLCipher.RSA_v1dot5, tsParameters).to("mock:encrypted") |
| .unmarshal().secureXML("", true, "recipient", testCypherAlgorithm, XMLCipher.RSA_OAEP, ksParameters).to("mock:decrypted"); |
| } |
| }); |
| |
| MockEndpoint resultEndpoint = context.getEndpoint("mock:decrypted", MockEndpoint.class); |
| resultEndpoint.setExpectedMessageCount(0); |
| // verify that the message was encrypted before checking that it is decrypted |
| xmlsecTestHelper.testEncryption(TestHelper.XML_FRAGMENT, context); |
| |
| resultEndpoint.assertIsSatisfied(100); |
| } |
| |
| @Test |
| public void testAsymmetricEncryptionAlgorithmPartialPayload() throws Exception { |
| |
| final KeyStoreParameters tsParameters = new KeyStoreParameters(); |
| tsParameters.setPassword("password"); |
| tsParameters.setResource("sender.ts"); |
| |
| final KeyStoreParameters ksParameters = new KeyStoreParameters(); |
| ksParameters.setPassword("password"); |
| ksParameters.setResource("recipient.ks"); |
| |
| // RSA v1.5 is not allowed unless explicitly configured |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal().secureXML("//cheesesites/italy", true, "recipient", testCypherAlgorithm, XMLCipher.RSA_v1dot5, tsParameters).to("mock:encrypted") |
| .unmarshal().secureXML("//cheesesites/italy", true, "recipient", testCypherAlgorithm, XMLCipher.RSA_OAEP, ksParameters).to("mock:decrypted"); |
| } |
| }); |
| |
| MockEndpoint resultEndpoint = context.getEndpoint("mock:decrypted", MockEndpoint.class); |
| resultEndpoint.setExpectedMessageCount(0); |
| // verify that the message was encrypted before checking that it is decrypted |
| xmlsecTestHelper.testEncryption(TestHelper.XML_FRAGMENT, context); |
| |
| resultEndpoint.assertIsSatisfied(100); |
| } |
| |
| @Test |
| public void testAsymmetricEncryptionAlgorithmPartialPayloadElement() throws Exception { |
| |
| final KeyStoreParameters tsParameters = new KeyStoreParameters(); |
| tsParameters.setPassword("password"); |
| tsParameters.setResource("sender.ts"); |
| |
| final KeyStoreParameters ksParameters = new KeyStoreParameters(); |
| ksParameters.setPassword("password"); |
| ksParameters.setResource("recipient.ks"); |
| |
| // RSA v1.5 is not allowed unless explicitly configured |
| context.addRoutes(new RouteBuilder() { |
| public void configure() { |
| from("direct:start") |
| .marshal().secureXML("//cheesesites/france/cheese", false, "recipient", testCypherAlgorithm, XMLCipher.RSA_v1dot5, tsParameters).to("mock:encrypted") |
| .unmarshal().secureXML("//cheesesites/france", false, "recipient", testCypherAlgorithm, XMLCipher.RSA_OAEP, ksParameters).to("mock:decrypted"); |
| } |
| }); |
| |
| MockEndpoint resultEndpoint = context.getEndpoint("mock:decrypted", MockEndpoint.class); |
| resultEndpoint.setExpectedMessageCount(0); |
| // verify that the message was encrypted before checking that it is decrypted |
| xmlsecTestHelper.testEncryption(TestHelper.XML_FRAGMENT, context); |
| |
| resultEndpoint.assertIsSatisfied(100); |
| } |
| } |