Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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.api;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dom.DOMStructure;
import javax.xml.crypto.dsig.Manifest;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.XMLObject;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureException;
import javax.xml.crypto.dsig.spec.XPathFilterParameterSpec;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.apache.camel.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Maps the XML signature to a camel message. A output node is determined from the XML signature document via a node
* search and then serialized and set to the output message body.
*
* There are three output node search types supported: "Default", "ElementName", and "XPath". All these search types
* support enveloped XML signature or enveloping XML signature.
*
*
*
The "ElementName" search uses the local name and namespace specified in the search value to determine the output
* element from the XML signature document. With the input parameter 'RemoveSignatureElements", you can specify whether
* the signature elements should be removed from the resulting output document. This flag shall be used for enveloped
* XML signatures.
*
The "XPath" search uses an XPath expression to evaluate the output node. In this case the output node can be of
* type Element, TextNode, or Document. With the input parameter 'RemoveSignatureElements", you can specify whether the
* signature elements should be removed from the resulting output document. This flag shall be used for enveloped XML
* signatures.
*
The "Default" search is explained in more detail below.
*
*
* Default Output Node Search:
*
* In the enveloped XML signature case, the XML document without the signature part is returned in the message body.
*
* In the enveloping XML signature case, the message body is determined from a referenced Object element in the
* following way:
*
*
Only same document references are taken into account (URI must start with '#').
*
Also indirect same document references to an object via manifest are taken into account.
*
The resulting number of object references must be 1.
*
The referenced object must contain exactly 1 {@link DOMStructure}.
*
The node of the DOMStructure is serialized to a byte array and added as body to the message.
*
* This does mean that the enveloping XML signature must have either the structure
*
*
*
*
*/
public class DefaultXmlSignature2Message implements XmlSignature2Message {
/**
* Search type 'Default' for determining the output node.
*
*/
public static final String OUTPUT_NODE_SEARCH_TYPE_DEFAULT = "Default";
/**
* Search type 'ElementName' for determining the output element.
*
*/
public static final String OUTPUT_NODE_SEARCH_TYPE_ELEMENT_NAME = "ElementName";
/**
* Search type 'XPath' for determining the output node. Search value must be of type
* {@link XPathFilterParameterSpec}.
*
*/
public static final String OUTPUT_NODE_SEARCH_TYPE_XPATH = "XPath";
private static final Logger LOG = LoggerFactory.getLogger(DefaultXmlSignature2Message.class);
@Override
public void mapToMessage(Input input, Message output) throws Exception {
Node node;
boolean removeSignatureElements = false;
if (OUTPUT_NODE_SEARCH_TYPE_DEFAULT.equals(input.getOutputNodeSearchType())) {
LOG.debug("Searching for output node via default search");
if (isEnveloping(input)) {
node = getNodeForMessageBodyInEnvelopingCase(input);
} else {
// enveloped or detached XML signature --> remove signature element
node = input.getMessageBodyDocument().getDocumentElement();
removeSignatureElements = true;
}
} else if (OUTPUT_NODE_SEARCH_TYPE_ELEMENT_NAME.equals(input.getOutputNodeSearchType())) {
node = getOutputElementViaLocalNameAndNamespace(input);
} else if (OUTPUT_NODE_SEARCH_TYPE_XPATH.equals(input.getOutputNodeSearchType())) {
node = getOutputNodeViaXPath(input);
} else {
throw new XmlSignatureException(
String.format("Wrong configuration: The output node search type %s is not supported.",
input.getOutputNodeSearchType()));
}
LOG.debug("Output node with local name {} and namespace {} found", node.getLocalName(), node.getNamespaceURI());
if (!removeSignatureElements) {
removeSignatureElements = input.getRemoveSignatureElements() != null && input.getRemoveSignatureElements();
}
if (removeSignatureElements) {
removeSignatureElements(node);
}
transformNodeToByteArrayAndSetToOutputMessage(input, output, node);
}
protected void transformNodeToByteArrayAndSetToOutputMessage(Input input, Message output, Node node)
throws Exception {
ByteArrayOutputStream os = new ByteArrayOutputStream();
XmlSignatureHelper.transformToOutputStream(node, os, omitXmlDeclaration(output, input), input.getOutputXmlEncoding());
output.setBody(os.toByteArray());
if (input.getOutputXmlEncoding() != null) {
output.setHeader(XmlSignatureConstants.CHARSET_NAME, input.getOutputXmlEncoding());
}
}
protected Node getOutputNodeViaXPath(Input input) throws Exception {
checkSearchValueNotNull(input);
checkSearchValueOfType(XPathFilterParameterSpec.class, input);
XPathFilterParameterSpec xpathFilter = (XPathFilterParameterSpec) input.getOutputNodeSearch();
XPathExpression expr = XmlSignatureHelper.getXPathExpression(xpathFilter);
NodeList nodes = (NodeList) expr.evaluate(input.getMessageBodyDocument(), XPathConstants.NODESET);
if (nodes == null || nodes.getLength() == 0) {
throw new XmlSignatureException(
String.format(
"Cannot extract root node for the output document from the XML signature document. No node found for XPATH %s as specified in the output node search.",
xpathFilter.getXPath()));
}
if (nodes.getLength() > 1) {
throw new XmlSignatureException(
String.format(
"Cannot extract root node for the output document from the XML signature document. XPATH %s as specified in the output node search results into more than one child.",
xpathFilter.getXPath()));
}
Node result = nodes.item(0);
if (Node.ELEMENT_NODE == result.getNodeType() || Node.TEXT_NODE == result.getNodeType()
|| Node.DOCUMENT_NODE == result.getNodeType()) {
return result;
}
throw new XmlSignatureException(
String.format("Cannot extract root node for the output document from the XML signature document. "
+ "XPATH %s as specified in the output node search results into a node which has the wrong type.",
xpathFilter.getXPath()));
}
protected Node getOutputElementViaLocalNameAndNamespace(Input input) throws Exception {
String search = getNonEmptyStringSearchValue(input);
String namespace;
String localName;
if ('{' == search.charAt(0)) {
// namespace
int index = search.indexOf('}');
if (index < 1) {
throw new XmlSignatureException(
String.format(
"Wrong configuration: Value %s for the output node search %s has wrong format. "
+ "Value must have the form '{}' or '' if no the element has no namespace.",
search, input.getOutputNodeSearchType()));
}
namespace = search.substring(1, index);
if (search.length() < index + 1) {
throw new XmlSignatureException(
String.format(
"Wrong configuration: Value %s for the output node search %s has wrong format. "
+ "Value must have the form '{}' or '' if no the element has no namespace.",
search, input.getOutputNodeSearchType()));
}
localName = search.substring(index + 1);
} else {
namespace = null;
localName = search;
}
NodeList nodeList = input.getMessageBodyDocument().getElementsByTagNameNS(namespace, localName);
if (nodeList.getLength() == 0) {
throw new XmlSignatureException(
String.format(
"Cannot extract root element for the output document from the XML signature document. Element with local name %s and namespace %s does not exist.",
namespace, localName));
}
if (nodeList.getLength() > 1) {
throw new XmlSignatureException(
String.format(
"Cannot extract root element for the output document from the XML signature document. More than one element found with local name %s and namespace %s.",
namespace, localName));
}
return nodeList.item(0);
}
protected String getNonEmptyStringSearchValue(Input input) throws Exception {
checkSearchValueNotNull(input);
checkSearchValueOfType(String.class, input);
String search = (String) input.getOutputNodeSearch();
checkStringSarchValueNotEmpty(search, input.getOutputNodeSearchType());
return search;
}
protected void checkSearchValueOfType(Class> cl, Input input) throws Exception {
if (!cl.isAssignableFrom(input.getOutputNodeSearch().getClass())) {
throw new XMLSignatureException(
String.format(
"Wrong configuration: Search value is of class %s, the output node search %s requires class %s.",
input
.getOutputNodeSearch().getClass().getName(),
input.getOutputNodeSearchType(), cl.getName()));
}
}
protected void checkStringSarchValueNotEmpty(String searchValue, String outputNodeSearchType) throws Exception {
if (searchValue.isEmpty()) {
throw new XMLSignatureException(
String.format("Wrong configuration: Value for output node search %s is empty.",
outputNodeSearchType));
}
}
protected void checkSearchValueNotNull(Input input) throws Exception {
LOG.debug("Searching for output element with search value '{}' and sarch type {}", input.getOutputNodeSearch(),
input.getOutputNodeSearchType());
if (input.getOutputNodeSearch() == null) {
throw new XMLSignatureException(
String.format("Wrong configuration: Value is missing for output node search %s.",
input.getOutputNodeSearchType()));
}
}
protected Node getNodeForMessageBodyInEnvelopingCase(Input input) throws Exception {
Node node;
List relevantReferences = getReferencesForMessageMapping(input);
List relevantObjects = getObjectsForMessageMapping(input);
DOMStructure domStruc = getDomStructureForMessageBody(relevantReferences, relevantObjects);
node = domStruc.getNode();
return node;
}
/**
* Removes the Signature elements from the document.
*/
protected void removeSignatureElements(Node node) {
Document doc = XmlSignatureHelper.getDocument(node);
NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
List nodesToBeRemoved = new ArrayList<>(nl.getLength());
for (int i = 0; i < nl.getLength(); i++) {
// you cannot remove the nodes within this loop, because nl list would change
nodesToBeRemoved.add(nl.item(i));
}
for (Node n : nodesToBeRemoved) {
Node parent = n.getParentNode();
if (parent != null) {
parent.removeChild(n);
}
}
}
/**
* Checks whether the XML document has as root element the signature element.
*
* @param input XML signature input
* @return true if the root element of the xml signature document is the signature element;
* otherwise false
* @throws Exception
*/
protected boolean isEnveloping(Input input) {
Element el = input.getMessageBodyDocument().getDocumentElement();
if ("Signature".equals(el.getLocalName()) && XMLSignature.XMLNS.equals(el.getNamespaceURI())) {
return true;
}
return false;
}
protected Boolean omitXmlDeclaration(Message message, Input input) {
Boolean omitXmlDeclaration = message.getHeader(XmlSignatureConstants.HEADER_OMIT_XML_DECLARATION, Boolean.class);
if (omitXmlDeclaration == null) {
omitXmlDeclaration = input.omitXmlDeclaration();
}
if (omitXmlDeclaration == null) {
omitXmlDeclaration = Boolean.FALSE;
}
return omitXmlDeclaration;
}
/**
* Returns the references whose referenced objects are taken into account for the message body. This message you can
* use to filter the relevant references from the references provided by the input parameter.
*
*
* @param input references and objects
* @return relevant references for the mapping to the camel message
* @throws Exception if an error occurs
*/
protected List getReferencesForMessageMapping(Input input) throws Exception {
return input.getReferences();
}
/**
* Returns the objects which must be taken into account for the mapping to the camel message.
*
* @param input references and objects
* @return relevant objects for the mapping to camel message
* @throws Exception if an error occurs
*/
protected List getObjectsForMessageMapping(Input input) throws Exception {
return input.getObjects();
}
/**
* Returns the DOM structure which is transformed to a byte array and set to the camel message body.
*
* @param relevantReferences input from method {@link #getReferencesForMessageMapping(ReferencesAndObjects)}
* @param relevantObjects input from method {@link #getObjectsForMessageMapping(ReferencesAndObjects)}
* @return dom structure
* @throws Exception if an error occurs
*/
protected DOMStructure getDomStructureForMessageBody(List relevantReferences, List relevantObjects)
throws Exception {
List referencedObjects = getReferencedSameDocumentObjects(relevantReferences, relevantObjects);
if (referencedObjects.isEmpty()) {
throw new XmlSignatureException(
"Unsupported XML signature document: Content object not found in the enveloping XML signature.");
}
if (referencedObjects.size() > 1) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < referencedObjects.size(); i++) {
XMLObject xmlOb = referencedObjects.get(i);
sb.append(xmlOb.getId());
if (i < referencedObjects.size() - 1) {
sb.append(", ");
}
}
throw new XmlSignatureException(
String.format(
"Unsupported XML signature document: More than one content objects found. Object IDs: %s",
sb.toString()));
}
@SuppressWarnings("unchecked")
List structures = referencedObjects.get(0).getContent();
if (structures.isEmpty()) {
throw new XmlSignatureException(
"Unsupported XML signature: XML signature is not enveloping; content not found in XML signature: structure list is empty.");
}
if (structures.size() > 1) {
throw new XmlSignatureException(
"Unsupported XML signature: more than one structure elements in referenced content object.");
}
XMLStructure structure = structures.get(0);
// only dom currently supported
DOMStructure domStruc = (DOMStructure) structure;
return domStruc;
}
protected List getReferencedSameDocumentObjects(
List relevantReferences, List relevantObjects) {
List referencedObjects = new ArrayList<>(1);
for (Reference ref : relevantReferences) {
String refUri = getSameDocumentReferenceUri(ref);
if (refUri == null) {
continue;
}
XMLObject referencedOb = getReferencedObject(relevantObjects, refUri);
if (referencedOb != null) {
referencedObjects.add(referencedOb);
continue;
}
// content could also be indirectly referenced via manifest
addManifestReferencedObjects(relevantObjects, referencedObjects, refUri);
}
return referencedObjects;
}
@SuppressWarnings("unchecked")
protected void addManifestReferencedObjects(
List allObjects, List referencedObjects, String manifestId) {
Manifest manifest = getReferencedManifest(allObjects, manifestId);
if (manifest == null) {
return;
}
for (Reference manifestRef : manifest.getReferences()) {
String manifestRefUri = getSameDocumentReferenceUri(manifestRef);
if (manifestRefUri == null) {
continue;
}
XMLObject manifestReferencedOb = getReferencedObject(allObjects, manifestRefUri);
if (manifestReferencedOb != null) {
referencedObjects.add(manifestReferencedOb);
}
}
}
protected String getSameDocumentReferenceUri(Reference ref) {
String refUri = ref.getURI();
if (refUri == null) {
LOG.warn("Ignoring reference {} which has no URI", ref);
return null;
}
if (!refUri.startsWith("#")) {
LOG.warn("Ignoring non-same document reference {}", refUri);
return null;
}
return refUri.substring(1);
}
protected Manifest getReferencedManifest(List objects, String id) {
for (XMLObject xo : objects) {
@SuppressWarnings("unchecked")
List content = xo.getContent();
for (XMLStructure xs : content) {
if (xs instanceof Manifest) {
Manifest man = (Manifest) xs;
if (id.equals(man.getId())) {
return man;
}
}
}
}
return null;
}
protected XMLObject getReferencedObject(List objects, String id) {
for (XMLObject ob : objects) {
if (id.equals(ob.getId())) {
return ob;
}
}
return null;
}
}