/** * 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.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeMap; import javax.xml.XMLConstants; import javax.xml.crypto.dsig.DigestMethod; import javax.xml.crypto.dsig.XMLSignature; import javax.xml.crypto.dsig.spec.XPathFilterParameterSpec; import javax.xml.namespace.NamespaceContext; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Source; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; 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.Document; import org.w3c.dom.Node; import org.xml.sax.SAXException; import org.apache.camel.Exchange; import org.apache.camel.Message; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; import org.apache.camel.component.xmlsecurity.api.DefaultXAdESSignatureProperties; import org.apache.camel.component.xmlsecurity.api.XAdESEncapsulatedPKIData; import org.apache.camel.component.xmlsecurity.api.XAdESSignatureProperties; import org.apache.camel.component.xmlsecurity.api.XmlSignatureConstants; import org.apache.camel.component.xmlsecurity.api.XmlSignatureException; import org.apache.camel.component.xmlsecurity.api.XmlSignatureHelper; import org.apache.camel.component.xmlsecurity.api.XmlSignatureProperties; import org.apache.camel.component.xmlsecurity.util.TestKeystore; import org.apache.camel.impl.JndiRegistry; import org.apache.camel.test.junit4.CamelTestSupport; import org.apache.camel.test.junit4.TestSupport; import org.junit.Before; import org.junit.Test; import static org.apache.camel.component.xmlsecurity.XmlSignatureTest.checkThrownException; public class XAdESSignaturePropertiesTest extends CamelTestSupport { private static final String NOT_EMPTY = "NOT_EMPTY"; private static String payload; static { boolean includeNewLine = true; 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>"; } @Before public void setUp() throws Exception { disableJMX(); super.setUp(); } @Override protected JndiRegistry createRegistry() throws Exception { JndiRegistry registry = super.createRegistry(); registry.bind("keyAccessorDefault", TestKeystore.getKeyAccessor("bob")); registry.bind("xmlSignatureProperties", getXmlSignatureProperties("bob")); 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); return registry; } @Override protected RouteBuilder[] createRouteBuilders() throws Exception { return new RouteBuilder[] {new RouteBuilder() { public void configure() throws Exception { onException(XmlSignatureException.class).handled(true).to("mock:exception"); from("direct:enveloped") .to("xmlsecurity:sign:xades?keyAccessor=#keyAccessorDefault&properties=#xmlSignatureProperties&parentLocalName=root&parentNamespace=http://test/test") .to("mock:result"); } }, new RouteBuilder() { public void configure() throws Exception { onException(XmlSignatureException.class).handled(true).to("mock:exception"); from("direct:enveloping").to("xmlsecurity:sign:xades?keyAccessor=#keyAccessorDefault&properties=#xmlSignatureProperties") .to("mock:result"); } }, new RouteBuilder() { public void configure() throws Exception { onException(XmlSignatureException.class).handled(true).to("mock:exception"); from("direct:emptySignatureId").to( "xmlsecurity:sign:xades?keyAccessor=#keyAccessorDefault&properties=#xmlSignatureProperties&signatureId=").to( "mock:result"); } }, new RouteBuilder() { public void configure() throws Exception { 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&properties=#xmlSignatureProperties") .to("mock:result"); } } }; } @Test public void envelopingAllParameters() throws Exception { Document doc = testEnveloping(); Map<String, String> prefix2Namespace = getPrefix2NamespaceMap(); String pathToSignatureProperties = getPathToSignatureProperties(); // signing time checkXpath(doc, pathToSignatureProperties + "etsi:SigningTime/text()", prefix2Namespace, NOT_EMPTY); // signing certificate checkXpath(doc, pathToSignatureProperties + "etsi:SigningCertificate/etsi:Cert/etsi:CertDigest/ds:DigestMethod/@Algorithm", prefix2Namespace, DigestMethod.SHA256); checkXpath(doc, pathToSignatureProperties + "etsi:SigningCertificate/etsi:Cert/etsi:CertDigest/ds:DigestValue/text()", prefix2Namespace, NOT_EMPTY); checkXpath(doc, pathToSignatureProperties + "etsi:SigningCertificate/etsi:Cert/etsi:IssuerSerial/ds:X509IssuerName/text()", prefix2Namespace, NOT_EMPTY); checkXpath(doc, pathToSignatureProperties + "etsi:SigningCertificate/etsi:Cert/etsi:IssuerSerial/ds:X509SerialNumber/text()", prefix2Namespace, NOT_EMPTY); checkXpath(doc, pathToSignatureProperties + "etsi:SigningCertificate/etsi:Cert/@URI", prefix2Namespace, "http://certuri"); // signature policy checkXpath(doc, pathToSignatureProperties + "etsi:SignaturePolicyIdentifier/etsi:SignaturePolicyId/etsi:SigPolicyId/etsi:Identifier/text()", prefix2Namespace, "1.2.840.113549.1.9.16.6.1"); checkXpath(doc, pathToSignatureProperties + "etsi:SignaturePolicyIdentifier/etsi:SignaturePolicyId/etsi:SigPolicyId/etsi:Identifier/@Qualifier", prefix2Namespace, "OIDAsURN"); checkXpath(doc, pathToSignatureProperties + "etsi:SignaturePolicyIdentifier/etsi:SignaturePolicyId/etsi:SigPolicyId/etsi:Description/text()", prefix2Namespace, "invoice version 3.1"); checkXpath(doc, pathToSignatureProperties + "etsi:SignaturePolicyIdentifier/etsi:SignaturePolicyId/etsi:SigPolicyHash/ds:DigestMethod/@Algorithm", prefix2Namespace, DigestMethod.SHA256); checkXpath(doc, pathToSignatureProperties + "etsi:SignaturePolicyIdentifier/etsi:SignaturePolicyId/etsi:SigPolicyHash/ds:DigestValue/text()", prefix2Namespace, "Ohixl6upD6av8N7pEvDABhEL6hM="); checkXpath( doc, pathToSignatureProperties + "etsi:SignaturePolicyIdentifier/etsi:SignaturePolicyId/etsi:SigPolicyQualifiers/etsi:SigPolicyQualifier[1]/etsi:SPURI/text()", prefix2Namespace, "http://test.com/sig.policy.pdf"); checkXpath( doc, pathToSignatureProperties + "etsi:SignaturePolicyIdentifier/etsi:SignaturePolicyId/etsi:SigPolicyQualifiers/etsi:SigPolicyQualifier[1]/etsi:SPUserNotice/etsi:ExplicitText/text()", prefix2Namespace, "display text"); checkXpath(doc, pathToSignatureProperties + "etsi:SignaturePolicyIdentifier/etsi:SignaturePolicyId/etsi:SigPolicyQualifiers/etsi:SigPolicyQualifier[2]/text()", prefix2Namespace, "category B"); checkXpath( doc, pathToSignatureProperties + "etsi:SignaturePolicyIdentifier/etsi:SignaturePolicyId/etsi:SigPolicyId/etsi:DocumentationReferences/etsi:DocumentationReference[1]/text()", prefix2Namespace, "http://test.com/policy.doc.ref1.txt"); checkXpath( doc, pathToSignatureProperties + "etsi:SignaturePolicyIdentifier/etsi:SignaturePolicyId/etsi:SigPolicyId/etsi:DocumentationReferences/etsi:DocumentationReference[2]/text()", prefix2Namespace, "http://test.com/policy.doc.ref2.txt"); // production place checkXpath(doc, pathToSignatureProperties + "etsi:SignatureProductionPlace/etsi:City/text()", prefix2Namespace, "Munich"); checkXpath(doc, pathToSignatureProperties + "etsi:SignatureProductionPlace/etsi:StateOrProvince/text()", prefix2Namespace, "Bavaria"); checkXpath(doc, pathToSignatureProperties + "etsi:SignatureProductionPlace/etsi:PostalCode/text()", prefix2Namespace, "80331"); checkXpath(doc, pathToSignatureProperties + "etsi:SignatureProductionPlace/etsi:CountryName/text()", prefix2Namespace, "Germany"); // signer role checkXpath(doc, pathToSignatureProperties + "etsi:SignerRole/etsi:ClaimedRoles/etsi:ClaimedRole[1]/text()", prefix2Namespace, "test"); checkXpath(doc, pathToSignatureProperties + "etsi:SignerRole/etsi:ClaimedRoles/etsi:ClaimedRole[2]/TestRole/text()", prefix2Namespace, "TestRole"); checkXpath(doc, pathToSignatureProperties + "etsi:SignerRole/etsi:CertifiedRoles/etsi:CertifiedRole/text()", prefix2Namespace, "Ahixl6upD6av8N7pEvDABhEL6hM="); checkXpath(doc, pathToSignatureProperties + "etsi:SignerRole/etsi:CertifiedRoles/etsi:CertifiedRole/@Encoding", prefix2Namespace, "http://uri.etsi.org/01903/v1.2.2#DER"); checkXpath(doc, pathToSignatureProperties + "etsi:SignerRole/etsi:CertifiedRoles/etsi:CertifiedRole/@Id", prefix2Namespace, "IdCertifiedRole"); String pathToDataObjectProperties = "/ds:Signature/ds:Object/etsi:QualifyingProperties/etsi:SignedProperties/etsi:SignedDataObjectProperties/"; //DataObjectFormat checkXpath(doc, pathToDataObjectProperties + "etsi:DataObjectFormat/etsi:Description/text()", prefix2Namespace, "invoice"); checkXpath(doc, pathToDataObjectProperties + "etsi:DataObjectFormat/etsi:MimeType/text()", prefix2Namespace, "text/xml"); checkXpath(doc, pathToDataObjectProperties + "etsi:DataObjectFormat/etsi:ObjectIdentifier/etsi:Identifier/text()", prefix2Namespace, "1.2.840.113549.1.9.16.6.2"); checkXpath(doc, pathToDataObjectProperties + "etsi:DataObjectFormat/etsi:ObjectIdentifier/etsi:Identifier/@Qualifier", prefix2Namespace, "OIDAsURN"); checkXpath(doc, pathToDataObjectProperties + "etsi:DataObjectFormat/etsi:ObjectIdentifier/etsi:Description/text()", prefix2Namespace, "identifier desc"); checkXpath(doc, pathToDataObjectProperties + "etsi:DataObjectFormat/etsi:ObjectIdentifier/etsi:DocumentationReferences/etsi:DocumentationReference[1]/text()", prefix2Namespace, "http://test.com/dataobject.format.doc.ref1.txt"); checkXpath(doc, pathToDataObjectProperties + "etsi:DataObjectFormat/etsi:ObjectIdentifier/etsi:DocumentationReferences/etsi:DocumentationReference[2]/text()", prefix2Namespace, "http://test.com/dataobject.format.doc.ref2.txt"); //commitment checkXpath(doc, pathToDataObjectProperties + "etsi:CommitmentTypeIndication/etsi:CommitmentTypeId/etsi:Identifier/text()", prefix2Namespace, "1.2.840.113549.1.9.16.6.4"); checkXpath(doc, pathToDataObjectProperties + "etsi:CommitmentTypeIndication/etsi:CommitmentTypeId/etsi:Identifier/@Qualifier", prefix2Namespace, "OIDAsURI"); checkXpath(doc, pathToDataObjectProperties + "etsi:CommitmentTypeIndication/etsi:CommitmentTypeId/etsi:Description/text()", prefix2Namespace, "description for commitment type ID"); checkXpath(doc, pathToDataObjectProperties + "etsi:CommitmentTypeIndication/etsi:CommitmentTypeId/etsi:DocumentationReferences/etsi:DocumentationReference[1]/text()", prefix2Namespace, "http://test.com/commitment.ref1.txt"); checkXpath(doc, pathToDataObjectProperties + "etsi:CommitmentTypeIndication/etsi:CommitmentTypeId/etsi:DocumentationReferences/etsi:DocumentationReference[2]/text()", prefix2Namespace, "http://test.com/commitment.ref2.txt"); checkXpath(doc, pathToDataObjectProperties + "etsi:CommitmentTypeIndication/etsi:CommitmentTypeQualifiers/etsi:CommitmentTypeQualifier[1]/text()", prefix2Namespace, "commitment qualifier"); checkXpath(doc, pathToDataObjectProperties + "etsi:CommitmentTypeIndication/etsi:CommitmentTypeQualifiers/etsi:CommitmentTypeQualifier[2]/C/text()", prefix2Namespace, "c"); } @Test public void noSigningTime() throws Exception { XmlSignerEndpoint endpoint = getSignerEndpoint(); XAdESSignatureProperties props = (XAdESSignatureProperties) endpoint.getProperties(); props.setAddSigningTime(false); Document doc = testEnveloping(); Map<String, String> prefix2Namespace = getPrefix2NamespaceMap(); String pathToSignatureProperties = getPathToSignatureProperties(); checkNode(doc, pathToSignatureProperties + "etsi:SigningTime", prefix2Namespace, false); } @Test public void noSigningCertificate() throws Exception { XmlSignerEndpoint endpoint = getSignerEndpoint(); XAdESSignatureProperties newProps = new XAdESSignatureProperties(); newProps.setAddSigningTime(true); endpoint.setProperties(newProps); Document doc = testEnveloping(); Map<String, String> prefix2Namespace = getPrefix2NamespaceMap(); String pathToSignatureProperties = getPathToSignatureProperties(); checkNode(doc, pathToSignatureProperties + "etsi:SigningTime", prefix2Namespace, true); checkNode(doc, pathToSignatureProperties + "etsi:SigningCertificate", prefix2Namespace, false); } @Test public void certificateChain() throws Exception { XmlSignerEndpoint endpoint = getSignerEndpoint(); endpoint.setProperties(new CertChainXAdESSignatureProperties()); Document doc = testEnveloping(); Map<String, String> prefix2Namespace = getPrefix2NamespaceMap(); String pathToSignatureProperties = getPathToSignatureProperties(); // signing certificate checkXpath(doc, pathToSignatureProperties + "etsi:SigningCertificate/etsi:Cert/etsi:CertDigest/ds:DigestMethod/@Algorithm", prefix2Namespace, DigestMethod.SHA256); checkXpath(doc, pathToSignatureProperties + "etsi:SigningCertificate/etsi:Cert/etsi:CertDigest/ds:DigestValue/text()", prefix2Namespace, NOT_EMPTY); checkXpath(doc, pathToSignatureProperties + "etsi:SigningCertificate/etsi:Cert/etsi:IssuerSerial/ds:X509IssuerName/text()", prefix2Namespace, NOT_EMPTY); checkXpath(doc, pathToSignatureProperties + "etsi:SigningCertificate/etsi:Cert/etsi:IssuerSerial/ds:X509SerialNumber/text()", prefix2Namespace, NOT_EMPTY); } @Test public void noPropertiesSpecified() throws Exception { XmlSignerEndpoint endpoint = getSignerEndpoint(); XAdESSignatureProperties props = new XAdESSignatureProperties(); props.setAddSigningTime(false); endpoint.setProperties(props); Document doc = testEnveloping(); // expecting no Qualifying Properties checkNode(doc, "/ds:Signature/ds:Object/etsi:QualifyingProperties", getPrefix2NamespaceMap(), false); } @Test public void policyImplied() throws Exception { XmlSignerEndpoint endpoint = getSignerEndpoint(); XAdESSignatureProperties props = (XAdESSignatureProperties) endpoint.getProperties(); props.setSignaturePolicy(XAdESSignatureProperties.SIG_POLICY_IMPLIED); Document doc = testEnveloping(); String pathToSignatureProperties = getPathToSignatureProperties(); checkNode(doc, pathToSignatureProperties + "etsi:SignaturePolicyIdentifier/etsi:SignaturePolicyId", getPrefix2NamespaceMap(), false); checkNode(doc, pathToSignatureProperties + "etsi:SignaturePolicyIdentifier/etsi:SignaturePolicyImplied", getPrefix2NamespaceMap(), true); } @Test public void policyNone() throws Exception { XmlSignerEndpoint endpoint = getSignerEndpoint(); XAdESSignatureProperties props = (XAdESSignatureProperties) endpoint.getProperties(); props.setSignaturePolicy(XAdESSignatureProperties.SIG_POLICY_NONE); Document doc = testEnveloping(); String pathToSignatureProperties = getPathToSignatureProperties(); checkNode(doc, pathToSignatureProperties + "etsi:SignaturePolicyIdentifier", getPrefix2NamespaceMap(), false); } @Test public void allPropertiesEmpty() throws Exception { XmlSignerEndpoint endpoint = getSignerEndpoint(); XAdESSignatureProperties props = new XAdESSignatureProperties(); props.setAddSigningTime(false); props.setCommitmentTypeId(""); props.setCommitmentTypeIdDescription(""); props.setCommitmentTypeIdQualifier(""); props.setDataObjectFormatDescription(""); props.setDataObjectFormatIdentifier(""); props.setDataObjectFormatIdentifierDescription(""); props.setDataObjectFormatIdentifierQualifier(""); props.setDataObjectFormatMimeType(""); props.setDigestAlgorithmForSigningCertificate(""); props.setSignaturePolicy("None"); props.setSigPolicyId(""); props.setSigPolicyIdDescription(""); props.setSigPolicyIdQualifier(""); props.setSignaturePolicyDigestAlgorithm(""); props.setSignaturePolicyDigestValue(""); props.setSignatureProductionPlaceCity(""); props.setSignatureProductionPlaceCountryName(""); props.setSignatureProductionPlacePostalCode(""); props.setSignatureProductionPlaceStateOrProvince(""); endpoint.setProperties(props); Document doc = testEnveloping(); // expecting no Qualifying Properties checkNode(doc, "/ds:Signature/ds:Object/etsi:QualifyingProperties", getPrefix2NamespaceMap(), false); } @Test public void emptySignatureId() throws Exception { Document doc = testEnveloping("direct:emptySignatureId"); checkNode(doc, "/ds:Signature/ds:Object/etsi:QualifyingProperties", getPrefix2NamespaceMap(), true); } @Test public void prefixAndNamespace() throws Exception { XmlSignerEndpoint endpoint = getSignerEndpoint(); XAdESSignatureProperties props = (XAdESSignatureProperties) endpoint.getProperties(); props.setPrefix("p"); props.setNamespace(XAdESSignatureProperties.HTTP_URI_ETSI_ORG_01903_V1_1_1); props.setCommitmentTypeIdDescription(null); props.setCommitmentTypeIdDocumentationReferences(Collections.<String> emptyList()); props.setCommitmentTypeIdQualifier(null); props.setDataObjectFormatIdentifierDescription(null); props.setDataObjectFormatIdentifierDocumentationReferences(Collections.<String> emptyList()); props.setDataObjectFormatIdentifierQualifier(null); props.setSigPolicyIdDescription(null); props.setSigPolicyIdDocumentationReferences(Collections.<String> emptyList()); props.setSigPolicyIdQualifier(null); // the following lists must be set to empty because otherwise they would contain XML fragments with a wrong namespace props.setSigPolicyQualifiers(Collections.<String> emptyList()); props.setSignerClaimedRoles(Collections.<String> emptyList()); props.setCommitmentTypeQualifiers(Collections.<String> emptyList()); Document doc = testEnveloping(); Map<String, String> prefix2Namespace = new TreeMap<String, String>(); prefix2Namespace.put("ds", XMLSignature.XMLNS); prefix2Namespace.put("etsi", XAdESSignatureProperties.HTTP_URI_ETSI_ORG_01903_V1_1_1); XPathExpression expr = getXpath("/ds:Signature/ds:Object/etsi:QualifyingProperties", prefix2Namespace); Object result = expr.evaluate(doc, XPathConstants.NODE); assertNotNull(result); Node node = (Node) result; assertEquals("p", node.getPrefix()); assertEquals(XAdESSignatureProperties.HTTP_URI_ETSI_ORG_01903_V1_1_1, node.getNamespaceURI()); } @Test public void headers() throws Exception { Map<String, Object> header = new TreeMap<String, Object>(); header.put(XmlSignatureConstants.HEADER_XADES_PREFIX, "ns1"); header.put(XmlSignatureConstants.HEADER_XADES_NAMESPACE, XAdESSignatureProperties.HTTP_URI_ETSI_ORG_01903_V1_2_2); header.put(XmlSignatureConstants.HEADER_XADES_QUALIFYING_PROPERTIES_ID, "QualId"); header.put(XmlSignatureConstants.HEADER_XADES_SIGNED_DATA_OBJECT_PROPERTIES_ID, "ObjId"); header.put(XmlSignatureConstants.HEADER_XADES_SIGNED_SIGNATURE_PROPERTIES_ID, "SigId"); header.put(XmlSignatureConstants.HEADER_XADES_DATA_OBJECT_FORMAT_ENCODING, "base64"); XmlSignerEndpoint endpoint = getSignerEndpoint(); XAdESSignatureProperties props = (XAdESSignatureProperties) endpoint.getProperties(); // the following lists must be set to empty because otherwise they would contain XML fragments with a wrong namespace props.setSigPolicyQualifiers(Collections.<String> emptyList()); props.setSignerClaimedRoles(Collections.<String> emptyList()); props.setCommitmentTypeQualifiers(Collections.<String> emptyList()); Document doc = testEnveloping("direct:enveloping", header); Map<String, String> prefix2Namespace = new TreeMap<String, String>(); prefix2Namespace.put("ds", XMLSignature.XMLNS); prefix2Namespace.put("etsi", XAdESSignatureProperties.HTTP_URI_ETSI_ORG_01903_V1_2_2); XPathExpression expr = getXpath("/ds:Signature/ds:Object/etsi:QualifyingProperties", prefix2Namespace); Object result = expr.evaluate(doc, XPathConstants.NODE); assertNotNull(result); Node node = (Node) result; assertEquals("ns1", node.getPrefix()); assertEquals(XAdESSignatureProperties.HTTP_URI_ETSI_ORG_01903_V1_2_2, node.getNamespaceURI()); checkXpath(doc, "/ds:Signature/ds:Object/etsi:QualifyingProperties/@Id", prefix2Namespace, "QualId"); checkXpath(doc, "/ds:Signature/ds:Object/etsi:QualifyingProperties/etsi:SignedProperties/etsi:SignedDataObjectProperties/@Id", prefix2Namespace, "ObjId"); checkXpath(doc, "/ds:Signature/ds:Object/etsi:QualifyingProperties/etsi:SignedProperties/etsi:SignedSignatureProperties/@Id", prefix2Namespace, "SigId"); checkXpath( doc, "/ds:Signature/ds:Object/etsi:QualifyingProperties/etsi:SignedProperties/etsi:SignedDataObjectProperties/etsi:DataObjectFormat/etsi:Encoding/text()", prefix2Namespace, "base64"); } @Test public void enveloped() throws Exception { setupMock(); sendBody("direct:enveloped", payload); assertMockEndpointsSatisfied(); } @Test public void detached() throws Exception { String detachedPayload = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + // "<ns:root xmlns:ns=\"http://test\"><a ID=\"myID\"><b>bValue</b></a></ns:root>"; setupMock(); sendBody("direct:detached", detachedPayload); assertMockEndpointsSatisfied(); } @Test public void sigPolicyIdEmpty() throws Exception { testExceptionSigPolicyIdMissing(""); } @Test public void sigPolicyIdNull() throws Exception { testExceptionSigPolicyIdMissing(null); } private void testExceptionSigPolicyIdMissing(String value) throws InterruptedException, Exception { MockEndpoint mock = getMockEndpoint("mock:exception"); mock.expectedMessageCount(1); XmlSignerEndpoint endpoint = getSignerEndpoint(); XAdESSignatureProperties props = (XAdESSignatureProperties) endpoint.getProperties(); props.setSigPolicyId(value); sendBody("direct:enveloping", payload, Collections.<String, Object> emptyMap()); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureException.class, "The XAdES-EPES confguration is invalid. The signature policy identifier is missing.", null); } @Test public void sigPolicyDigestEmpty() throws Exception { testExceptionSigPolicyDigestMissing(""); } @Test public void sigPolicyDigestNull() throws Exception { testExceptionSigPolicyDigestMissing(null); } private void testExceptionSigPolicyDigestMissing(String value) throws InterruptedException, Exception { MockEndpoint mock = getMockEndpoint("mock:exception"); mock.expectedMessageCount(1); XmlSignerEndpoint endpoint = getSignerEndpoint(); XAdESSignatureProperties props = (XAdESSignatureProperties) endpoint.getProperties(); props.setSignaturePolicyDigestValue(value); sendBody("direct:enveloping", payload, Collections.<String, Object> emptyMap()); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureException.class, "The XAdES-EPES confguration is invalid. The digest value for the signature policy is missing.", null); } @Test public void sigPolicyDigestAlgoEmpty() throws Exception { testExceptionSigPolicyDigestAlgoMissing(""); } @Test public void sigPolicyDigestAlgoNull() throws Exception { testExceptionSigPolicyDigestAlgoMissing(null); } private void testExceptionSigPolicyDigestAlgoMissing(String value) throws InterruptedException, Exception { MockEndpoint mock = getMockEndpoint("mock:exception"); mock.expectedMessageCount(1); XmlSignerEndpoint endpoint = getSignerEndpoint(); XAdESSignatureProperties props = (XAdESSignatureProperties) endpoint.getProperties(); props.setSignaturePolicyDigestAlgorithm(value); sendBody("direct:enveloping", payload, Collections.<String, Object> emptyMap()); assertMockEndpointsSatisfied(); checkThrownException(mock, XmlSignatureException.class, "The XAdES-EPES confguration is invalid. The digest algorithm for the signature policy is missing.", null); } @Test public void invalidXmlFragmentForClaimedRole() throws Exception { MockEndpoint mock = getMockEndpoint("mock:exception"); mock.expectedMessageCount(1); XmlSignerEndpoint endpoint = getSignerEndpoint(); XAdESSignatureProperties props = (XAdESSignatureProperties) endpoint.getProperties(); props.setSignerClaimedRoles(Collections.singletonList("<ClaimedRole>wrong XML fragment<ClaimedRole>")); // Element 'ClaimedRole' is not closed correctly sendBody("direct:enveloping", payload, Collections.<String, Object> emptyMap()); assertMockEndpointsSatisfied(); checkThrownException( mock, XmlSignatureException.class, "The XAdES confguration is invalid. The list of the claimed roles contains the invalid entry '<ClaimedRole>wrong XML fragment<ClaimedRole>'. An entry must either be a text or" + " an XML fragment with the root element 'ClaimedRole' with the namespace 'http://uri.etsi.org/01903/v1.3.2#'.", null); } @Test public void invalidXmlFragmentForCommitmentTypeQualifier() throws Exception { MockEndpoint mock = getMockEndpoint("mock:exception"); mock.expectedMessageCount(1); XmlSignerEndpoint endpoint = getSignerEndpoint(); XAdESSignatureProperties props = (XAdESSignatureProperties) endpoint.getProperties(); props.setCommitmentTypeQualifiers(Collections.singletonList("<CommitmentTypeQualifier>wrong XML fragment<CommitmentTypeQualifier>")); // end tag is not correct sendBody("direct:enveloping", payload, Collections.<String, Object> emptyMap()); assertMockEndpointsSatisfied(); checkThrownException( mock, XmlSignatureException.class, "The XAdES confguration is invalid. The list of the commitment type qualifiers contains the invalid entry '<CommitmentTypeQualifier>wrong XML fragment<CommitmentTypeQualifier>'." + " An entry must either be a text or an XML fragment with the root element 'CommitmentTypeQualifier' with the namespace 'http://uri.etsi.org/01903/v1.3.2#'.", null); } @Test public void invalidXmlFragmentForSigPolicyQualifier() throws Exception { MockEndpoint mock = getMockEndpoint("mock:exception"); mock.expectedMessageCount(1); XmlSignerEndpoint endpoint = getSignerEndpoint(); XAdESSignatureProperties props = (XAdESSignatureProperties) endpoint.getProperties(); props.setSigPolicyQualifiers(Collections.singletonList("<SigPolicyQualifier>wrong XML fragment<SigPolicyQualifier>")); // end tag is not correct sendBody("direct:enveloping", payload, Collections.<String, Object> emptyMap()); assertMockEndpointsSatisfied(); checkThrownException( mock, XmlSignatureException.class, "The XAdES confguration is invalid. The list of the signatue policy qualifiers contains the invalid entry '<SigPolicyQualifier>wrong XML fragment<SigPolicyQualifier>'." + " An entry must either be a text or an XML fragment with the root element 'SigPolicyQualifier' with the namespace 'http://uri.etsi.org/01903/v1.3.2#'.", null); } @Test public void invalidNamespaceForTheRootElementInXmlFragmentForSigPolicyQualifier() throws Exception { MockEndpoint mock = getMockEndpoint("mock:exception"); mock.expectedMessageCount(1); XmlSignerEndpoint endpoint = getSignerEndpoint(); XAdESSignatureProperties props = (XAdESSignatureProperties) endpoint.getProperties(); props.setSigPolicyQualifiers(Collections .singletonList("<SigPolicyQualifier xmlns=\"http://invalid.com\">XML fragment with wrong namespace for root element</SigPolicyQualifier>")); sendBody("direct:enveloping", payload, Collections.<String, Object> emptyMap()); assertMockEndpointsSatisfied(); checkThrownException( mock, XmlSignatureException.class, "The XAdES confguration is invalid. The root element 'SigPolicyQualifier' of the provided XML fragment " + "'<SigPolicyQualifier xmlns=\"http://invalid.com\">XML fragment with wrong namespace for root element</SigPolicyQualifier>' has the invalid namespace 'http://invalid.com'." + " The correct namespace is 'http://uri.etsi.org/01903/v1.3.2#'.", null); } @Test(expected = IllegalArgumentException.class) public void namespaceNull() throws Exception { new XAdESSignatureProperties().setNamespace(null); } @Test(expected = IllegalArgumentException.class) public void signingCertificateURIsNull() throws Exception { new XAdESSignatureProperties().setSigningCertificateURIs(null); } @Test(expected = IllegalArgumentException.class) public void sigPolicyInvalid() throws Exception { new XAdESSignatureProperties().setSignaturePolicy("invalid"); } @Test(expected = IllegalArgumentException.class) public void sigPolicyIdDocumentationReferencesNull() throws Exception { new XAdESSignatureProperties().setSigPolicyIdDocumentationReferences(null); } @Test(expected = IllegalArgumentException.class) public void sigPolicyIdDocumentationReferencesNullEntry() throws Exception { new XAdESSignatureProperties().setSigPolicyIdDocumentationReferences(Collections.<String> singletonList(null)); } @Test(expected = IllegalArgumentException.class) public void sigPolicyIdDocumentationReferencesEmptyEntry() throws Exception { new XAdESSignatureProperties().setSigPolicyIdDocumentationReferences(Collections.<String> singletonList("")); } @Test(expected = IllegalArgumentException.class) public void dataObjectFormatIdentifierDocumentationReferencesNull() throws Exception { new XAdESSignatureProperties().setDataObjectFormatIdentifierDocumentationReferences(null); } @Test(expected = IllegalArgumentException.class) public void dataObjectFormatIdentifierDocumentationReferencesNullEntry() throws Exception { new XAdESSignatureProperties().setDataObjectFormatIdentifierDocumentationReferences(Collections.<String> singletonList(null)); } @Test(expected = IllegalArgumentException.class) public void dataObjectFormatIdentifierDocumentationReferencesEmptyEntry() throws Exception { new XAdESSignatureProperties().setDataObjectFormatIdentifierDocumentationReferences(Collections.<String> singletonList("")); } @Test(expected = IllegalArgumentException.class) public void signerClaimedRolesNull() throws Exception { new XAdESSignatureProperties().setSignerClaimedRoles(null); } @Test(expected = IllegalArgumentException.class) public void signerClaimedRolesNullEntry() throws Exception { new XAdESSignatureProperties().setSignerClaimedRoles(Collections.<String> singletonList(null)); } @Test(expected = IllegalArgumentException.class) public void signerClaimedRolesEmptyEntry() throws Exception { new XAdESSignatureProperties().setSignerClaimedRoles(Collections.<String> singletonList("")); } @Test(expected = IllegalArgumentException.class) public void signerCertifiedRolesNull() throws Exception { new XAdESSignatureProperties().setSignerCertifiedRoles(null); } @Test(expected = IllegalArgumentException.class) public void signerCertifiedRolesNullEntry() throws Exception { new XAdESSignatureProperties().setSignerCertifiedRoles(Collections.<XAdESEncapsulatedPKIData> singletonList(null)); } @Test(expected = IllegalArgumentException.class) public void commitmentTypeIdDocumentationReferencesNull() throws Exception { new XAdESSignatureProperties().setCommitmentTypeIdDocumentationReferences(null); } @Test(expected = IllegalArgumentException.class) public void commitmentTypeIdDocumentationReferencesNullEntry() throws Exception { new XAdESSignatureProperties().setCommitmentTypeIdDocumentationReferences(Collections.<String> singletonList(null)); } @Test(expected = IllegalArgumentException.class) public void commitmentTypeIdDocumentationReferencesEmptyEntry() throws Exception { new XAdESSignatureProperties().setCommitmentTypeIdDocumentationReferences(Collections.<String> singletonList("")); } @Test(expected = IllegalArgumentException.class) public void commitmentTypeQualifiersNull() throws Exception { new XAdESSignatureProperties().setCommitmentTypeQualifiers(null); } @Test(expected = IllegalArgumentException.class) public void commitmentTypeQualifiersNullEntry() throws Exception { new XAdESSignatureProperties().setCommitmentTypeQualifiers(Collections.<String> singletonList(null)); } @Test(expected = IllegalArgumentException.class) public void commitmentTypeQualifiersEmptyEntry() throws Exception { new XAdESSignatureProperties().setCommitmentTypeQualifiers(Collections.<String> singletonList("")); } // private XmlSignerEndpoint getSignerEndpoint() { return (XmlSignerEndpoint) context().getEndpoint( "xmlsecurity:sign:xades?keyAccessor=#keyAccessorDefault&properties=#xmlSignatureProperties"); } private String getPathToSignatureProperties() { return "/ds:Signature/ds:Object/etsi:QualifyingProperties/etsi:SignedProperties/etsi:SignedSignatureProperties/"; } static Map<String, String> getPrefix2NamespaceMap() { Map<String, String> prefix2Namespace = new TreeMap<String, String>(); prefix2Namespace.put("ds", XMLSignature.XMLNS); prefix2Namespace.put("etsi", XAdESSignatureProperties.HTTP_URI_ETSI_ORG_01903_V1_3_2); return prefix2Namespace; } private Document testEnveloping() throws InterruptedException, SAXException, IOException, ParserConfigurationException, Exception { return testEnveloping("direct:enveloping"); } protected Document testEnveloping(String fromUri) throws InterruptedException, SAXException, IOException, ParserConfigurationException, Exception { return testEnveloping(fromUri, Collections.<String, Object> emptyMap()); } protected Document testEnveloping(String fromUri, Map<String, Object> headers) throws InterruptedException, SAXException, IOException, ParserConfigurationException, Exception { MockEndpoint mock = setupMock(); sendBody(fromUri, payload, headers); assertMockEndpointsSatisfied(); Message message = getMessage(mock); byte[] body = message.getBody(byte[].class); Document doc = XmlSignatureHelper.newDocumentBuilder(true).parse(new ByteArrayInputStream(body)); validateAgainstSchema(doc); return doc; } private MockEndpoint setupMock() { MockEndpoint mock = getMockEndpoint("mock:result"); mock.expectedMessageCount(1); return mock; } private static XmlSignatureProperties getXmlSignatureProperties(String alias) throws IOException, GeneralSecurityException { DefaultXAdESSignatureProperties props = new DefaultXAdESSignatureProperties(); props.setKeystore(TestKeystore.getKeyStore()); props.setAlias(alias); props.setAddSigningTime(true); props.setDigestAlgorithmForSigningCertificate(DigestMethod.SHA256); props.setSigningCertificateURIs(Collections.singletonList("http://certuri")); // policy props.setSignaturePolicy(XAdESSignatureProperties.SIG_POLICY_EXPLICIT_ID); props.setSigPolicyId("1.2.840.113549.1.9.16.6.1"); props.setSigPolicyIdQualifier("OIDAsURN"); props.setSigPolicyIdDescription("invoice version 3.1"); props.setSignaturePolicyDigestAlgorithm(DigestMethod.SHA256); props.setSignaturePolicyDigestValue("Ohixl6upD6av8N7pEvDABhEL6hM="); props.setSigPolicyQualifiers(Arrays .asList(new String[] { "<SigPolicyQualifier xmlns=\"http://uri.etsi.org/01903/v1.3.2#\"><SPURI>http://test.com/sig.policy.pdf</SPURI><SPUserNotice><ExplicitText>display text</ExplicitText>" + "</SPUserNotice></SigPolicyQualifier>", "category B" })); props.setSigPolicyIdDocumentationReferences(Arrays.asList(new String[] {"http://test.com/policy.doc.ref1.txt", "http://test.com/policy.doc.ref2.txt" })); // production place props.setSignatureProductionPlaceCity("Munich"); props.setSignatureProductionPlaceCountryName("Germany"); props.setSignatureProductionPlacePostalCode("80331"); props.setSignatureProductionPlaceStateOrProvince("Bavaria"); //role props.setSignerClaimedRoles(Arrays.asList(new String[] {"test", "<a:ClaimedRole xmlns:a=\"http://uri.etsi.org/01903/v1.3.2#\"><TestRole>TestRole</TestRole></a:ClaimedRole>" })); props.setSignerCertifiedRoles(Collections.singletonList(new XAdESEncapsulatedPKIData("Ahixl6upD6av8N7pEvDABhEL6hM=", "http://uri.etsi.org/01903/v1.2.2#DER", "IdCertifiedRole"))); // data object format props.setDataObjectFormatDescription("invoice"); props.setDataObjectFormatMimeType("text/xml"); props.setDataObjectFormatIdentifier("1.2.840.113549.1.9.16.6.2"); props.setDataObjectFormatIdentifierQualifier("OIDAsURN"); props.setDataObjectFormatIdentifierDescription("identifier desc"); props.setDataObjectFormatIdentifierDocumentationReferences(Arrays.asList(new String[] { "http://test.com/dataobject.format.doc.ref1.txt", "http://test.com/dataobject.format.doc.ref2.txt" })); //commitment props.setCommitmentTypeId("1.2.840.113549.1.9.16.6.4"); props.setCommitmentTypeIdQualifier("OIDAsURI"); props.setCommitmentTypeIdDescription("description for commitment type ID"); props.setCommitmentTypeIdDocumentationReferences(Arrays.asList(new String[] {"http://test.com/commitment.ref1.txt", "http://test.com/commitment.ref2.txt" })); props.setCommitmentTypeQualifiers(Arrays.asList(new String[] {"commitment qualifier", "<c:CommitmentTypeQualifier xmlns:c=\"http://uri.etsi.org/01903/v1.3.2#\"><C>c</C></c:CommitmentTypeQualifier>" })); return props; } private void validateAgainstSchema(Document doc) throws Exception { SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); Source schema1 = new StreamSource(new File("target/test-classes/org/apache/camel/component/xmlsecurity/xades/XAdES.xsd")); Source schema2 = new StreamSource(new File( "target/test-classes/org/apache/camel/component/xmlsecurity/xades/xmldsig-core-schema.xsd")); Schema schema = factory.newSchema(new Source[] {schema2, schema1 }); Validator validator = schema.newValidator(); validator.validate(new DOMSource(doc)); } static void checkXpath(Document doc, String xpathString, final Map<String, String> prefix2Namespace, String expectedResult) throws XPathExpressionException { XPathExpression expr = getXpath(xpathString, prefix2Namespace); String result = (String) expr.evaluate(doc, XPathConstants.STRING); assertNotNull("The xpath " + xpathString + " returned a null value", result); if (NOT_EMPTY.equals(expectedResult)) { assertTrue("Not empty result for xpath " + xpathString + " expected", !result.isEmpty()); } else { assertEquals(expectedResult, result); } } private void checkNode(Document doc, String xpathString, final Map<String, String> prefix2Namespace, boolean exists) throws XPathExpressionException { XPathExpression expr = getXpath(xpathString, prefix2Namespace); Object result = expr.evaluate(doc, XPathConstants.NODE); if (exists) { assertNotNull("The xpath " + xpathString + " returned null, expected was a node", result); } else { assertNull("The xpath " + xpathString + " returned a node, expected was none: ", result); } } static XPathExpression getXpath(String xpathString, final Map<String, String> prefix2Namespace) throws XPathExpressionException { 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); return expr; } private 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 static class CertChainXAdESSignatureProperties extends XAdESSignatureProperties { private KeyStore keystore = getKeystore(); private String alias = "bob"; CertChainXAdESSignatureProperties() { setAddSigningTime(false); } @Override protected X509Certificate getSigningCertificate() throws Exception { //NOPMD return null; } @Override protected X509Certificate[] getSigningCertificateChain() throws Exception { //NOPMD Certificate[] certs = keystore.getCertificateChain(alias); X509Certificate[] result = new X509Certificate[certs.length]; int counter = 0; for (Certificate cert : certs) { result[counter] = (X509Certificate) cert; counter++; } return result; } private static KeyStore getKeystore() { try { return TestKeystore.getKeyStore(); } catch (Exception e) { throw new IllegalStateException(e); } } } }