You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fx-dev@ws.apache.org by we...@apache.org on 2006/02/16 13:22:33 UTC

svn commit: r378232 - in /webservices/wss4j/trunk/src/org/apache/ws/security: WSSecurityEngineResult.java errors.properties processor/SignatureProcessor.java util/WSSecurityUtil.java

Author: werner
Date: Thu Feb 16 04:22:27 2006
New Revision: 378232

URL: http://svn.apache.org/viewcvs?rev=378232&view=rev
Log:
JIRA WSS-33 Patch applied. This patch helps to close a
security vulnerability. See additional comments and example
code for WSS-33.
Patch submitted from "University of Southampton IT Innovation Centre"

Modified:
    webservices/wss4j/trunk/src/org/apache/ws/security/WSSecurityEngineResult.java
    webservices/wss4j/trunk/src/org/apache/ws/security/errors.properties
    webservices/wss4j/trunk/src/org/apache/ws/security/processor/SignatureProcessor.java
    webservices/wss4j/trunk/src/org/apache/ws/security/util/WSSecurityUtil.java

Modified: webservices/wss4j/trunk/src/org/apache/ws/security/WSSecurityEngineResult.java
URL: http://svn.apache.org/viewcvs/webservices/wss4j/trunk/src/org/apache/ws/security/WSSecurityEngineResult.java?rev=378232&r1=378231&r2=378232&view=diff
==============================================================================
--- webservices/wss4j/trunk/src/org/apache/ws/security/WSSecurityEngineResult.java (original)
+++ webservices/wss4j/trunk/src/org/apache/ws/security/WSSecurityEngineResult.java Thu Feb 16 04:22:27 2006
@@ -1,5 +1,6 @@
 /*
- * Copyright  2003-2004 The Apache Software Foundation.
+ * Copyright  2003-2006 The Apache Software Foundation, or their licensors, as
+ * appropriate.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -23,7 +24,7 @@
 
 import java.security.Principal;
 import java.security.cert.X509Certificate;
-import java.util.Vector;
+import java.util.Set;
 
 /**
  * @author Werner Dittmann (Werner.Dittmann@t-online.de)
@@ -35,7 +36,7 @@
     private X509Certificate cert;
     private SAMLAssertion assertion;
     private Timestamp timestamp;
-    private Vector signedElementQnames;
+    private Set signedElements;
     private byte[] signatureValue = null;
     private SignatureConfirmation sigConf = null;
 
@@ -47,11 +48,11 @@
     }
 
     public WSSecurityEngineResult(int act, Principal princ,
-            X509Certificate certificate, Vector elemQnames, byte[] sv) {
+            X509Certificate certificate, Set elements, byte[] sv) {
         principal = princ;
         action = act;
         cert = certificate;
-        signedElementQnames = elemQnames;
+        signedElements = elements;
         signatureValue = sv;
     }
 
@@ -103,10 +104,10 @@
     }
 
     /**
-     * @return Returns the signedElementQnames.
+     * @return Returns the signedElements.
      */
