Hi all, I've recently developped a full-featured XPath resolver, which may resolve things like
#xmlns(etsi=http://uri.etsi.org/01903/v1.1.1#) xpointer(id('etsi-data-object-1-20070418142710935-6555091641353629364')/child::etsi:QualifyingProperties/child::etsi:SignedProperties) The attached source file uses XPathFactory, which supposedly is a part of JAVA 1.5. However, feel free to incorporate this in xml-security, mabe rewritten in a fashion suitable for jdk 1.3 et al. Please note, that the resolver only works for documents, which have fully been schema-parsed, because otherwise Document.getElemntByID is not functional. Regards, Wolfgang
/*********************************************************** * $Id: XPointerResourceResolver.java 52 2007-04-07 19:45:06Z wglas $ * * Tapestry support for the austrian security card layer. * * Copyright (C) 2007 ev-i Informationstechnologie GmbH * * Licensed 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. * * Created: Apr 6, 2007 * * Author: wglas * ***********************************************************/ package org.clazzes.tapestry.sl.test.dsig; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.HashSet; import java.util.Set; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xml.security.signature.XMLSignatureInput; import org.apache.xml.security.utils.resolver.ResourceResolverException; import org.apache.xml.security.utils.resolver.ResourceResolverSpi; import org.w3c.dom.Attr; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * An implementation of a resource resolver, which evaluates xpointer expressions. * * @author wglas */ public class XPointerResourceResolver extends ResourceResolverSpi { private static Log log = LogFactory.getLog(XPointerResourceResolver.class); private static final String XP_OPEN = "xpointer("; private static final String XNS_OPEN = "xmlns("; private XPathFactory xPathFactory; private Node baseNode; public XPointerResourceResolver(Node baseNode) { this.xPathFactory = XPathFactory.newInstance(); this.baseNode = baseNode; } /* (non-Javadoc) * @see org.apache.xml.security.utils.resolver.ResourceResolverSpi#engineCanResolve(org.w3c.dom.Attr, java.lang.String) */ @Override public boolean engineCanResolve(Attr uri, String BaseURI) { String v = uri.getNodeValue(); if (v.charAt(0) != '#') return false; String xpURI; try { xpURI = URLDecoder.decode(v, "utf-8"); } catch (UnsupportedEncodingException e) { log.warn("utf-8 not a valid encoding",e); return false; } String parts[] = xpURI.substring(1).split("\\s"); // plain ID reference. if (parts.length == 1 && !parts[0].startsWith(XNS_OPEN)) return true; int i=0; for (;i<parts.length-1;++i) if (!parts[i].endsWith(")") || !parts[i].startsWith(XNS_OPEN)) return false; if (!parts[i].endsWith(")") || !parts[i].startsWith(XP_OPEN)) return false; log.debug("xpURI="+xpURI); log.debug("BaseURI="+BaseURI); return true; } /* (non-Javadoc) * @see org.apache.xml.security.utils.resolver.ResourceResolverSpi#engineResolve(org.w3c.dom.Attr, java.lang.String) */ @Override public XMLSignatureInput engineResolve(Attr uri, String BaseURI) throws ResourceResolverException { String v = uri.getNodeValue(); if (v.charAt(0) != '#') return null; String xpURI; try { xpURI = URLDecoder.decode(v, "utf-8"); } catch (UnsupportedEncodingException e) { log.warn("utf-8 not a valid encoding",e); return null; } String parts[] = xpURI.substring(1).split("\\s"); int i=0; DSigNamespaceContext nsContext=null; if (parts.length > 1) { nsContext= new DSigNamespaceContext(); for (;i<parts.length-1;++i) { if (!parts[i].endsWith(")") || !parts[i].startsWith(XNS_OPEN)) return null; String mapping = parts[i].substring(XNS_OPEN.length(),parts[i].length()-1); int pos = mapping.indexOf('='); if (pos <= 0 || pos >= mapping.length()-1) throw new ResourceResolverException("malformed namespace part of XPointer expression",uri,BaseURI); nsContext.addNamespace(mapping.substring(0,pos), mapping.substring(pos+1)); } } try { Node node = null; NodeList nodes=null; // plain ID reference. if (i==0 && !parts[i].startsWith(XP_OPEN)) { node = this.baseNode.getOwnerDocument().getElementById(parts[i]); } else { if (!parts[i].endsWith(")") || !parts[i].startsWith(XP_OPEN)) return null; XPath xp = this.xPathFactory.newXPath(); if (nsContext != null) xp.setNamespaceContext(nsContext); nodes = (NodeList)xp.evaluate(parts[i].substring(XP_OPEN.length(),parts[i].length()-1), this.baseNode, XPathConstants.NODESET); if (nodes.getLength() == 0) return null; if (nodes.getLength() == 1) node = nodes.item(0); } XMLSignatureInput result = null; if (node != null) { result = new XMLSignatureInput(node); } else if (nodes !=null) { Set<Node> nodeSet = new HashSet<Node>(nodes.getLength()); for (int j=0 ; j<nodes.getLength(); ++j) { nodeSet.add(nodes.item(j)); } result = new XMLSignatureInput(nodeSet); } else return null; result.setMIMEType("text/xml"); result.setExcludeComments(true); result.setSourceURI((BaseURI != null) ? BaseURI.concat(v):v); return result; } catch (XPathExpressionException e) { throw new ResourceResolverException("malformed XPath inside XPointer expression",e,uri,BaseURI); } } }