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