-    public Vector getSignedElementQnames() {
-        return signedElementQnames;
+    public Set getSignedElements() {
+        return signedElements;
     }
 
     /**

Modified: webservices/wss4j/trunk/src/org/apache/ws/security/errors.properties
URL: http://svn.apache.org/viewcvs/webservices/wss4j/trunk/src/org/apache/ws/security/errors.properties?rev=378232&r1=378231&r2=378232&view=diff
==============================================================================
--- webservices/wss4j/trunk/src/org/apache/ws/security/errors.properties (original)
+++ webservices/wss4j/trunk/src/org/apache/ws/security/errors.properties Thu Feb 16 04:22:27 2006
@@ -70,4 +70,8 @@
 decoding.general = Error while decoding
 
 unknownAction=Unknown Action {0}
-unableToLoadClass=Unable to load class {0}
\ No newline at end of file
+unableToLoadClass=Unable to load class {0}
+
+requiredElementNoID=Element {0} is not signed; it does not have a wsu:Id attribute
+noSignResult=No SIGN result in WSS4J result vector
+requiredElementNotSigned=Element {0} is not included in the signature

Modified: webservices/wss4j/trunk/src/org/apache/ws/security/processor/SignatureProcessor.java
URL: http://svn.apache.org/viewcvs/webservices/wss4j/trunk/src/org/apache/ws/security/processor/SignatureProcessor.java?rev=378232&r1=378231&r2=378232&view=diff
==============================================================================
--- webservices/wss4j/trunk/src/org/apache/ws/security/processor/SignatureProcessor.java (original)
+++ webservices/wss4j/trunk/src/org/apache/ws/security/processor/SignatureProcessor.java Thu Feb 16 04:22:27 2006
@@ -1,5 +1,6 @@
 /*
- * Copyright  2003-2004 The Apache Software Foundation.
+ * Copyright  2003-2006 The Apache Software Foundation, or their licensors, as
+ * appropriate.
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -55,6 +56,8 @@
 import java.security.cert.CertificateNotYetValidException;
 import java.security.cert.X509Certificate;
 import java.util.Vector;
+import java.util.HashSet;
+import java.util.Set;
 
 public class SignatureProcessor implements Processor {
     private static Log log = LogFactory.getLog(SignatureProcessor.class.getName());
@@ -69,12 +72,12 @@
         }
         WSDocInfoStore.store(wsDocInfo);
         X509Certificate[] returnCert = new X509Certificate[1];
-        Vector returnQname[] = new Vector[1];
+        Set returnElements = new HashSet();
         byte[][] signatureValue = new byte[1][];
         Principal lastPrincipalFound = null;
         try {
             lastPrincipalFound = verifyXMLSignature((Element) elem,
-                    crypto, returnCert, returnQname, signatureValue);
+                    crypto, returnCert, returnElements, signatureValue);
         } catch (WSSecurityException ex) {
             throw ex;
         } finally {
@@ -83,12 +86,12 @@
         if (lastPrincipalFound instanceof WSUsernameTokenPrincipal) {
             returnResults.add(0, new WSSecurityEngineResult(
                     WSConstants.UT_SIGN, lastPrincipalFound, null,
-                    returnQname[0], signatureValue[0]));
+                    returnElements, signatureValue[0]));
 
         } else {
             returnResults.add(0, new WSSecurityEngineResult(
                     WSConstants.SIGN, lastPrincipalFound,
-                    returnCert[0], returnQname[0], signatureValue[0]));
+                    returnCert[0], returnElements, signatureValue[0]));
         }
         signatureId = elem.getAttributeNS(null, "Id");
     }
@@ -123,9 +126,8 @@
      * @param returnCert  verifyXMLSignature stores the certificate in the first
      *                    entry of this array. Ther caller may then further validate
      *                    the certificate
-     * @param returnQname verifyXMLSignature store the Qnames of all signed elements
-     *                    in this Vector ordered according the sequence in the Signature
-     *                    header.
+     * @param returnElements verifyXMLSignature adds the wsu:ID attribute values for
+     * 			     the signed elements to this Set
      * @return the subject principal of the validated X509 certificate (the
      *         authenticated subject). The calling function may use this
      *         principal for further authentication or authorization.
@@ -134,7 +136,7 @@
     protected Principal verifyXMLSignature(Element elem,
                                            Crypto crypto,
                                            X509Certificate[] returnCert,
-                                           Vector[] returnQname,
+                                           Set returnElements,
                                            byte[][] signatureValue)
             throws WSSecurityException {
         if (log.isDebugEnabled()) {
@@ -298,11 +300,9 @@
                         throw new WSSecurityException(
                                 WSSecurityException.FAILED_CHECK);
                     }
-                    QName qn = new QName(se.getNamespaceURI(), se
-                            .getLocalName());
-                    qvec.add(qn);
+                    returnElements.add(WSSecurityUtil.getIDfromReference(uri));                    
                 }
-                returnQname[0] = qvec;
+                
                 if (certs != null) {
                     returnCert[0] = certs[0];
                     return certs[0].getSubjectDN();

Modified: webservices/wss4j/trunk/src/org/apache/ws/security/util/WSSecurityUtil.java
URL: http://svn.apache.org/viewcvs/webservices/wss4j/trunk/src/org/apache/ws/security/util/WSSecurityUtil.java?rev=378232&r1=378231&r2=378232&view=diff
==============================================================================
--- webservices/wss4j/trunk/src/org/apache/ws/security/util/WSSecurityUtil.java (original)
+++ webservices/wss4j/trunk/src/org/apache/ws/security/util/WSSecurityUtil.java Thu Feb 16 04:22:27 2006
@@ -1,23 +1,27 @@
-
 /*
-* Copyright  2003-2004 The Apache Software Foundation.
-*
-*  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.
-*
-*/
+ * Copyright  2003-2006 The Apache Software Foundation, or their licensors, as
+ * appropriate.
+ *
+ *  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.
+ *
+ */
 
 package org.apache.ws.security.util;
 
+import java.util.Set;
+import org.apache.ws.security.handler.WSHandlerResult;
+import java.util.Iterator;
+import java.security.cert.X509Certificate;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.ws.security.SOAP11Constants;
@@ -51,34 +55,33 @@
 import java.util.Vector;
 
 /**
- * WS-Security Utility methods.
- * <p/>
- *
+ * WS-Security Utility methods. <p/>
+ * 
  * @author Davanum Srinivas (dims@yahoo.com).
  */
 public class WSSecurityUtil {
     private static Log log = LogFactory.getLog(WSSecurityUtil.class);
+
     private static boolean doDebug = false;
 
     static {
         doDebug = log.isDebugEnabled();
     }
 
-
     /**
-     * Returns the first WS-Security header element for a given actor.
-     * Only one WS-Security header is allowed for an actor.
-     *
+     * Returns the first WS-Security header element for a given actor. Only one
+     * WS-Security header is allowed for an actor.
+     * 
      * @param doc
      * @param actor
-     * @return the <code>wsse:Security</code> element or
-     *         <code>null</code> if not such element found
+     * @return the <code>wsse:Security</code> element or <code>null</code>
+     *         if not such element found
      */
-    public static Element getSecurityHeader(Document doc, String actor, SOAPConstants sc) {
-        Element soapHeaderElement =
-            (Element) getDirectChild(doc.getFirstChild(),
-                                     sc.getHeaderQName().getLocalPart(),
-                                     sc.getEnvelopeURI());
+    public static Element getSecurityHeader(Document doc, String actor,
+            SOAPConstants sc) {
+        Element soapHeaderElement = (Element) getDirectChild(doc
+                .getFirstChild(), sc.getHeaderQName().getLocalPart(), sc
+                .getEnvelopeURI());
 
         if (soapHeaderElement == null) { // no SOAP header at all
             return null;
@@ -87,7 +90,8 @@
         // get all wsse:Security nodes
         NodeList list = null;
         int len = 0;
-        list = soapHeaderElement.getElementsByTagNameNS(WSConstants.WSSE_NS, WSConstants.WSSE_LN);
+        list = soapHeaderElement.getElementsByTagNameNS(WSConstants.WSSE_NS,
+                WSConstants.WSSE_LN);
         if (list == null) {
             return null;
         } else {
@@ -98,7 +102,8 @@
         String hActor;
         for (int i = 0; i < len; i++) {
             elem = (Element) list.item(i);
-            attr = elem.getAttributeNodeNS(sc.getEnvelopeURI(), sc.getRoleAttributeQName().getLocalPart());
+            attr = elem.getAttributeNodeNS(sc.getEnvelopeURI(), sc
+                    .getRoleAttributeQName().getLocalPart());
             hActor = (attr != null) ? attr.getValue() : null;
             if (WSSecurityUtil.isActorEqual(actor, hActor)) {
                 return elem;
@@ -108,19 +113,18 @@
     }
 
     /**
-     * Compares two actor strings and returns true if these are equal.
-     * Takes care of the null length strings and uses ignore case.
-     *
+     * Compares two actor strings and returns true if these are equal. Takes
+     * care of the null length strings and uses ignore case.
+     * 
      * @param actor
      * @param hActor
      * @return TODO
      */
     public static boolean isActorEqual(String actor, String hActor) {
-        if ((((hActor == null) || (hActor.length() == 0))
-                && ((actor == null) || (actor.length() == 0)))
-                || ((hActor != null)
-                && (actor != null)
-                && hActor.equalsIgnoreCase(actor))) {
+        if ((((hActor == null) || (hActor.length() == 0)) && ((actor == null) || (actor
+                .length() == 0)))
+                || ((hActor != null) && (actor != null) && hActor
+                        .equalsIgnoreCase(actor))) {
             return true;
         } else {
             return false;
@@ -128,22 +132,22 @@
     }
 
     /**
-     * Gets a direct child with specified localname and namespace.
-     * <p/>
-     *
-     * @param fNode     the node where to start the search
-     * @param localName local name of the child to get
-     * @param namespace the namespace of the child to get
+     * Gets a direct child with specified localname and namespace. <p/>
+     * 
+     * @param fNode
+     *            the node where to start the search
+     * @param localName
+     *            local name of the child to get
+     * @param namespace
+     *            the namespace of the child to get
      * @return the node or <code>null</code> if not such node found
      */
-    public static Node getDirectChild(Node fNode,
-                                      String localName,
-                                      String namespace) {
-        for (Node currentChild = fNode.getFirstChild();
-             currentChild != null;
-             currentChild = currentChild.getNextSibling()) {
-            if (localName.equals(currentChild.getLocalName()) &&
-                    namespace.equals(currentChild.getNamespaceURI())) {
+    public static Node getDirectChild(Node fNode, String localName,
+            String namespace) {
+        for (Node currentChild = fNode.getFirstChild(); currentChild != null; currentChild = currentChild
+                .getNextSibling()) {
+            if (localName.equals(currentChild.getLocalName())
+                    && namespace.equals(currentChild.getNamespaceURI())) {
                 return currentChild;
             }
         }
@@ -151,42 +155,39 @@
     }
 
     /**
-     * return the first soap "Body" element.
-     * <p/>
-     *
+     * return the first soap "Body" element. <p/>
+     * 
      * @param doc
      * @return the body element or <code>null</code> if document does not
      *         contain a SOAP body
      */
     public static Element findBodyElement(Document doc, SOAPConstants sc) {
-        Element soapBodyElement =
-                (Element) WSSecurityUtil.getDirectChild(doc.getFirstChild(),
-                        sc.getBodyQName().getLocalPart(),
-                        sc.getEnvelopeURI());
+        Element soapBodyElement = (Element) WSSecurityUtil.getDirectChild(doc
+                .getFirstChild(), sc.getBodyQName().getLocalPart(), sc
+                .getEnvelopeURI());
         return soapBodyElement;
     }
 
     /**
      * Returns the first element that matches <code>name</code> and
-     * <code>namespace</code>.
-     * <p/>
-     * This is a replacement for a XPath lookup <code>//name</code> with
-     * the given namespace. It's somewhat faster than XPath, and we do
-     * not deal with prefixes, just with the real namespace URI
-     *
-     * @param startNode Where to start the search
-     * @param name      Local name of the element
-     * @param namespace Namespace URI of the element
+     * <code>namespace</code>. <p/> This is a replacement for a XPath lookup
+     * <code>//name</code> with the given namespace. It's somewhat faster than
+     * XPath, and we do not deal with prefixes, just with the real namespace URI
+     * 
+     * @param startNode
+     *            Where to start the search
+     * @param name
+     *            Local name of the element
+     * @param namespace
+     *            Namespace URI of the element
      * @return The found element or <code>null</code>
      */
-    public static Node findElement(Node startNode,
-                                   String name,
-                                   String namespace) {
+    public static Node findElement(Node startNode, String name, String namespace) {
 
         /*
-        * Replace the formely recursive implementation
-        * with a depth-first-loop lookup
-        */
+         * Replace the formely recursive implementation with a depth-first-loop
+         * lookup
+         */
         if (startNode == null) {
             return null;
         }
@@ -230,26 +231,32 @@
     }
 
     /**
-     * Returns the first element that containes an Id with value
-     * <code>uri</code> and <code>namespace</code>.
-     * <p/>
-     * This is a replacement for a XPath Id lookup with
-     * the given namespace. It's somewhat faster than XPath, and we do
-     * not deal with prefixes, just with the real namespace URI
-     *
-     * @param startNode Where to start the search
-     * @param value     Value of the Id attribute
-     * @param namespace Namespace URI of the Id
-     * @return The found element or <code>null</code>
-     */
-    public static Element findElementById(Node startNode,
-                                          String value,
-                                          String namespace) {
+     * Returns the single element that containes an Id with value
+     * <code>uri</code> and <code>namespace</code>. <p/> This is a
+     * replacement for a XPath Id lookup with the given namespace. It's somewhat
+     * faster than XPath, and we do not deal with prefixes, just with the real
+     * namespace URI
+     * 
+     * If there are multiple elements, we log a warning and return null as this
+     * can be used to get around the signature checking.
+     * 
+     * @param startNode
+     *            Where to start the search
+     * @param value
+     *            Value of the Id attribute
+     * @param namespace
+     *            Namespace URI of the Id
+     * @return The found element if there was exactly one match, or
+     *         <code>null</code> otherwise
+     */
+    public static Element findElementById(Node startNode, String value,
+            String namespace) {
+        Element foundElement = null;
 
         /*
-        * Replace the formely recursive implementation with a depth-first-loop
-        * lookup
-        */
+         * Replace the formely recursive implementation with a depth-first-loop
+         * lookup
+         */
         if (startNode == null) {
             return null;
         }
@@ -260,9 +267,16 @@
             // start node processing at this point
             if (startNode.getNodeType() == Node.ELEMENT_NODE) {
                 Element se = (Element) startNode;
-                if (se.hasAttributeNS(namespace, "Id") &&
-                        value.equals(se.getAttributeNS(namespace, "Id"))) {
-                    return se;
+                if (se.hasAttributeNS(namespace, "Id")
+                        && value.equals(se.getAttributeNS(namespace, "Id"))) {
+                    if (foundElement == null) {
+                        foundElement = se; // Continue searching to find
+                        // duplicates
+                    } else {
+                        log
+                                .warn("Multiple elements with the same 'Id' attribute value!");
+                        return null;
+                    }
                 }
             }
 
@@ -279,37 +293,38 @@
             while (startNode == null) {
                 processedNode = processedNode.getParentNode();
                 if (processedNode == startParent) {
-                    return null;
+                    return foundElement;
                 }
                 // close parent node processing (processed node now)
                 startNode = processedNode.getNextSibling();
             }
         }
-        return null;
+        return foundElement;
     }
 
     /**
-     * set the namespace if it is not set already.
-     * <p/>
-     *
+     * set the namespace if it is not set already. <p/>
+     * 
      * @param element
      * @param namespace
      * @param prefix
      * @return TODO
      */
-    public static String setNamespace(Element element,
-                                      String namespace,
-                                      String prefix) {
+    public static String setNamespace(Element element, String namespace,
+            String prefix) {
         String pre = getPrefixNS(namespace, element);
         if (pre != null) {
             return pre;
         }
-        element.setAttributeNS(WSConstants.XMLNS_NS,
-                "xmlns:" + prefix, namespace);
+        element.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:" + prefix,
+                namespace);
         return prefix;
     }
 
-    /* ** The following methods were copied over from aixs.utils.XMLUtils and adapted */
+    /*
+     * ** The following methods were copied over from aixs.utils.XMLUtils and
+     * adapted
+     */
 
     public static String getPrefixNS(String uri, Node e) {
         while (e != null && (e.getNodeType() == Element.ELEMENT_NODE)) {
@@ -317,8 +332,8 @@
             for (int n = 0; n < attrs.getLength(); n++) {
                 Attr a = (Attr) attrs.item(n);
                 String name;
-                if ((name = a.getName()).startsWith("xmlns:") &&
-                        a.getNodeValue().equals(uri)) {
+                if ((name = a.getName()).startsWith("xmlns:")
+                        && a.getNodeValue().equals(uri)) {
                     return name.substring(6);
                 }
             }
@@ -336,16 +351,17 @@
                 attr = ((Element) e).getAttributeNodeNS(WSConstants.XMLNS_NS,
                         prefix);
             }
-            if (attr != null) return attr.getValue();
+            if (attr != null)
+                return attr.getValue();
             e = e.getParentNode();
         }
         return null;
     }
 
     /**
-     * Return a QName when passed a string like "foo:bar" by mapping
-     * the "foo" prefix to a namespace in the context of the given Node.
-     *
+     * Return a QName when passed a string like "foo:bar" by mapping the "foo"
+     * prefix to a namespace in the context of the given Node.
+     * 
      * @return a QName generated from the given string representation
      */
     public static QName getQNameFromString(String str, Node e) {
@@ -353,19 +369,18 @@
     }
 
     /**
-     * Return a QName when passed a string like "foo:bar" by mapping
-     * the "foo" prefix to a namespace in the context of the given Node.
-     * If default namespace is found it is returned as part of the QName.
-     *
+     * Return a QName when passed a string like "foo:bar" by mapping the "foo"
+     * prefix to a namespace in the context of the given Node. If default
+     * namespace is found it is returned as part of the QName.
+     * 
      * @return a QName generated from the given string representation
      */
     public static QName getFullQNameFromString(String str, Node e) {
         return getQNameFromString(str, e, true);
     }
 
-    private static QName getQNameFromString(String str,
-                                            Node e,
-                                            boolean defaultNS) {
+    private static QName getQNameFromString(String str, Node e,
+            boolean defaultNS) {
         if (str == null || e == null)
             return null;
         int idx = str.indexOf(':');
@@ -386,8 +401,8 @@
     }
 
     /**
-     * Return a string for a particular QName, mapping a new prefix
-     * if necessary.
+     * Return a string for a particular QName, mapping a new prefix if
+     * necessary.
      */
     public static String getStringForQName(QName qname, Element e) {
         String uri = qname.getNamespaceURI();
@@ -399,19 +414,20 @@
                 i++;
                 prefix = "ns" + i;
             }
-            e.setAttributeNS(WSConstants.XMLNS_NS,
-                    "xmlns:" + prefix, uri);
+            e.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:" + prefix, uri);
         }
         return prefix + ":" + qname.getLocalPart();
     }
+
     /* ** up to here */
 
-   /**
-     * Search for an element given its wsu:id.
-     * <p/>
-     *
-     * @param doc the DOM document (SOAP request) 
-     * @param id the Id of the element
+    /**
+     * Search for an element given its wsu:id. <p/>
+     * 
+     * @param doc
+     *            the DOM document (SOAP request)
+     * @param id
+     *            the Id of the element
      * @return the found element or null if no element with the Id exists
      */
     public static Element getElementByWsuId(Document doc, String id) {
@@ -419,20 +435,33 @@
         if (id == null) {
             return null;
         }
-        id = id.trim();
+        id = getIDfromReference(id);
+        return WSSecurityUtil.findElementById(doc.getDocumentElement(), id,
+                WSConstants.WSU_NS);
+    }
+
+    /**
+     * Turn a reference (eg "#5") into an ID (eg "5").
+     * 
+     * @param ref
+     * @return ref trimmed and with the leading "#" removed, or null if not
+     *         correctly formed
+     */
+    public static String getIDfromReference(String ref) {
+        String id = ref.trim();
         if ((id.length() == 0) || (id.charAt(0) != '#')) {
             return null;
         }
-        id = id.substring(1);
-        return WSSecurityUtil.findElementById(doc.getDocumentElement(), id, WSConstants.WSU_NS);
+        return id.substring(1);
     }
 
     /**
-     * Search for an element given its generic id.
-     * <p/>
-     *
-     * @param doc the DOM document (SOAP request) 
-     * @param id the Id of the element
+     * Search for an element given its generic id. <p/>
+     * 
+     * @param doc
+     *            the DOM document (SOAP request)
+     * @param id
+     *            the Id of the element
      * @return the found element or null if no element with the Id exists
      */
     public static Element getElementByGenId(Document doc, String id) {
@@ -444,23 +473,25 @@
             return null;
         }
         id = id.substring(1);
-        return WSSecurityUtil.findElementById(doc.getDocumentElement(), id, null);
+        return WSSecurityUtil.findElementById(doc.getDocumentElement(), id,
+                null);
     }
 
     /**
-     * Create a BinarySecurityToken element
-     * <p/>
-     *
-     * @param doc the DOM document (SOAP request) 
-     * @param wsuIdVal the value for the wsu:Id
+     * Create a BinarySecurityToken element <p/>
+     * 
+     * @param doc
+     *            the DOM document (SOAP request)
+     * @param wsuIdVal
+     *            the value for the wsu:Id
      * @return then BST element (DOM element)
      */
     public static Element createBinarySecurityToken(Document doc,
-                                                    String wsuIdVal) {
+            String wsuIdVal) {
         Element retVal = doc.createElementNS(WSConstants.WSSE_NS,
                 "wsse:BinarySecurityToken");
-        retVal.setAttributeNS(WSConstants.XMLNS_NS,
-                "xmlns:wsu", WSConstants.WSU_NS);
+        retVal.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:wsu",
+                WSConstants.WSU_NS);
         retVal.setAttributeNS(WSConstants.WSU_NS, "wsu:Id", wsuIdVal);
         retVal.setAttributeNS(null, "ValueType", X509Security.getType());
         retVal.setAttributeNS(null, "EncodingType",
@@ -469,15 +500,16 @@
     }
 
     /**
-     * create a new element in the same namespace
-     * <p/>
-     *
-     * @param parent for the new element
-     * @param localName of the new element
+     * create a new element in the same namespace <p/>
+     * 
+     * @param parent
+     *            for the new element
+     * @param localName
+     *            of the new element
      * @return the new element
      */
     private static Element createElementInSameNamespace(Element parent,
-                                                        String localName) {
+            String localName) {
         String prefix = parent.getPrefix();
         if (prefix == null) {
             prefix = "";
@@ -488,25 +520,26 @@
     }
 
     /**
-     * find a child element with given namespace and local name
-     * <p/>
-     *
-     * @param parent the node to start the search
-     * @param namespaceUri of the element
-     * @param localName of the eleme
+     * find a child element with given namespace and local name <p/>
+     * 
+     * @param parent
+     *            the node to start the search
+     * @param namespaceUri
+     *            of the element
+     * @param localName
+     *            of the eleme
      * @return the found element or null if the element does not exist
      */
     private static Element findChildElement(Element parent,
-                                            String namespaceUri,
-                                            String localName) {
+            String namespaceUri, String localName) {
         NodeList children = parent.getChildNodes();
         int len = children.getLength();
         for (int i = 0; i < len; i++) {
             Node child = children.item(i);
             if (child.getNodeType() == Node.ELEMENT_NODE) {
                 Element elementChild = (Element) child;
-                if (namespaceUri.equals(elementChild.getNamespaceURI()) &&
-                        localName.equals(elementChild.getLocalName())) {
+                if (namespaceUri.equals(elementChild.getNamespaceURI())
+                        && localName.equals(elementChild.getLocalName())) {
                     return elementChild;
                 }
             }
@@ -515,17 +548,18 @@
     }
 
     /**
-     * append a child element
-     * <p/>
-     *
-     * @param doc the DOM document (SOAP request)
-     * @param parent element of this child element
-     * @param child the element to append
+     * append a child element <p/>
+     * 
+     * @param doc
+     *            the DOM document (SOAP request)
+     * @param parent
+     *            element of this child element
+     * @param child
+     *            the element to append
      * @return the child element
      */
-    public static Element appendChildElement(Document doc,
-                                             Element parent,
-                                             Element child) {
+    public static Element appendChildElement(Document doc, Element parent,
+            Element child) {
         Node whitespaceText = doc.createTextNode("\n");
         parent.appendChild(whitespaceText);
         parent.appendChild(child);
@@ -533,19 +567,20 @@
     }
 
     /**
-     * prepend a child element
-     * <p/>
-     *
-     * @param doc the DOM document (SOAP request)
-     * @param parent element of this child element
-     * @param child the element to append
-     * @param addWhitespace if true prepend a newline before child
+     * prepend a child element <p/>
+     * 
+     * @param doc
+     *            the DOM document (SOAP request)
+     * @param parent
+     *            element of this child element
+     * @param child
+     *            the element to append
+     * @param addWhitespace
+     *            if true prepend a newline before child
      * @return the child element
      */
-    public static Element prependChildElement(Document doc,
-                                              Element parent,
-                                              Element child,
-                                              boolean addWhitespace) {
+    public static Element prependChildElement(Document doc, Element parent,
+            Element child, boolean addWhitespace) {
         Node firstChild = parent.getFirstChild();
         if (firstChild == null) {
             parent.appendChild(child);
@@ -560,68 +595,76 @@
     }
 
     /**
-     * find the first ws-security header block
-     * <p/>
-     *
-     * @param doc the DOM document (SOAP request)
-     * @param envelope the SOAP envelope
-     * @param doCreate if true create a new WSS header block if none exists
+     * find the first ws-security header block <p/>
+     * 
+     * @param doc
+     *            the DOM document (SOAP request)
+     * @param envelope
+     *            the SOAP envelope
+     * @param doCreate
+     *            if true create a new WSS header block if none exists
      * @return the WSS header or null if none found and doCreate is false
      */
-    public static Element findWsseSecurityHeaderBlock(Document doc, Element envelope, boolean doCreate) {
+    public static Element findWsseSecurityHeaderBlock(Document doc,
+            Element envelope, boolean doCreate) {
         return findWsseSecurityHeaderBlock(doc, envelope, null, doCreate);
     }
 
     /**
-     * find a ws-security header block for a given actor
-     * <p/>
-     *
-     * @param doc the DOM document (SOAP request)
-     * @param envelope the SOAP envelope
-     * @param actor the acttoer (role) name of the WSS header
-     * @param doCreate if true create a new WSS header block if none exists
+     * find a ws-security header block for a given actor <p/>
+     * 
+     * @param doc
+     *            the DOM document (SOAP request)
+     * @param envelope
+     *            the SOAP envelope
+     * @param actor
+     *            the acttoer (role) name of the WSS header
+     * @param doCreate
+     *            if true create a new WSS header block if none exists
      * @return the WSS header or null if none found and doCreate is false
      */
     public static Element findWsseSecurityHeaderBlock(Document doc,
-                                                      Element envelope,
-                                                      String actor,
-                                                      boolean doCreate) {
+            Element envelope, String actor, boolean doCreate) {
         SOAPConstants sc = getSOAPConstants(envelope);
         Element wsseSecurity = getSecurityHeader(doc, actor, sc);
         if (wsseSecurity != null) {
             return wsseSecurity;
         }
-        Element header = findChildElement(envelope, sc.getEnvelopeURI(), sc.getHeaderQName().getLocalPart());
+        Element header = findChildElement(envelope, sc.getEnvelopeURI(), sc
+                .getHeaderQName().getLocalPart());
         if (header == null) {
             if (doCreate) {
-                header = createElementInSameNamespace(envelope, sc.getHeaderQName().getLocalPart());
+                header = createElementInSameNamespace(envelope, sc
+                        .getHeaderQName().getLocalPart());
                 header = prependChildElement(doc, envelope, header, true);
             }
         }
         if (doCreate) {
-            wsseSecurity = header.getOwnerDocument().createElementNS(WSConstants.WSSE_NS, "wsse:Security");
-            wsseSecurity.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:wsse", WSConstants.WSSE_NS);
+            wsseSecurity = header.getOwnerDocument().createElementNS(
+                    WSConstants.WSSE_NS, "wsse:Security");
+            wsseSecurity.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:wsse",
+                    WSConstants.WSSE_NS);
             return prependChildElement(doc, header, wsseSecurity, true);
         }
         return null;
     }
 
     /**
-     * create a base64 test node
-     * <p/>
-     *
-     * @param doc the DOM document (SOAP request)
-     * @param data to encode
+     * create a base64 test node <p/>
+     * 
+     * @param doc
+     *            the DOM document (SOAP request)
+     * @param data
+     *            to encode
      * @return a Text node containing the base64 encoded data
      */
     public static Text createBase64EncodedTextNode(Document doc, byte data[]) {
         return doc.createTextNode(Base64.encode(data));
     }
 
-    public static SecretKey prepareSecretKey(String symEncAlgo,
-                                             byte[] rawKey) {
-        SecretKeySpec keySpec = new SecretKeySpec(
-            rawKey, JCEMapper.getJCEKeyAlgorithmFromURI(symEncAlgo));
+    public static SecretKey prepareSecretKey(String symEncAlgo, byte[] rawKey) {
+        SecretKeySpec keySpec = new SecretKeySpec(rawKey, JCEMapper
+                .getJCEKeyAlgorithmFromURI(symEncAlgo));
         return (SecretKey) keySpec;
     }
 
@@ -636,63 +679,65 @@
     }
 
     public static Cipher getCipherInstance(String cipherAlgo, String jceId)
-			throws WSSecurityException {
-		Cipher cipher = null;
-		try {
-			if (cipherAlgo.equalsIgnoreCase(WSConstants.KEYTRANSPORT_RSA15)) {
-				if (jceId == null) {
-					cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
-				} else {
-					cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING", jceId);
-				}
-			} else if (cipherAlgo
-					.equalsIgnoreCase(WSConstants.KEYTRANSPORT_RSAOEP)) {
-				if (jceId == null) {
-					cipher = Cipher.getInstance("RSA/NONE/OAEPPADDING");
-				} else {
-					cipher = Cipher.getInstance("RSA/NONE/OAEPPADDING", jceId);
-				}
-			} else {
-				throw new WSSecurityException(
-						WSSecurityException.UNSUPPORTED_ALGORITHM,
-						"unsupportedKeyTransp", new Object[] { cipherAlgo });
-			}
-		} catch (NoSuchPaddingException ex) {
-			throw new WSSecurityException(
-					WSSecurityException.UNSUPPORTED_ALGORITHM,
-					"unsupportedKeyTransp", new Object[] { "No such padding: "
-							+ cipherAlgo });
-		} catch (NoSuchProviderException ex) {
-			throw new WSSecurityException(
-					WSSecurityException.UNSUPPORTED_ALGORITHM,
-					"unsupportedKeyTransp", new Object[] { "no provider: "
-							+ cipherAlgo });
-		} catch (NoSuchAlgorithmException ex) {
-			throw new WSSecurityException(
-					WSSecurityException.UNSUPPORTED_ALGORITHM,
-					"unsupportedKeyTransp",
-					new Object[] { "No such algorithm: " + cipherAlgo });
-		}
-		return cipher;
-	}
-
-    /**
-	 * Fetch the result of a given action from a given result vector <p/>
-	 * 
-	 * @param wsResultVector
-	 *            The result vector to fetch an action from
-	 * @param action
-	 *            The action to fetch
-	 * @return The result fetched from the result vector, null if the result
-	 *         could not be found
-	 */
-    public static WSSecurityEngineResult fetchActionResult(Vector wsResultVector, int action) {
+            throws WSSecurityException {
+        Cipher cipher = null;
+        try {
+            if (cipherAlgo.equalsIgnoreCase(WSConstants.KEYTRANSPORT_RSA15)) {
+                if (jceId == null) {
+                    cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
+                } else {
+                    cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING", jceId);
+                }
+            } else if (cipherAlgo
+                    .equalsIgnoreCase(WSConstants.KEYTRANSPORT_RSAOEP)) {
+                if (jceId == null) {
+                    cipher = Cipher.getInstance("RSA/NONE/OAEPPADDING");
+                } else {
+                    cipher = Cipher.getInstance("RSA/NONE/OAEPPADDING", jceId);
+                }
+            } else {
+                throw new WSSecurityException(
+                        WSSecurityException.UNSUPPORTED_ALGORITHM,
+                        "unsupportedKeyTransp", new Object[] { cipherAlgo });
+            }
+        } catch (NoSuchPaddingException ex) {
+            throw new WSSecurityException(
+                    WSSecurityException.UNSUPPORTED_ALGORITHM,
+                    "unsupportedKeyTransp", new Object[] { "No such padding: "
+                            + cipherAlgo });
+        } catch (NoSuchProviderException ex) {
+            throw new WSSecurityException(
+                    WSSecurityException.UNSUPPORTED_ALGORITHM,
+                    "unsupportedKeyTransp", new Object[] { "no provider: "
+                            + cipherAlgo });
+        } catch (NoSuchAlgorithmException ex) {
+            throw new WSSecurityException(
+                    WSSecurityException.UNSUPPORTED_ALGORITHM,
+                    "unsupportedKeyTransp",
+                    new Object[] { "No such algorithm: " + cipherAlgo });
+        }
+        return cipher;
+    }
+
+    /**
+     * Fetch the result of a given action from a given result vector <p/>
+     * 
+     * @param wsResultVector
+     *            The result vector to fetch an action from
+     * @param action
+     *            The action to fetch
+     * @return The result fetched from the result vector, null if the result
+     *         could not be found
+     */
+    public static WSSecurityEngineResult fetchActionResult(
+            Vector wsResultVector, int action) {
         WSSecurityEngineResult wsResult = null;
 
         // Find the part of the security result that matches the given action
 
         for (int i = 0; i < wsResultVector.size(); i++) {
-            // Check the result of every action whether it matches the given action
+            // Check the result of every action whether it matches the given
+            // action
             if (((WSSecurityEngineResult) wsResultVector.get(i)).getAction() == action) {
                 wsResult = (WSSecurityEngineResult) wsResultVector.get(i);
             }
@@ -700,6 +745,7 @@
 
         return wsResult;
     }
+
     /**
      * Fetch the result of a given action from a given result vector <p/>
      * 
@@ -707,7 +753,8 @@
      *            The result vector to fetch an action from
      * @param action
      *            The action to fetch
-     * @param results where to store the found results data for the action 
+     * @param results
+     *            where to store the found results data for the action
      * @return The result fetched from the result vector, null if the result
      *         could not be found
      */
@@ -725,7 +772,6 @@
         return results;
     }
 
-
     static public int decodeAction(String action, Vector actions)
             throws WSSecurityException {
 
@@ -764,45 +810,48 @@
                 doAction |= WSConstants.UT_SIGN;
                 actions.add(new Integer(WSConstants.UT_SIGN));
             } else {
-                throw new WSSecurityException("WSDoAllSender: Unknown action defined" + single[i]);
+                throw new WSSecurityException(
+                        "WSDoAllSender: Unknown action defined" + single[i]);
             }
         }
         return doAction;
     }
-    
+
     /**
-     * Returns the length of the key in # of bytes 
+     * Returns the length of the key in # of bytes
+     * 
      * @param algorithm
      * @return
      */
     public static int getKeyLength(String algorithm) throws WSSecurityException {
-        if(algorithm.equals(WSConstants.TRIPLE_DES)) {
+        if (algorithm.equals(WSConstants.TRIPLE_DES)) {
             return 24;
-        } else if (algorithm.equals(WSConstants.AES_128)){
+        } else if (algorithm.equals(WSConstants.AES_128)) {
             return 16;
-        } else if (algorithm.equals(WSConstants.AES_192)){
+        } else if (algorithm.equals(WSConstants.AES_192)) {
             return 24;
-        } else if (algorithm.equals(WSConstants.AES_256)){
+        } else if (algorithm.equals(WSConstants.AES_256)) {
             return 32;
-        } else if(XMLSignature.ALGO_ID_MAC_HMAC_SHA1.equals(algorithm)) {
+        } else if (XMLSignature.ALGO_ID_MAC_HMAC_SHA1.equals(algorithm)) {
             return 20;
-        } else if(XMLSignature.ALGO_ID_MAC_HMAC_SHA256.equals(algorithm)) {
+        } else if (XMLSignature.ALGO_ID_MAC_HMAC_SHA256.equals(algorithm)) {
             return 32;
-        } else if(XMLSignature.ALGO_ID_MAC_HMAC_SHA384.equals(algorithm)) {
+        } else if (XMLSignature.ALGO_ID_MAC_HMAC_SHA384.equals(algorithm)) {
             return 48;
-        } else if(XMLSignature.ALGO_ID_MAC_HMAC_SHA512.equals(algorithm)) {
+        } else if (XMLSignature.ALGO_ID_MAC_HMAC_SHA512.equals(algorithm)) {
             return 64;
-        } else if(XMLSignature.ALGO_ID_MAC_HMAC_NOT_RECOMMENDED_MD5.equals(algorithm)) {
+        } else if (XMLSignature.ALGO_ID_MAC_HMAC_NOT_RECOMMENDED_MD5
+                .equals(algorithm)) {
             return 16;
         } else {
-            throw new WSSecurityException(WSSecurityException.UNSUPPORTED_ALGORITHM, null, null, null);
+            throw new WSSecurityException(
+                    WSSecurityException.UNSUPPORTED_ALGORITHM, null, null, null);
         }
     }
-    
-    
-    
+
     /**
      * Generate a nonce of the given length
+     * 
      * @return
      * @throws Exception
      */
@@ -816,5 +865,121 @@
             throw new WSSecurityException(
                     "Error in generating nonce of length " + length, e);
         }
+    }
+
+    /**
+     * Search through a WSS4J results vector for a single signature covering all
+     * these elements.
+     * 
+     * @param results
+     *            results (e.g., as stored as WSHandlerConstants.RECV_RESULTS on
+     *            an Axis MessageContext)
+     * @param elements
+     *            the elements to check
+     * @return the identity of the signer
+     * @throws WSSecurityException
+     *             if no suitable signature could be found or if any element
+     *             didn't have a wsu:Id attribute
+     */
+    public static X509Certificate ensureSignedTogether(Iterator results,
+            Element[] elements) throws WSSecurityException {
+        log.debug("ensureSignedTogether()");
+
+        if (results == null)
+            throw new IllegalArgumentException("No results vector");
+        if (elements == null || elements.length == 0)
+            throw new IllegalArgumentException("No elements to check!");
+
+        // Turn the list of required elements into a list of required wsu:Id
+        // strings
+        String[] requiredIDs = new String[elements.length];
+        for (int i = 0; i < elements.length; i++) {
+            Element e = (Element) elements[i];
+            if (e == null) {
+                throw new IllegalArgumentException("elements[" + i
+                        + "] is null!");
+            }
+            requiredIDs[i] = e.getAttributeNS(WSConstants.WSU_NS, "Id");
+            if (requiredIDs[i] == null) {
+                throw new WSSecurityException(WSSecurityException.FAILED_CHECK,
+                        "requiredElementNoID", new Object[] { e.getNodeName() });
+            }
+            log.debug("Required element " + e.getNodeName() + " has wsu:Id "
+                    + requiredIDs[i]);
+        }
+
+        WSSecurityException fault = null;
+
+        // Search through the results for a SIGN result
+        while (results.hasNext()) {
+            WSHandlerResult result = (WSHandlerResult) results.next();
+            Iterator actions = result.getResults().iterator();
+
+            while (actions.hasNext()) {
+                WSSecurityEngineResult resultItem = (WSSecurityEngineResult) actions
+                        .next();
+                if (resultItem.getAction() == WSConstants.SIGN) {
+                    try {
+                        checkSignsAllElements(resultItem, requiredIDs);
+                        return resultItem.getCertificate();
+                    } catch (WSSecurityException ex) {
+                        // Store the exception but keep going... there may be a
+                        // better signature later
+                        log
+                                .debug(
+                                        "SIGN result does not sign all required elements",
+                                        ex);
+                        fault = ex;
+                    }
+                }
+            }
+        }
+
+        if (fault != null)
+            throw fault;
+
+        throw new WSSecurityException(WSSecurityException.FAILED_CHECK,
+                "noSignResult");
+    }
+
+    /**
+     * Ensure that this signature covers all required elements (identified by
+     * their wsu:Id attributes).
+     * 
+     * @param resultItem
+     *            the signature to check
+     * @param requiredIDs
+     *            the list of wsu:Id values that must be covered
+     * @throws WSSecurityException
+     *             if any required element is not included
+     */
+    private static void checkSignsAllElements(
+            WSSecurityEngineResult resultItem, String[] requiredIDs)
+            throws WSSecurityException {
+        if (resultItem.getAction() != WSConstants.SIGN)
+            throw new IllegalArgumentException("Not a SIGN result");
+
+        Set signedIDs = resultItem.getSignedElements();
+        if (signedIDs == null)
+            throw new RuntimeException(
+                    "Missing signedElements set in WSSecurityEngineResult!");
+
+        log.debug("Found SIGN result...");
+        for (Iterator i = signedIDs.iterator(); i.hasNext();) {
+            String e = (String) i.next();
+            log.debug("Signature includes element with ID " + e);
+        }
+
+        log.debug("Checking required elements are in the signature...");
+        for (int i = 0; i < requiredIDs.length; i++) {
+            if (!signedIDs.contains(requiredIDs[i])) {
+                throw new WSSecurityException(WSSecurityException.FAILED_CHECK,
+                        "requiredElementNotSigned",
+                        new Object[] { requiredIDs[i] });
+            }
+            log.debug("Element with ID " + requiredIDs[i]
+                    + " was correctly signed");
+        }
+        log.debug("All required elements are signed");
     }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: wss4j-dev-unsubscribe@ws.apache.org
For additional commands, e-mail: wss4j-dev-help@ws.apache.org