/** * 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.xmlsecurity; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.security.Key; import java.security.KeyException; 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.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeMap; import javax.xml.crypto.AlgorithmMethod; import javax.xml.crypto.KeySelector; import javax.xml.crypto.KeySelectorException; import javax.xml.crypto.KeySelectorResult; import javax.xml.crypto.URIDereferencer; import javax.xml.crypto.XMLCryptoContext; import javax.xml.crypto.XMLStructure; import javax.xml.crypto.dsig.CanonicalizationMethod; import javax.xml.crypto.dsig.SignatureMethod; import javax.xml.crypto.dsig.XMLSignature; import javax.xml.crypto.dsig.keyinfo.KeyInfo; import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory; import javax.xml.crypto.dsig.keyinfo.KeyValue; import javax.xml.crypto.dsig.spec.XPathFilterParameterSpec; import javax.xml.crypto.dsig.spec.XPathType; import javax.xml.namespace.NamespaceContext; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.SAXException; 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.component.xmlsecurity.api.KeyAccessor; import org.apache.camel.component.xmlsecurity.api.ValidationFailedHandler; import org.apache.camel.component.xmlsecurity.api.XmlSignature2Message; import org.apache.camel.component.xmlsecurity.api.XmlSignatureChecker; import org.apache.camel.component.xmlsecurity.api.XmlSignatureConstants; import org.apache.camel.component.xmlsecurity.api.XmlSignatureException; import org.apache.camel.component.xmlsecurity.api.XmlSignatureFormatException; import org.apache.camel.component.xmlsecurity.api.XmlSignatureHelper; import org.apache.camel.component.xmlsecurity.api.XmlSignatureHelper.XPathAndFilter; import org.apache.camel.component.xmlsecurity.api.XmlSignatureInvalidContentHashException; import org.apache.camel.component.xmlsecurity.api.XmlSignatureInvalidException; import org.apache.camel.component.xmlsecurity.api.XmlSignatureInvalidKeyException; import org.apache.camel.component.xmlsecurity.api.XmlSignatureInvalidValueException; import org.apache.camel.component.xmlsecurity.api.XmlSignatureProperties; import org.apache.camel.component.xmlsecurity.processor.XmlSignatureConfiguration; import org.apache.camel.component.xmlsecurity.util.EnvelopingXmlSignatureChecker; import org.apache.camel.component.xmlsecurity.util.SameDocumentUriDereferencer; import org.apache.camel.component.xmlsecurity.util.TestKeystore; import org.apache.camel.component.xmlsecurity.util.TimestampProperty; import org.apache.camel.component.xmlsecurity.util.ValidationFailedHandlerIgnoreManifestFailures; import org.apache.camel.component.xmlsecurity.util.XmlSignature2Message2MessageWithTimestampProperty; import org.apache.camel.impl.DefaultCamelContext; import org.apache.camel.impl.JndiRegistry; import org.apache.camel.processor.validation.SchemaValidationException; import org.apache.camel.test.junit4.CamelTestSupport; import org.apache.camel.test.junit4.TestSupport; import org.junit.Before; import org.junit.Test; public class XmlSignatureTest extends CamelTestSupport { protected static String payload; private static boolean includeNewLine = true; private KeyPair keyPair; static { if (TestSupport.getJavaMajorVersion() >= 9) { includeNewLine = false; } payload = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + (includeNewLine ? "\n" : "") + "<root xmlns=\"http://test/test\"><test>Test Message</test></root>"; } @Override protected JndiRegistry createRegistry() throws Exception { JndiRegistry registry = super.createRegistry(); registry.bind("accessor", getKeyAccessor(keyPair.getPrivate())); registry.bind("canonicalizationMethod1", getCanonicalizationMethod()); registry.bind("selector", KeySelector.singletonKeySelector(keyPair.getPublic())); registry.bind("selectorKeyValue", getKeyValueKeySelector()); registry.bind("transformsXPath2", getTransformsXPath2()); registry.bind("transformsXsltXPath", getTransformsXsltXpath()); registry.bind("uriDereferencer", getSameDocumentUriDereferencer()); registry.bind("baseUri", getBaseUri()); registry.bind("cryptoContextProperties", getCrytoContextProperties()); registry.bind("keyAccessorDefault", getDefaultKeyAccessor()); registry.bind("keySelectorDefault", getDefaultKeySelector()); registry.bind("envelopingSignatureChecker", getEnvelopingXmlSignatureChecker()); registry.bind("xmlSignature2MessageWithTimestampProperty", getXmlSignature2MessageWithTimestampdProperty()); registry.bind("validationFailedHandlerIgnoreManifestFailures", getValidationFailedHandlerIgnoreManifestFailures()); registry.bind("signatureProperties", getSignatureProperties()); registry.bind("nodesearchxpath", getNodeSerachXPath()); Map<String, String> namespaceMap = Collections.singletonMap("ns", "http://test"); List<XPathFilterParameterSpec> xpaths = Collections .singletonList(XmlSignatureHelper.getXpathFilter("/ns:root/a/@ID", namespaceMap)); registry.bind("xpathsToIdAttributes", xpaths); registry.bind("parentXpathBean", getParentXPathBean()); return registry; } @Override protected RouteBuilder[] createRouteBuilders() throws Exception { return new RouteBuilder[] {new RouteBuilder() { public void configure() throws Exception { // START SNIPPET: enveloping XML signature onException(XmlSignatureException.class).handled(true).to("mock:exception"); from("direct:enveloping").to(getSignerEndpointURIEnveloping()).to("mock:signed").to(getVerifierEndpointURIEnveloping()) .to("mock:result"); // END SNIPPET: enveloping XML signature } }, new RouteBuilder() { public void configure() throws Exception { // START SNIPPET: enveloping XML signature with plain text // message body onException(XmlSignatureException.class).handled(true).to("mock:exception"); from("direct:plaintext").to("xmlsecurity:sign:plaintext?keyAccessor=#accessor&plainText=true&plainTextEncoding=UTF-8") .to("xmlsecurity:verify:plaintext?keySelector=#selector").to("mock:result"); // END SNIPPET: enveloping XML signature with plain text message // body } }, new RouteBuilder() { public void configure() throws Exception { // START SNIPPET: enveloped XML signature onException(XmlSignatureException.class).handled(true).to("mock:exception"); from("direct:enveloped").to(getSignerEndpointURIEnveloped()).to("mock:signed").to(getVerifierEndpointURIEnveloped()) .to("mock:result"); // END SNIPPET: enveloped XML signature } }, new RouteBuilder() { public void configure() throws Exception { // START SNIPPET: canonicalization // we can set the configuration properties explicitly on the // endpoint instances. context.getEndpoint("xmlsecurity:sign:canonicalization?canonicalizationMethod=#canonicalizationMethod1", XmlSignerEndpoint.class).setKeyAccessor(getKeyAccessor(keyPair.getPrivate())); context.getEndpoint("xmlsecurity:sign:canonicalization?canonicalizationMethod=#canonicalizationMethod1", XmlSignerEndpoint.class).setSignatureAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"); context.getEndpoint("xmlsecurity:verify:canonicalization", XmlVerifierEndpoint.class).setKeySelector( KeySelector.singletonKeySelector(keyPair.getPublic())); from("direct:canonicalization").to("xmlsecurity:sign:canonicalization?canonicalizationMethod=#canonicalizationMethod1", "xmlsecurity:verify:canonicalization", "mock:result"); // END SNIPPET: canonicalization } }, new RouteBuilder() { public void configure() throws Exception { // START SNIPPET: signature and digest algorithm from("direct:signaturedigestalgorithm") .to("xmlsecurity:sign:signaturedigestalgorithm?keyAccessor=#accessor" + "&signatureAlgorithm=http://www.w3.org/2001/04/xmldsig-more#rsa-sha512&digestAlgorithm=http://www.w3.org/2001/04/xmlenc#sha512", "xmlsecurity:verify:signaturedigestalgorithm?keySelector=#selector").to("mock:result"); // END SNIPPET: signature and digest algorithm } }, new RouteBuilder() { public void configure() throws Exception { // START SNIPPET: transforms XPath2 from("direct:transformsXPath2").to( "xmlsecurity:sign:transformsXPath2?keyAccessor=#accessor&transformMethods=#transformsXPath2", "xmlsecurity:verify:transformsXPath2?keySelector=#selector").to("mock:result"); // END SNIPPET: transform XPath } }, new RouteBuilder() { public void configure() throws Exception { // START SNIPPET: transforms XSLT,XPath onException(XmlSignatureException.class).handled(false).to("mock:exception"); from("direct:transformsXsltXPath").to( "xmlsecurity:sign:transformsXsltXPath?keyAccessor=#accessor&transformMethods=#transformsXsltXPath", "xmlsecurity:verify:transformsXsltXPath?keySelector=#selector").to("mock:result"); // END SNIPPET: transforms XSLT,XPath } }, new RouteBuilder() { public void configure() throws Exception { // START SNIPPET: transforms XSLT,XPath - secure Validation // disabled from("direct:transformsXsltXPathSecureValDisabled") .to("xmlsecurity:sign:transformsXsltXPathSecureValDisabled?keyAccessor=#accessor&transformMethods=#transformsXsltXPath", "xmlsecurity:verify:transformsXsltXPathSecureValDisabled?keySelector=#selector&secureValidation=false") .to("mock:result"); // END SNIPPET: transforms XSLT,XPath - secure Validation // disabled } }, new RouteBuilder() { public void configure() throws Exception { // START SNIPPET: cryptocontextprops onException(XmlSignatureException.class).handled(false).to("mock:exception"); from("direct:cryptocontextprops") .to("xmlsecurity:verify:cryptocontextprops?keySelector=#selectorKeyValue&cryptoContextProperties=#cryptoContextProperties") .to("mock:result"); // END SNIPPET: cryptocontextprops } }, new RouteBuilder() { public void configure() throws Exception { // START SNIPPET: URI dereferencer from("direct:uridereferencer") .to("xmlsecurity:sign:uriderferencer?keyAccessor=#accessor&uriDereferencer=#uriDereferencer") .to("xmlsecurity:verify:uridereferencer?keySelector=#selector&uriDereferencer=#uriDereferencer") .to("mock:result"); // END SNIPPET: URI dereferencer } }, new RouteBuilder() { public void configure() throws Exception { // START SNIPPET: keyAccessorKeySelectorDefault from("direct:keyAccessorKeySelectorDefault") .to("xmlsecurity:sign:keyAccessorKeySelectorDefault?keyAccessor=#keyAccessorDefault&addKeyInfoReference=true") .to("xmlsecurity:verify:keyAccessorKeySelectorDefault?keySelector=#keySelectorDefault").to("mock:result"); // END SNIPPET: keyAccessorKeySelectorDefault } }, new RouteBuilder() { public void configure() throws Exception { // START SNIPPET: xmlSignatureChecker onException(XmlSignatureInvalidException.class).handled(false).to("mock:exception"); from("direct:xmlSignatureChecker") .to("xmlsecurity:verify:xmlSignatureChecker?keySelector=#selectorKeyValue&xmlSignatureChecker=#envelopingSignatureChecker") .to("mock:result"); // END SNIPPET: xmlSignatureChecker } }, new RouteBuilder() { public void configure() throws Exception { // // START SNIPPET: properties from("direct:props") .to("xmlsecurity:sign:properties?keyAccessor=#accessor&properties=#signatureProperties") .to("xmlsecurity:verify:properties?keySelector=#selector&xmlSignature2Message=#xmlSignature2MessageWithTimestampProperty") .to("mock:result"); // END SNIPPET: properties } }, new RouteBuilder() { public void configure() throws Exception { // START SNIPPET: verify output node search element name onException(XmlSignatureException.class).handled(true).to("mock:exception"); from("direct:outputnodesearchelementname").to( "xmlsecurity:verify:outputnodesearchelementname?keySelector=#selectorKeyValue" + "&outputNodeSearchType=ElementName&outputNodeSearch={http://test/test}root&removeSignatureElements=true") .to("mock:result"); // END SNIPPET: verify output node search element name } }, new RouteBuilder() { public void configure() throws Exception { // START SNIPPET: verify output node search xpath onException(XmlSignatureException.class).handled(true).to("mock:exception"); from("direct:outputnodesearchxpath") .to("xmlsecurity:verify:outputnodesearchxpath?keySelector=#selectorKeyValue&outputNodeSearchType=XPath&outputNodeSearch=#nodesearchxpath&removeSignatureElements=true") .to("mock:result"); // END SNIPPET: verify output node search xpath } }, new RouteBuilder() { public void configure() throws Exception { // START SNIPPET: validationFailedHandler from("direct:validationFailedHandler") .to("xmlsecurity:verify:validationFailedHandler?keySelector=#selectorKeyValue&validationFailedHandler=validationFailedHandlerIgnoreManifestFailures") .to("mock:result"); // END SNIPPET: validationFailedHandler } }, new RouteBuilder() { public void configure() throws Exception { // START SNIPPET: further parameters from("direct:furtherparams") .to("xmlsecurity:sign:furtherparams?keyAccessor=#accessor&prefixForXmlSignatureNamespace=digsig&disallowDoctypeDecl=false") .to("xmlsecurity:verify:bfurtherparams?keySelector=#selector&disallowDoctypeDecl=false").to("mock:result"); // END SNIPPET: further parameters } }, new RouteBuilder() { public void configure() throws Exception { // START SNIPPET: signer invalid keyexception onException(XmlSignatureInvalidKeyException.class).handled(true).to("mock:exception"); from("direct:signexceptioninvalidkey").to( "xmlsecurity:sign:signexceptioninvalidkey?signatureAlgorithm=http://www.w3.org/2001/04/xmldsig-more#rsa-sha512") .to("mock:result"); // END SNIPPET: signer invalid keyexception } }, new RouteBuilder() { public void configure() throws Exception { // START SNIPPET: signer exceptions onException(XmlSignatureException.class).handled(true).to("mock:exception"); from("direct:signexceptions") .to("xmlsecurity:sign:signexceptions?keyAccessor=#accessor&signatureAlgorithm=http://www.w3.org/2001/04/xmldsig-more#rsa-sha512") .to("mock:result"); // END SNIPPET: signer exceptions } }, new RouteBuilder() { public void configure() throws Exception { onException(XmlSignatureException.class).handled(true).to("mock:exception"); from("direct:noSuchAlgorithmException") .to("xmlsecurity:sign:noSuchAlgorithmException?keyAccessor=#accessor&signatureAlgorithm=wrongalgorithm&digestAlgorithm=http://www.w3.org/2001/04/xmlenc#sha512") .to("mock:result"); } }, new RouteBuilder() { public void configure() throws Exception { onException(XmlSignatureException.class).handled(false).to("mock:exception"); from("direct:verifyexceptions").to("xmlsecurity:verify:verifyexceptions?keySelector=#selector").to("mock:result"); } }, new RouteBuilder() { public void configure() throws Exception { onException(XmlSignatureException.class).handled(false).to("mock:exception"); from("direct:verifyInvalidKeyException").to("xmlsecurity:verify:verifyInvalidKeyException?keySelector=#selector").to( "mock:result"); } }, new RouteBuilder() { public void configure() throws Exception { onException(XmlSignatureException.class).handled(false).to("mock:exception"); from("direct:invalidhash").to( "xmlsecurity:verify:invalidhash?keySelector=#selectorKeyValue&baseUri=#baseUri&secureValidation=false").to( "mock:result"); } }, createDetachedRoute(), createRouteForEnvelopedWithParentXpath() }; } RouteBuilder createDetachedRoute() { return new RouteBuilder() { public void configure() throws Exception { // START SNIPPET: detached XML signature onException(Exception.class).handled(false).to("mock:exception"); from("direct:detached") .to("xmlsecurity:sign:detached?keyAccessor=#keyAccessorDefault&xpathsToIdAttributes=#xpathsToIdAttributes&"// + "schemaResourceUri=org/apache/camel/component/xmlsecurity/Test.xsd&signatureId=&clearHeaders=false") .to("mock:result") .to("xmlsecurity:verify:detached?keySelector=#keySelectorDefault&schemaResourceUri=org/apache/camel/component/xmlsecurity/Test.xsd") .to("mock:verified"); // END SNIPPET: detached XML signature } }; } private RouteBuilder createRouteForEnvelopedWithParentXpath() { return new RouteBuilder() { public void configure() throws Exception { // START SNIPPET: enveloped XML signature with parent XPath onException(XmlSignatureException.class).handled(false).to("mock:exception"); from("direct:envelopedParentXpath").to("xmlsecurity:sign:enveloped?keyAccessor=#accessor&parentXpath=#parentXpathBean") .to("mock:signed").to(getVerifierEndpointURIEnveloped()).to("mock:result"); // END SNIPPET: enveloped XML signature with parent XPath } }; } @Test public void testEnvelopingSignature() throws Exception { setupMock(); sendBody("direct:enveloping", payload); assertMockEndpointsSatisfied(); } @Test public void testEnvelopedSignatureWithTransformHeader() throws Exception { setupMock(payload); sendBody("direct:enveloped", payload, Collections.<String, Object>singletonMap(XmlSignatureConstants.HEADER_TRANSFORM_METHODS, "http://www.w3.org/2000/09/xmldsig#enveloped-signature,http://www.w3.org/TR/2001/REC-xml-c14n-20010315")); assertMockEndpointsSatisfied(); } @Test public void testEnvelopingSignatureWithPlainText() throws Exception { String text = "plain test text"; setupMock(text); sendBody("direct:plaintext", text); assertMockEndpointsSatisfied(); } @Test public void testEnvelopingSignatureWithPlainTextSetByHeaders() throws Exception { String text = "plain test text"; setupMock(text); Map<String, Object> headers = new TreeMap<String, Object>(); headers.put(XmlSignatureConstants.HEADER_MESSAGE_IS_PLAIN_TEXT, Boolean.TRUE); headers.put(XmlSignatureConstants.HEADER_PLAIN_TEXT_ENCODING, "UTF-8"); sendBody("direct:enveloping", text, headers); assertMockEndpointsSatisfied(); } @Test public void testExceptionSignatureForPlainTextWithWrongEncoding() throws Exception { String text = "plain test text"; MockEndpoint mock = setupExceptionMock(); Map<String, Object> headers = new TreeMap<String, Object>(); headers.put(XmlSignatureConstants.HEADER_MESSAGE_IS_PLAIN_TEXT, Boolean.TRUE); headers.put(XmlSignatureConstants.HEADER_PLAIN_TEXT_ENCODING, "wrongEncoding"); sendBody("direct:enveloping", text, headers); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureException.class, UnsupportedEncodingException.class); } @Test public void testEnvelopedSignature() throws Exception { setupMock(payload); sendBody("direct:enveloped", payload); assertMockEndpointsSatisfied(); } @Test public void testExceptionEnvelopedSignatureWithWrongParent() throws Exception { // payload root element renamed to a -> parent name in route definition // does not fit String payload = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><a xmlns=\"http://test/test\"><test>Test Message</test></a>"; MockEndpoint mock = setupExceptionMock(); sendBody("direct:enveloped", payload); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureFormatException.class, null); } @Test public void testExceptionEnvelopedSignatureWithPlainTextPayload() throws Exception { // payload root element renamed to a -> parent name in route definition // does not fit String payload = "plain text Message"; Map<String, Object> headers = new HashMap<String, Object>(1); headers.put(XmlSignatureConstants.HEADER_MESSAGE_IS_PLAIN_TEXT, Boolean.TRUE); MockEndpoint mock = setupExceptionMock(); sendBody("direct:enveloped", payload, headers); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureFormatException.class, null); } /** * The parameter can also be configured via * {@link XmlSignatureConfiguration#setOmitXmlDeclaration(Boolean)} */ @Test public void testOmitXmlDeclarationViaHeader() throws Exception { String payloadOut = "<root xmlns=\"http://test/test\"><test>Test Message</test></root>"; setupMock(payloadOut); Map<String, Object> headers = new TreeMap<String, Object>(); headers.put(XmlSignatureConstants.HEADER_OMIT_XML_DECLARATION, Boolean.TRUE); InputStream payload = XmlSignatureTest.class .getResourceAsStream("/org/apache/camel/component/xmlsecurity/ExampleEnvelopedXmlSig.xml"); assertNotNull("Cannot load payload", payload); sendBody("direct:outputnodesearchelementname", payload, headers); assertMockEndpointsSatisfied(); } @Test public void testkeyAccessorKeySelectorDefault() throws Exception { setupMock(); sendBody("direct:keyAccessorKeySelectorDefault", payload); assertMockEndpointsSatisfied(); } @Test public void testSetCanonicalizationMethodInRouteDefinition() throws Exception { setupMock(); sendBody("direct:canonicalization", payload); assertMockEndpointsSatisfied(); } @Test public void testSetDigestAlgorithmInRouteDefinition() throws Exception { setupMock(); sendBody("direct:signaturedigestalgorithm", payload); assertMockEndpointsSatisfied(); } @Test public void testSetTransformMethodXpath2InRouteDefinition() throws Exception { // example from http://www.w3.org/TR/2002/REC-xmldsig-filter2-20021108/ String payload = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + (includeNewLine ? "\n" : "") + "<Document xmlns=\"http://test/test\"> " + "<ToBeSigned> " + " <!-- comment --> " + " <Data>1</Data> " + " <NotToBeSigned> " + " <ReallyToBeSigned> " + " <!-- comment --> " + " <Data>2</Data> " + " </ReallyToBeSigned> " + " </NotToBeSigned> " + " </ToBeSigned> " + " <ToBeSigned> " + " <Data>3</Data> " + " <NotToBeSigned> " + " <Data>4</Data> " + " </NotToBeSigned> " + " </ToBeSigned> " + "</Document>"; setupMock(payload); sendBody("direct:transformsXPath2", payload); assertMockEndpointsSatisfied(); } // Secure Validation is enabled and so this should fail @Test public void testSetTransformMethodXsltXpathInRouteDefinition() throws Exception { // byte[] encoded = Base64.encode("Test Message".getBytes("UTF-8")); // String contentBase64 = new String(encoded, "UTF-8"); // String payload = // "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root xmlns=\"http://test/test\"><test></test></root>"; MockEndpoint mock = setupExceptionMock(); sendBody("direct:transformsXsltXPath", payload); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureException.class, null); } @Test public void testSetTransformMethodXsltXpathInRouteDefinitionSecValDisabled() throws Exception { setupMock(); sendBody("direct:transformsXsltXPathSecureValDisabled", payload); assertMockEndpointsSatisfied(); } @Test public void testProperties() throws Exception { setupMock(); sendBody("direct:props", payload); assertMockEndpointsSatisfied(); } @Test public void testVerifyOutputNodeSearchElementName() throws Exception { setupMock(); InputStream payload = XmlSignatureTest.class .getResourceAsStream("/org/apache/camel/component/xmlsecurity/ExampleEnvelopedXmlSig.xml"); assertNotNull("Cannot load payload", payload); sendBody("direct:outputnodesearchelementname", payload); assertMockEndpointsSatisfied(); } @Test public void testVerifyExceptionOutputNodeSearchElementNameInvalidFormat1() throws Exception { XmlVerifierEndpoint endpoint = context.getEndpoint("xmlsecurity:verify:outputnodesearchelementname?keySelector=#selectorKeyValue" + "&outputNodeSearchType=ElementName&outputNodeSearch={http://test/test}root&removeSignatureElements=true", XmlVerifierEndpoint.class); endpoint.setOutputNodeSearch("{wrongformat"); // closing '}' missing MockEndpoint mock = setupExceptionMock(); InputStream payload = XmlSignatureTest.class .getResourceAsStream("/org/apache/camel/component/xmlsecurity/ExampleEnvelopedXmlSig.xml"); assertNotNull("Cannot load payload", payload); sendBody("direct:outputnodesearchelementname", payload); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureException.class, null); } @Test public void testVerifyExceptionOutputNodeSearchElementNameInvalidFormat2() throws Exception { context.getEndpoint( "xmlsecurity:verify:outputnodesearchelementname?keySelector=#selectorKeyValue" + "&outputNodeSearchType=ElementName&outputNodeSearch={http://test/test}root&removeSignatureElements=true", XmlVerifierEndpoint.class).setOutputNodeSearch("{wrongformat}"); // local name missing MockEndpoint mock = setupExceptionMock(); InputStream payload = XmlSignatureTest.class .getResourceAsStream("/org/apache/camel/component/xmlsecurity/ExampleEnvelopedXmlSig.xml"); assertNotNull("Cannot load payload", payload); sendBody("direct:outputnodesearchelementname", payload); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureException.class, null); } @Test public void testExceptionVerifyOutputNodeSearchWrongElementName() throws Exception { MockEndpoint mock = setupExceptionMock(); InputStream payload = XmlSignatureTest.class .getResourceAsStream("/org/apache/camel/component/xmlsecurity/ExampleEnvelopingDigSig.xml"); assertNotNull("Cannot load payload", payload); sendBody("direct:outputnodesearchelementname", payload); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureException.class, null); } @Test public void testExceptionVerifyOutputNodeSearchElementNameMoreThanOneOutputElement() throws Exception { MockEndpoint mock = setupExceptionMock(); InputStream payload = XmlSignatureTest.class .getResourceAsStream("/org/apache/camel/component/xmlsecurity/ExampleEnvelopingDigSigWithSeveralElementsWithNameRoot.xml"); assertNotNull("Cannot load payload", payload); sendBody("direct:outputnodesearchelementname", payload); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureException.class, null); } @Test public void testVerifyOutputNodeSearchXPath() throws Exception { setupMock(); InputStream payload = XmlSignatureTest.class .getResourceAsStream("/org/apache/camel/component/xmlsecurity/ExampleEnvelopedXmlSig.xml"); assertNotNull("Cannot load payload", payload); sendBody("direct:outputnodesearchxpath", payload); assertMockEndpointsSatisfied(); } @Test public void testExceptionVerifyOutputNodeSearchXPathWithNoResultNode() throws Exception { MockEndpoint mock = setupExceptionMock(); InputStream payload = XmlSignatureTest.class .getResourceAsStream("/org/apache/camel/component/xmlsecurity/ExampleEnvelopingDigSig.xml"); assertNotNull("Cannot load payload", payload); sendBody("direct:outputnodesearchxpath", payload); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureException.class, null); } @Test public void testExceptionVerifyOutputNodeSearchXPathMoreThanOneOutputElement() throws Exception { MockEndpoint mock = setupExceptionMock(); InputStream payload = XmlSignatureTest.class .getResourceAsStream("/org/apache/camel/component/xmlsecurity/ExampleEnvelopingDigSigWithSeveralElementsWithNameRoot.xml"); assertNotNull("Cannot load payload", payload); sendBody("direct:outputnodesearchxpath", payload); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureException.class, null); } @Test public void testInvalidKeyException() throws Exception { MockEndpoint mock = setupExceptionMock(); // wrong key type setUpKeys("DSA", 512); context.getEndpoint( "xmlsecurity:sign:signexceptioninvalidkey?signatureAlgorithm=http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", XmlSignerEndpoint.class).setKeyAccessor(getKeyAccessor(keyPair.getPrivate())); sendBody("direct:signexceptioninvalidkey", payload); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureInvalidKeyException.class, null); } @Test public void testSignatureFormatException() throws Exception { MockEndpoint mock = setupExceptionMock(); sendBody("direct:signexceptions", "wrongFormatedPayload"); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureFormatException.class, null); } @Test public void testNoSuchAlgorithmException() throws Exception { MockEndpoint mock = setupExceptionMock(); sendBody("direct:noSuchAlgorithmException", payload); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureException.class, NoSuchAlgorithmException.class); } @Test public void testVerifyFormatExceptionNoXml() throws Exception { MockEndpoint mock = setupExceptionMock(); sendBody("direct:verifyexceptions", "wrongFormatedPayload"); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureFormatException.class, null); } @Test public void testVerifyFormatExceptionNoXmlWithoutSignatureElement() throws Exception { MockEndpoint mock = setupExceptionMock(); sendBody("direct:verifyexceptions", "<?xml version=\"1.0\" encoding=\"UTF-8\"?><NoSignature></NoSignature>"); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureFormatException.class, null); } @Test public void testVerifyInvalidContentHashException() throws Exception { MockEndpoint mock = setupExceptionMock(); InputStream payload = XmlSignatureTest.class.getResourceAsStream("/org/apache/camel/component/xmlsecurity/ExampleDetached.xml"); assertNotNull("Cannot load payload", payload); sendBody("direct:invalidhash", payload); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureInvalidContentHashException.class, null); } @Test public void testVerifyMantifestInvalidContentHashException() throws Exception { MockEndpoint mock = setupExceptionMock(); InputStream payload = XmlSignatureTest.class .getResourceAsStream("/org/apache/camel/component/xmlsecurity/ManifestTest_TamperedContent.xml"); assertNotNull("Cannot load payload", payload); sendBody("direct:invalidhash", payload); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureInvalidContentHashException.class, null); } @Test public void testVerifySetCryptoContextProperties() throws Exception { // although the content referenced by the manifest was tempered, this is // not detected by // the core validation because the manifest validation is switched off // by the crypto context properties setupMock("some text tampered"); InputStream payload = XmlSignatureTest.class .getResourceAsStream("/org/apache/camel/component/xmlsecurity/ManifestTest_TamperedContent.xml"); assertNotNull("Cannot load payload", payload); sendBody("direct:cryptocontextprops", payload); assertMockEndpointsSatisfied(); } @Test public void testVerifySignatureInvalidValueException() throws Exception { MockEndpoint mock = setupExceptionMock(); setUpKeys("DSA", 512); context.getEndpoint("xmlsecurity:verify:verifyexceptions?keySelector=#selector", XmlVerifierEndpoint.class).setKeySelector( KeySelector.singletonKeySelector(keyPair.getPublic())); // payload needs DSA key InputStream payload = XmlSignatureTest.class .getResourceAsStream("/org/apache/camel/component/xmlsecurity/ExampleEnvelopingDigSig.xml"); assertNotNull("Cannot load payload", payload); sendBody("direct:verifyexceptions", payload); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureInvalidValueException.class, null); } @Test public void testVerifyInvalidKeyException() throws Exception { MockEndpoint mock = setupExceptionMock(); InputStream payload = XmlSignatureTest.class .getResourceAsStream("/org/apache/camel/component/xmlsecurity/ExampleEnvelopingDigSig.xml"); assertNotNull("Cannot load payload", payload); sendBody("direct:verifyInvalidKeyException", payload); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureInvalidKeyException.class, null); } @Test public void testUriDereferencerAndBaseUri() throws Exception { setupMock(); sendBody("direct:uridereferencer", payload); assertMockEndpointsSatisfied(); } @Test public void testVerifyXmlSignatureChecker() throws Exception { MockEndpoint mock = setupExceptionMock(); InputStream payload = XmlSignatureTest.class .getResourceAsStream("/org/apache/camel/component/xmlsecurity/ExampleEnvelopedXmlSig.xml"); assertNotNull("Cannot load payload", payload); sendBody("direct:xmlSignatureChecker", payload); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureInvalidException.class, null); } @Test public void testVerifyValidationFailedHandler() throws Exception { setupMock("some text tampered"); InputStream payload = XmlSignatureTest.class .getResourceAsStream("/org/apache/camel/component/xmlsecurity/ManifestTest_TamperedContent.xml"); assertNotNull("Cannot load payload", payload); sendBody("direct:validationFailedHandler", payload); assertMockEndpointsSatisfied(); } @Test public void testFurtherParameters() throws Exception { setupMock(payload); String payloadWithDTDoctype = "<?xml version=\'1.0\'?>" + "<!DOCTYPE Signature SYSTEM " + "\"src/test/resources/org/apache/camel/component/xmlsecurity/xmldsig-core-schema.dtd\" [ <!ENTITY dsig " + "\"http://www.w3.org/2000/09/xmldsig#\"> ]>" + "<root xmlns=\"http://test/test\"><test>Test Message</test></root>"; sendBody("direct:furtherparams", payloadWithDTDoctype); assertMockEndpointsSatisfied(); } @Test public void testReferenceUriWithIdAttributeInTheEnvelopedCase() throws Exception { XmlSignerEndpoint endpoint = getDetachedSignerEndpoint(); endpoint.setParentLocalName("root"); endpoint.setParentNamespace("http://test"); endpoint.setXpathsToIdAttributes(null); String detachedPayload = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + (includeNewLine ? "\n" : "") + "<ns:root xmlns:ns=\"http://test\"><a ID=\"myID\"><b>bValue</b></a></ns:root>"; MockEndpoint mock = getMockEndpoint("mock:result"); mock.expectedMessageCount(1); sendBody("direct:detached", detachedPayload, Collections.singletonMap(XmlSignatureConstants.HEADER_CONTENT_REFERENCE_URI, (Object) "#myID")); assertMockEndpointsSatisfied(); String expectedPartContent = "<ds:Reference URI=\"#myID\">"; checkBodyContains(mock, expectedPartContent); } @Test public void testDetachedSignature() throws Exception { testDetachedSignatureInternal(); } @Test public void testDetachedSignatureWitTransformHeader() throws Exception { testDetachedSignatureInternal(Collections.singletonMap(XmlSignatureConstants.HEADER_TRANSFORM_METHODS, (Object)"http://www.w3.org/2000/09/xmldsig#enveloped-signature,http://www.w3.org/TR/2001/REC-xml-c14n-20010315")); } @Test public void testSignatureIdAtributeNull() throws Exception { // the signature Id parameter must be empty, this is set in the URI // already Element sigEle = testDetachedSignatureInternal(); Attr attr = sigEle.getAttributeNode("Id"); assertNull("Signature element contains Id attribute", attr); } @Test public void testSignatureIdAttribute() throws Exception { String signatureId = "sigId"; XmlSignerEndpoint endpoint = getDetachedSignerEndpoint(); endpoint.setSignatureId(signatureId); Element sigEle = testDetachedSignatureInternal(); String value = sigEle.getAttribute("Id"); assertNotNull("Signature Id is null", value); assertEquals(signatureId, value); } @Test public void testSignatureIdAttributeGenerated() throws Exception { String signatureId = null; XmlSignerEndpoint endpoint = getDetachedSignerEndpoint(); endpoint.setSignatureId(signatureId); Element sigEle = testDetachedSignatureInternal(); String value = sigEle.getAttribute("Id"); assertNotNull("Signature Id is null", value); assertTrue("Signature Id value does not start with '_'", value.startsWith("_")); } private Element testDetachedSignatureInternal() throws InterruptedException, XPathExpressionException, SAXException, IOException, ParserConfigurationException { return testDetachedSignatureInternal(Collections.<String, Object>emptyMap()); } private Element testDetachedSignatureInternal(Map<String, Object> headers) throws InterruptedException, XPathExpressionException, SAXException, IOException, ParserConfigurationException { String detachedPayload = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + (includeNewLine ? "\n" : "") + "<ns:root xmlns:ns=\"http://test\"><a ID=\"myID\"><b>bValue</b></a></ns:root>"; MockEndpoint mock = getMockEndpoint("mock:result"); mock.expectedMessageCount(1); MockEndpoint mockVerified = getMockEndpoint("mock:verified"); mockVerified.expectedBodiesReceived(detachedPayload); sendBody("direct:detached", detachedPayload, headers); assertMockEndpointsSatisfied(); Map<String, String> namespaceMap = new TreeMap<String, String>(); namespaceMap.put("ns", "http://test"); namespaceMap.put("ds", XMLSignature.XMLNS); Object obj = checkXpath(mock, "ns:root/ds:Signature", namespaceMap); Element sigEle = (Element) obj; return sigEle; } @Test public void testDetachedSignatureComplexSchema() throws Exception { String xpath1exp = "/ns:root/test/ns1:B/C/@ID"; String xpath2exp = "/ns:root/test/@ID"; testDetached2Xpaths(xpath1exp, xpath2exp); } /** * Checks that the processor sorts the xpath expressions in such a way that * elements with deeper hierarchy level are signed first. * */ @Test public void testDetachedSignatureWrongXPathOrder() throws Exception { String xpath2exp = "/ns:root/test/ns1:B/C/@ID"; String xpath1exp = "/ns:root/test/@ID"; testDetached2Xpaths(xpath1exp, xpath2exp); } void testDetached2Xpaths(String xpath1exp, String xpath2exp) throws InterruptedException, XPathExpressionException, SAXException, IOException, ParserConfigurationException { String detachedPayload = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + (includeNewLine ? "\n" : "") + "<ns:root xmlns:ns=\"http://test\"><test ID=\"myID\"><b>bValue</b><ts:B xmlns:ts=\"http://testB\"><C ID=\"cID\"><D>dvalue</D></C></ts:B></test></ns:root>"; MockEndpoint mock = getMockEndpoint("mock:result"); mock.expectedMessageCount(1); MockEndpoint mockVerified = getMockEndpoint("mock:verified"); mockVerified.expectedBodiesReceived(detachedPayload); Map<String, Object> headers = new TreeMap<String, Object>(); headers.put(XmlSignatureConstants.HEADER_SCHEMA_RESOURCE_URI, "org/apache/camel/component/xmlsecurity/TestComplex.xsd"); Map<String, String> namespaceMap = new TreeMap<String, String>(); namespaceMap.put("ns", "http://test"); namespaceMap.put("ns1", "http://testB"); XPathFilterParameterSpec xpath1 = XmlSignatureHelper.getXpathFilter(xpath1exp, namespaceMap); XPathFilterParameterSpec xpath2 = XmlSignatureHelper.getXpathFilter(xpath2exp, namespaceMap); List<XPathFilterParameterSpec> xpaths = new ArrayList<XPathFilterParameterSpec>(); xpaths.add(xpath1); xpaths.add(xpath2); headers.put(XmlSignatureConstants.HEADER_XPATHS_TO_ID_ATTRIBUTES, xpaths); sendBody("direct:detached", detachedPayload, headers); assertMockEndpointsSatisfied(); Map<String, String> namespaceMap2 = new TreeMap<String, String>(); namespaceMap2.put("ns", "http://test"); namespaceMap2.put("ds", XMLSignature.XMLNS); namespaceMap2.put("nsB", "http://testB"); checkXpath(mock, "ns:root/test/nsB:B/ds:Signature", namespaceMap2); checkXpath(mock, "ns:root/ds:Signature", namespaceMap2); } @Test public void testExceptionEnvelopedAndDetached() throws Exception { String detachedPayload = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + // "<ns:root xmlns:ns=\"http://test\"><a ID=\"myID\"><b>bValue</b></a></ns:root>"; XmlSignerEndpoint endpoint = getDetachedSignerEndpoint(); String parentLocalName = "parent"; endpoint.setParentLocalName(parentLocalName); MockEndpoint mock = setupExceptionMock(); mock.expectedMessageCount(1); sendBody("direct:detached", detachedPayload); assertMockEndpointsSatisfied(); checkThrownException( mock, XmlSignatureException.class, "The configuration of the XML signer component is wrong. The parent local name " + parentLocalName + " for an enveloped signature and the XPATHs to ID attributes for a detached signature are specified. You must not specify both parameters.", null); } @Test public void testExceptionSchemaValidation() throws Exception { String detachedPayload = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + (includeNewLine ? "\n" : "") + "<ns:root xmlns:ns=\"http://test\"><a ID=\"myID\"><error>bValue</error></a></ns:root>"; MockEndpoint mock = setupExceptionMock(); mock.expectedMessageCount(1); sendBody("direct:detached", detachedPayload); assertMockEndpointsSatisfied(); checkThrownException(mock, SchemaValidationException.class, null); } @Test public void testEceptionDetachedNoXmlSchema() throws Exception { String detachedPayload = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + (includeNewLine ? "\n" : "") + "<ns:root xmlns:ns=\"http://test\"><a ID=\"myID\"><b>bValue</b></a></ns:root>"; XmlSignerEndpoint endpoint = getDetachedSignerEndpoint(); endpoint.setSchemaResourceUri(null); MockEndpoint mock = setupExceptionMock(); mock.expectedMessageCount(1); sendBody("direct:detached", detachedPayload); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureException.class, "The configruation of the XML Signature component is wrong: No XML schema specified in the detached case", null); } @Test public void testExceptionDetachedXpathInvalid() throws Exception { String wrongXPath = "n1:p/a"; // namespace prefix is not defined MockEndpoint mock = testXpath(wrongXPath); checkThrownException(mock, XmlSignatureException.class, "The configured xpath expression " + wrongXPath + " is invalid.", XPathExpressionException.class); } @Test public void testExceptionDetachedXPathNoIdAttribute() throws Exception { String value = "not id"; String detachedPayload = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + (includeNewLine ? "\n" : "") + "<ns:root xmlns:ns=\"http://test\"><a ID=\"myID\" stringAttr=\"" + value + "\"><b>bValue</b></a></ns:root>"; String xPath = "a/@stringAttr"; MockEndpoint mock = testXpath(xPath, detachedPayload); checkThrownException(mock, XmlSignatureException.class, "Wrong configured xpath expression for ID attributes: The evaluation of the xpath expression " + xPath + " resulted in an attribute which is not of type ID. The attribute value is " + value + ".", null); } @Test public void testExceptionDetachedXpathNoAttribute() throws Exception { String xPath = "a"; // Element a MockEndpoint mock = testXpath(xPath); checkThrownException(mock, XmlSignatureException.class, "Wrong configured xpath expression for ID attributes: The evaluation of the xpath expression " + xPath + " returned a node which was not of type Attribute.", null); } @Test public void testExceptionDetachedXPathNoResult() throws Exception { String xPath = "a/@stringAttr"; // for this xpath there is no result MockEndpoint mock = testXpath(xPath); checkThrownException( mock, XmlSignatureException.class, "No element to sign found in the detached case. No node found for the configured xpath expressions " + xPath + ". Either the configuration of the XML signature component is wrong or the incoming message has not the correct structure.", null); } private MockEndpoint testXpath(String xPath) throws InterruptedException { String detachedPayload = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + (includeNewLine ? "\n" : "") + "<ns:root xmlns:ns=\"http://test\"><a ID=\"myID\"><b>bValue</b></a></ns:root>"; return testXpath(xPath, detachedPayload); } private MockEndpoint testXpath(String xPath, String detachedPayload) throws InterruptedException { MockEndpoint mock = setupExceptionMock(); mock.expectedMessageCount(1); List<XPathFilterParameterSpec> list = Collections.singletonList(XmlSignatureHelper.getXpathFilter(xPath, null)); sendBody("direct:detached", detachedPayload, Collections.singletonMap(XmlSignatureConstants.HEADER_XPATHS_TO_ID_ATTRIBUTES, (Object) list)); assertMockEndpointsSatisfied(); return mock; } @Test public void testExceptionDetachedNoParent() throws Exception { String detachedPayload = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + (includeNewLine ? "\n" : "") + "<ns:root ID=\"rootId\" xmlns:ns=\"http://test\"><a ID=\"myID\"><b>bValue</b></a></ns:root>"; String xPath = "//@ID"; String localName = "root"; String namespaceURI = "http://test"; String referenceUri = "#rootId"; MockEndpoint mock = testXpath(xPath, detachedPayload); checkThrownException(mock, XmlSignatureException.class, "Either the configuration of the XML Signature component is wrong or the incoming document has an invalid structure: The element " + localName + "{" + namespaceURI + "} which is referenced by the reference URI " + referenceUri + " has no parent element. The element must have a parent element in the configured detached case.", null); } @Test public void testOutputXmlEncodingEnveloping() throws Exception { String inputEncoding = "UTF-8"; String signerEncoding = "UTF-16"; String outputEncoding = "ISO-8859-1"; // latin 1 String signerEndpointUri = getSignerEndpointURIEnveloping(); String verifierEndpointUri = getVerifierEndpointURIEnveloping(); String directStart = "direct:enveloping"; checkOutputEncoding(inputEncoding, signerEncoding, outputEncoding, signerEndpointUri, verifierEndpointUri, directStart); } String getVerifierEndpointURIEnveloping() { return "xmlsecurity:verify:enveloping?keySelector=#selector"; } String getSignerEndpointURIEnveloping() { return "xmlsecurity:sign:enveloping?keyAccessor=#accessor&schemaResourceUri="; } @Test public void testOutputXmlEncodingEnveloped() throws Exception { String inputEncoding = "UTF-8"; String signerEncoding = "UTF-16"; String outputEncoding = "ISO-8859-1"; // latin 1 String signerEndpointUri = getSignerEndpointURIEnveloped(); String verifierEndpointUri = getVerifierEndpointURIEnveloped(); String directStart = "direct:enveloped"; checkOutputEncoding(inputEncoding, signerEncoding, outputEncoding, signerEndpointUri, verifierEndpointUri, directStart); } String getVerifierEndpointURIEnveloped() { return "xmlsecurity:verify:enveloped?keySelector=#selector"; } String getSignerEndpointURIEnveloped() { return "xmlsecurity:sign:enveloped?keyAccessor=#accessor&parentLocalName=root&parentNamespace=http://test/test"; } private byte[] getPayloadForEncoding(String encoding) { String s = "<?xml version=\"1.0\" encoding=\"" + encoding + "\"?>" + (includeNewLine ? "\n" : "") + "<root xmlns=\"http://test/test\"><test>Test Message</test></root>"; return s.getBytes(Charset.forName(encoding)); } @Test public void testExceptionParentLocalNameAndXPathSet() throws Exception { XmlSignerEndpoint endpoint = getSignatureEncpointForSignException(); MockEndpoint mock = setupExceptionMock(); try { endpoint.setParentXpath(getNodeSerachXPath()); endpoint.setParentLocalName("root"); sendBody("direct:signexceptions", payload); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureException.class, "The configuration of the XML signer component is wrong. " + // "The parent local name root and the parent XPath //pre:root are specified. You must not specify both parameters.", null); } finally { endpoint.setParentXpath(null); endpoint.setParentLocalName(null); } } @Test public void testExceptionXpathsToIdAttributesNameAndXPathSet() throws Exception { XmlSignerEndpoint endpoint = getSignatureEncpointForSignException(); MockEndpoint mock = setupExceptionMock(); try { endpoint.setParentXpath(getNodeSerachXPath()); List<XPathFilterParameterSpec> xpaths = Collections.singletonList(XmlSignatureHelper.getXpathFilter("/ns:root/a/@ID", null)); endpoint.setXpathsToIdAttributes(xpaths); sendBody("direct:signexceptions", payload); assertMockEndpointsSatisfied(); checkThrownException( mock, XmlSignatureException.class, "The configuration of the XML signer component is wrong. " + // "The parent XPath //pre:root for an enveloped signature and the XPATHs to ID attributes for a detached signature are specified. You must not specify both parameters.", null); } finally { endpoint.setParentXpath(null); endpoint.setXpathsToIdAttributes(null); } } @Test public void testExceptionInvalidParentXpath() throws Exception { XmlSignerEndpoint endpoint = getSignatureEncpointForSignException(); MockEndpoint mock = setupExceptionMock(); try { endpoint.setParentXpath(XmlSignatureHelper.getXpathFilter("//pre:root", null)); // invalid xpath: namespace-prefix mapping is missing sendBody("direct:signexceptions", payload); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureException.class, "The parent XPath //pre:root is wrongly configured: The XPath //pre:root is invalid.", null); } finally { endpoint.setParentXpath(null); } } @Test public void testExceptionParentXpathWithNoResult() throws Exception { XmlSignerEndpoint endpoint = getSignatureEncpointForSignException(); MockEndpoint mock = setupExceptionMock(); try { endpoint.setParentXpath(XmlSignatureHelper.getXpathFilter("//root", null)); // xpath with no result sendBody("direct:signexceptions", payload); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureException.class, "The parent XPath //root returned no result. Check the configuration of the XML signer component.", null); } finally { endpoint.setParentXpath(null); } } XmlSignerEndpoint getSignatureEncpointForSignException() { XmlSignerEndpoint endpoint = (XmlSignerEndpoint) context().getEndpoint("xmlsecurity:sign:signexceptions?keyAccessor=#accessor" + // "&signatureAlgorithm=http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"); return endpoint; } @Test public void testExceptionParentXpathWithNoElementResult() throws Exception { XmlSignerEndpoint endpoint = getSignatureEncpointForSignException(); MockEndpoint mock = setupExceptionMock(); try { String myPayload = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + (includeNewLine ? "\n" : "") + "<ns:root ID=\"rootId\" xmlns:ns=\"http://test\"></ns:root>"; endpoint.setParentXpath(XmlSignatureHelper.getXpathFilter("/pre:root/@ID", Collections.singletonMap("pre", "http://test"))); // xpath with no element result sendBody("direct:signexceptions", myPayload); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureException.class, "The parent XPath /pre:root/@ID returned no element. Check the configuration of the XML signer component.", null); } finally { endpoint.setParentXpath(null); } } @Test public void testEnvelopedSignatureWithParentXpath() throws Exception { String myPayload = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + (includeNewLine ? "\n" : "") + "<ns:root xmlns:ns=\"http://test\"><a>a1</a><a/><test>Test Message</test></ns:root>"; setupMock(myPayload); sendBody("direct:envelopedParentXpath", myPayload); assertMockEndpointsSatisfied(); } XmlSignerEndpoint getDetachedSignerEndpoint() { XmlSignerEndpoint endpoint = (XmlSignerEndpoint) context().getEndpoint( "xmlsecurity:sign:detached?keyAccessor=#keyAccessorDefault&xpathsToIdAttributes=#xpathsToIdAttributes&"// + "schemaResourceUri=org/apache/camel/component/xmlsecurity/Test.xsd&signatureId=&clearHeaders=false"); return endpoint; } private void checkOutputEncoding(String inputEncoding, String signerEncoding, String outputEncoding, String signerEndpointUri, String verifierEndpointUri, String directStart) throws InterruptedException, UnsupportedEncodingException { byte[] inputPayload = getPayloadForEncoding(inputEncoding); byte[] expectedPayload = getPayloadForEncoding(outputEncoding); MockEndpoint mock = getMockEndpoint("mock:result"); mock.expectedBodiesReceived(expectedPayload); MockEndpoint mockSigned = getMockEndpoint("mock:signed"); mock.expectedMessageCount(1); XmlSignerEndpoint endpointSigner = (XmlSignerEndpoint) context().getEndpoint(signerEndpointUri); XmlVerifierEndpoint endpoinVerifier = (XmlVerifierEndpoint) context().getEndpoint(verifierEndpointUri); try { endpointSigner.setOutputXmlEncoding(signerEncoding); endpoinVerifier.setOutputXmlEncoding(outputEncoding); sendBody(directStart, inputPayload); assertMockEndpointsSatisfied(); Message signedMessage = mockSigned.getExchanges().get(0).getIn(); byte[] signedBytes = signedMessage.getBody(byte[].class); String signedPayload = new String(signedBytes, signerEncoding); assertTrue(signedPayload.contains(signerEncoding)); String charsetHeaderSigner = signedMessage.getHeader(Exchange.CHARSET_NAME, String.class); assertEquals(signerEncoding, charsetHeaderSigner); String charsetHeaderVerifier = mock.getExchanges().get(0).getIn().getHeader(Exchange.CHARSET_NAME, String.class); assertEquals(outputEncoding, charsetHeaderVerifier); } finally { endpointSigner.setOutputXmlEncoding(null); endpoinVerifier.setOutputXmlEncoding(null); } } private void checkBodyContains(MockEndpoint mock, String expectedPartContent) { Message message = getMessage(mock); String body = message.getBody(String.class); assertNotNull(body); assertTrue("The message body " + body + " does not contain the expected string " + expectedPartContent, body.contains(expectedPartContent)); } private Object checkXpath(MockEndpoint mock, String xpathString, final Map<String, String> prefix2Namespace) throws XPathExpressionException, SAXException, IOException, ParserConfigurationException { Message mess = getMessage(mock); InputStream body = mess.getBody(InputStream.class); assertNotNull(body); XPathFactory xpathFactory = XPathFactory.newInstance(); XPath xpath = xpathFactory.newXPath(); NamespaceContext nc = new NamespaceContext() { @SuppressWarnings("rawtypes") @Override public Iterator getPrefixes(String namespaceURI) { return null; } @Override public String getPrefix(String namespaceURI) { return null; } @Override public String getNamespaceURI(String prefix) { return prefix2Namespace.get(prefix); } }; xpath.setNamespaceContext(nc); XPathExpression expr = xpath.compile(xpathString); Object result = expr.evaluate(XmlSignatureHelper.newDocumentBuilder(true).parse(body), XPathConstants.NODE); assertNotNull("The xpath " + xpathString + " returned a null value", result); return result; } Message getMessage(MockEndpoint mock) { List<Exchange> exs = mock.getExchanges(); assertNotNull(exs); assertEquals(1, exs.size()); Exchange ex = exs.get(0); Message mess = ex.getIn(); assertNotNull(mess); return mess; } private void checkThrownException(MockEndpoint mock, Class<? extends Exception> cl, Class<? extends Exception> expectedCauseClass) throws Exception { checkThrownException(mock, cl, null, expectedCauseClass); } static void checkThrownException(MockEndpoint mock, Class<? extends Exception> cl, String expectedMessage, Class<? extends Exception> expectedCauseClass) throws Exception { Exception e = (Exception) mock.getExchanges().get(0).getProperty(Exchange.EXCEPTION_CAUGHT); assertNotNull("Expected excpetion " + cl.getName() + " missing", e); if (e.getClass() != cl) { String stackTrace = getStrackTrace(e); fail("Exception " + cl.getName() + " excpected, but was " + e.getClass().getName() + ": " + stackTrace); } if (expectedMessage != null) { assertEquals(expectedMessage, e.getMessage()); } if (expectedCauseClass != null) { Throwable cause = e.getCause(); assertNotNull("Expected cause exception" + expectedCauseClass.getName() + " missing", cause); if (expectedCauseClass != cause.getClass()) { fail("Cause exception " + expectedCauseClass.getName() + " expected, but was " + cause.getClass().getName() + ": " + getStrackTrace(e)); } } } private static String getStrackTrace(Exception e) throws UnsupportedEncodingException { ByteArrayOutputStream os = new ByteArrayOutputStream(); PrintWriter w = new PrintWriter(os); e.printStackTrace(w); w.close(); String stackTrace = new String(os.toByteArray(), "UTF-8"); return stackTrace; } private MockEndpoint setupExceptionMock() { MockEndpoint mock = getMockEndpoint("mock:exception"); mock.setExpectedMessageCount(1); MockEndpoint mockResult = getMockEndpoint("mock:result"); mockResult.setExpectedMessageCount(0); return mock; } private MockEndpoint setupMock() { return setupMock(payload); } private MockEndpoint setupMock(String payload) { 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("RSA", 1024); disableJMX(); super.setUp(); } public void setUpKeys(String algorithm, int keylength) throws Exception { keyPair = getKeyPair(algorithm, keylength); } public static KeyPair getKeyPair(String algorithm, int keylength) { KeyPairGenerator keyGen; try { keyGen = KeyPairGenerator.getInstance(algorithm); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } keyGen.initialize(keylength, new SecureRandom()); return keyGen.generateKeyPair(); } public static KeyStore loadKeystore() throws Exception { KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); InputStream in = XmlSignatureTest.class.getResourceAsStream("/bob.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()); } private AlgorithmMethod getCanonicalizationMethod() { List<String> inclusivePrefixes = new ArrayList<String>(1); inclusivePrefixes.add("ds"); return XmlSignatureHelper.getCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE, inclusivePrefixes); } private List<AlgorithmMethod> getTransformsXPath2() { List<XPathAndFilter> list = new ArrayList<XPathAndFilter>(3); XPathAndFilter xpath1 = new XPathAndFilter("//n0:ToBeSigned", XPathType.Filter.INTERSECT.toString()); list.add(xpath1); XPathAndFilter xpath2 = new XPathAndFilter("//n0:NotToBeSigned", XPathType.Filter.SUBTRACT.toString()); list.add(xpath2); XPathAndFilter xpath3 = new XPathAndFilter("//n0:ReallyToBeSigned", XPathType.Filter.UNION.toString()); list.add(xpath3); List<AlgorithmMethod> result = new ArrayList<AlgorithmMethod>(2); result.add(XmlSignatureHelper.getCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE)); result.add(XmlSignatureHelper.getXPath2Transform(list, getNamespaceMap())); return result; } private Map<String, String> getNamespaceMap() { Map<String, String> result = new HashMap<String, String>(1); result.put("n0", "http://test/test"); return result; } private List<AlgorithmMethod> getTransformsXsltXpath() { try { AlgorithmMethod transformXslt = XmlSignatureHelper.getXslTransform("/org/apache/camel/component/xmlsecurity/xslt_test.xsl"); Map<String, String> namespaceMap = new HashMap<String, String>(1); namespaceMap.put("n0", "https://org.apache/camel/xmlsecurity/test"); AlgorithmMethod transformXpath = XmlSignatureHelper.getXPathTransform("//n0:XMLSecurity/n0:Content", namespaceMap); // I removed base 64 transform because the JDK implementation does // not correctly support this transformation // AlgorithmMethod transformBase64 = helper.getBase64Transform(); List<AlgorithmMethod> result = new ArrayList<AlgorithmMethod>(3); result.add(XmlSignatureHelper.getCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE)); result.add(transformXslt); result.add(transformXpath); // result.add(transformBase64); return result; } catch (Exception e) { throw new IllegalStateException(e); } } static KeyAccessor getKeyAccessor(final PrivateKey privateKey) { KeyAccessor accessor = new KeyAccessor() { @Override public KeySelector getKeySelector(Message message) throws Exception { return KeySelector.singletonKeySelector(privateKey); } @Override public KeyInfo getKeyInfo(Message mess, Node messageBody, KeyInfoFactory keyInfoFactory) throws Exception { return null; } }; return accessor; } public static String getBaseUri() { String uri = "file:/" + System.getProperty("user.dir") + "/src/test/resources/org/apache/camel/component/xmlsecurity/"; return uri.replace('\\', '/'); } public static KeySelector getKeyValueKeySelector() { return new KeyValueKeySelector(); } /** * KeySelector which retrieves the public key from the KeyValue element and * returns it. NOTE: If the key algorithm doesn't match signature algorithm, * then the public key will be ignored. */ static class KeyValueKeySelector extends KeySelector { public KeySelectorResult select(KeyInfo keyInfo, KeySelector.Purpose purpose, AlgorithmMethod method, XMLCryptoContext context) throws KeySelectorException { if (keyInfo == null) { throw new KeySelectorException("Null KeyInfo object!"); } SignatureMethod sm = (SignatureMethod) method; @SuppressWarnings("rawtypes") List list = keyInfo.getContent(); for (int i = 0; i < list.size(); i++) { XMLStructure xmlStructure = (XMLStructure) list.get(i); if (xmlStructure instanceof KeyValue) { PublicKey pk = null; try { pk = ((KeyValue) xmlStructure).getPublicKey(); } catch (KeyException ke) { throw new KeySelectorException(ke); } // make sure algorithm is compatible with method if (algEquals(sm.getAlgorithm(), pk.getAlgorithm())) { return new SimpleKeySelectorResult(pk); } } } throw new KeySelectorException("No KeyValue element found!"); } static boolean algEquals(String algURI, String algName) { return (algName.equalsIgnoreCase("DSA") && algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1)) || (algName.equalsIgnoreCase("RSA") && algURI.equalsIgnoreCase(SignatureMethod.RSA_SHA1)); } } private static class SimpleKeySelectorResult implements KeySelectorResult { private PublicKey pk; SimpleKeySelectorResult(PublicKey pk) { this.pk = pk; } public Key getKey() { return pk; } } public static Map<String, ? extends Object> getCrytoContextProperties() { return Collections.singletonMap("org.jcp.xml.dsig.validateManifests", Boolean.FALSE); } public static KeyAccessor getDefaultKeyAccessor() throws Exception { return TestKeystore.getKeyAccessor("bob"); } public static KeySelector getDefaultKeySelector() throws Exception { return TestKeystore.getKeySelector("bob"); } public static KeyAccessor getDefaultKeyAccessorDsa() throws Exception { return TestKeystore.getKeyAccessor("bobdsa"); } public static KeySelector getDefaultKeySelectorDsa() throws Exception { return TestKeystore.getKeySelector("bobdsa"); } public static XmlSignatureChecker getEnvelopingXmlSignatureChecker() { return new EnvelopingXmlSignatureChecker(); } public static XmlSignature2Message getXmlSignature2MessageWithTimestampdProperty() { return new XmlSignature2Message2MessageWithTimestampProperty(); } public static ValidationFailedHandler getValidationFailedHandlerIgnoreManifestFailures() { return new ValidationFailedHandlerIgnoreManifestFailures(); } public static XmlSignatureProperties getSignatureProperties() { return new TimestampProperty(); } public static XPathFilterParameterSpec getNodeSerachXPath() { Map<String, String> prefix2Namespace = Collections.singletonMap("pre", "http://test/test"); return XmlSignatureHelper.getXpathFilter("//pre:root", prefix2Namespace); } public static URIDereferencer getSameDocumentUriDereferencer() { return SameDocumentUriDereferencer.getInstance(); } public static XPathFilterParameterSpec getParentXPathBean() { Map<String, String> prefix2Namespace = Collections.singletonMap("ns", "http://test"); return XmlSignatureHelper.getXpathFilter("/ns:root/a[last()]", prefix2Namespace); } }