org.apache.ws.security.util.WSSecurityUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of wss4j Show documentation
Show all versions of wss4j Show documentation
The Apache WSS4J project provides a Java implementation of the primary security standards
for Web Services, namely the OASIS Web Services Security (WS-Security) specifications
from the OASIS Web Services Security TC.
/*
* 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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ws.security.SOAP11Constants;
import org.apache.ws.security.SOAP12Constants;
import org.apache.ws.security.SOAPConstants;
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.WSSecurityEngineResult;
import org.apache.ws.security.WSSecurityException;
import org.apache.ws.security.WSSConfig;
import org.apache.ws.security.handler.WSHandlerConstants;
import org.apache.ws.security.handler.WSHandlerResult;
import org.apache.xml.security.algorithms.JCEMapper;
import org.apache.xml.security.signature.XMLSignature;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.namespace.QName;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;
/**
* WS-Security Utility methods.
*
* @author Davanum Srinivas ([email protected]).
*/
public class WSSecurityUtil {
private static Log log = LogFactory.getLog(WSSecurityUtil.class);
/**
* A cached pseudo-random number generator
* NB. On some JVMs, caching this random number
* generator is required to overcome punitive
* overhead.
*/
private static SecureRandom random = null;
private static String randomAlgorithm = null;
/**
* 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 wsse:Security
element or null
* if not such element found
*/
public static Element getSecurityHeader(Document doc, String actor, SOAPConstants sc) {
Element soapHeaderElement =
(Element) getDirectChild(
doc.getDocumentElement(),
sc.getHeaderQName().getLocalPart(),
sc.getEnvelopeURI()
);
if (soapHeaderElement == null) { // no SOAP header at all
return null;
}
// get all wsse:Security nodes
NodeList list =
soapHeaderElement.getElementsByTagNameNS(WSConstants.WSSE_NS, WSConstants.WSSE_LN);
if (list == null) {
return null;
}
for (int i = 0; i < list.getLength(); i++) {
Element elem = (Element) list.item(i);
Attr attr =
elem.getAttributeNodeNS(
sc.getEnvelopeURI(), sc.getRoleAttributeQName().getLocalPart()
);
String hActor = (attr != null) ? attr.getValue() : null;
if (WSSecurityUtil.isActorEqual(actor, hActor)) {
return elem;
}
}
return null;
}
/**
* 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 true is the actor arguments are equal
*/
public static boolean isActorEqual(String actor, String hActor) {
if (((hActor == null) || (hActor.length() == 0))
&& ((actor == null) || (actor.length() == 0))) {
return true;
}
if ((hActor != null) && (actor != null) && hActor.equalsIgnoreCase(actor)) {
return true;
}
return false;
}
/**
* Gets a direct child with specified localname and namespace.
*
* @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 null
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())) {
return currentChild;
}
}
return null;
}
/**
* Gets a direct child with specified localname and namespace.
*
* @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 null
if not such node found
*/
public static Element getDirectChildElement(
Node fNode,
String localName,
String namespace
) {
for (
Node currentChild = fNode.getFirstChild();
currentChild != null;
currentChild = currentChild.getNextSibling()
) {
if (Node.ELEMENT_NODE == currentChild.getNodeType()
&& localName.equals(currentChild.getLocalName())
&& namespace.equals(currentChild.getNamespaceURI())) {
return (Element)currentChild;
}
}
return null;
}
/**
* return the first soap "Body" element.
*
* @param doc
* @return the body element or null
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()
);
return soapBodyElement;
}
/**
* Returns the first element that matches name
and
* namespace
. This is a replacement for a XPath lookup
* //name
with the given namespace. It's somewhat faster than
* XPath, and we do not deal with prefixes, just with the real namespace URI
*
* @param start Node Where to start the search
* @param name Local name of the element
* @param namespace Namespace URI of the element
* @return The found element or null
*/
public static Node findElement(Node startNode, String name, String namespace) {
//
// Replace the formerly recursive implementation with a depth-first-loop
// lookup
//
if (startNode == null) {
return null;
}
Node startParent = startNode.getParentNode();
Node processedNode = null;
while (startNode != null) {
// start node processing at this point
if (startNode.getNodeType() == Node.ELEMENT_NODE
&& startNode.getLocalName().equals(name)) {
String ns = startNode.getNamespaceURI();
if (ns != null && ns.equals(namespace)) {
return startNode;
}
if ((namespace == null || namespace.length() == 0)
&& (ns == null || ns.length() == 0)) {
return startNode;
}
}
processedNode = startNode;
startNode = startNode.getFirstChild();
// no child, this node is done.
if (startNode == null) {
// close node processing, get sibling
startNode = processedNode.getNextSibling();
}
// no more siblings, get parent, all children
// of parent are processed.
while (startNode == null) {
processedNode = processedNode.getParentNode();
if (processedNode == startParent) {
return null;
}
// close parent node processing (processed node now)
startNode = processedNode.getNextSibling();
}
}
return null;
}
/**
* Returns the single SAMLAssertion element that contains an AssertionID/ID that
* matches the supplied parameter.
*
* @param startNode Where to start the search
* @param value Value of the AssertionID/ID attribute
* @return The found element if there was exactly one match, or
* null
otherwise
*/
public static Element findSAMLAssertionElementById(Node startNode, String value) {
Element foundElement = null;
//
// Replace the formerly recursive implementation with a depth-first-loop
// lookup
//
if (startNode == null) {
return null;
}
Node startParent = startNode.getParentNode();
Node processedNode = null;
while (startNode != null) {
// start node processing at this point
if (startNode.getNodeType() == Node.ELEMENT_NODE) {
Element se = (Element) startNode;
if ((se.hasAttribute("ID") && value.equals(se.getAttribute("ID")))
|| (se.hasAttribute("AssertionID")
&& value.equals(se.getAttribute("AssertionID")))) {
if (foundElement == null) {
foundElement = se; // Continue searching to find duplicates
} else {
log.warn("Multiple elements with the same 'ID' attribute value!");
return null;
}
}
}
processedNode = startNode;
startNode = startNode.getFirstChild();
// no child, this node is done.
if (startNode == null) {
// close node processing, get sibling
startNode = processedNode.getNextSibling();
}
// no more siblings, get parent, all children
// of parent are processed.
while (startNode == null) {
processedNode = processedNode.getParentNode();
if (processedNode == startParent) {
return foundElement;
}
// close parent node processing (processed node now)
startNode = processedNode.getNextSibling();
}
}
return foundElement;
}
/**
* Returns the single element that contains an Id with value
* uri
and namespace
. 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
* null
otherwise
*/
public static Element findElementById(Node startNode, String value, String namespace) {
Element foundElement = null;
//
// Replace the formerly recursive implementation with a depth-first-loop
// lookup
//
if (startNode == null) {
return null;
}
Node startParent = startNode.getParentNode();
Node processedNode = null;
while (startNode != null) {
// 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"))) {
if (foundElement == null) {
foundElement = se; // Continue searching to find duplicates
} else {
log.warn("Multiple elements with the same 'Id' attribute value!");
return null;
}
}
}
processedNode = startNode;
startNode = startNode.getFirstChild();
// no child, this node is done.
if (startNode == null) {
// close node processing, get sibling
startNode = processedNode.getNextSibling();
}
// no more siblings, get parent, all children
// of parent are processed.
while (startNode == null) {
processedNode = processedNode.getParentNode();
if (processedNode == startParent) {
return foundElement;
}
// close parent node processing (processed node now)
startNode = processedNode.getNextSibling();
}
}
return foundElement;
}
/**
* Set a namespace/prefix on an element if it is not set already. First off, it
* searches for the element for the prefix associated with the specified
* namespace. If the prefix isn't null, then this is returned. Otherwise, it
* creates a new attribute using the namespace/prefix passed as parameters.
*
* @param element
* @param namespace
* @param prefix
* @return the prefix associated with the set namespace
*/
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);
return prefix;
}
/*
* The following methods were copied over from axis.utils.XMLUtils and adapted
*/
public static String getPrefixNS(String uri, Node e) {
while (e != null && (e.getNodeType() == Element.ELEMENT_NODE)) {
NamedNodeMap attrs = e.getAttributes();
for (int n = 0; n < attrs.getLength(); n++) {
Attr a = (Attr) attrs.item(n);
String name = a.getName();
if (name.startsWith("xmlns:") && a.getNodeValue().equals(uri)) {
return name.substring(6);
}
}
e = e.getParentNode();
}
return null;
}
public static String getNamespace(String prefix, Node e) {
while (e != null && (e.getNodeType() == Node.ELEMENT_NODE)) {
Attr attr = null;
if (prefix == null) {
attr = ((Element) e).getAttributeNode("xmlns");
} else {
attr = ((Element) e).getAttributeNodeNS(WSConstants.XMLNS_NS, prefix);
}
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 generated from the given string representation
*/
public static QName getQNameFromString(String str, Node e) {
return getQNameFromString(str, e, false);
}
/**
* 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) {
if (str == null || e == null) {
return null;
}
int idx = str.indexOf(':');
if (idx > -1) {
String prefix = str.substring(0, idx);
String ns = getNamespace(prefix, e);
if (ns == null) {
return null;
}
return new QName(ns, str.substring(idx + 1));
} else {
if (defaultNS) {
String ns = getNamespace(null, e);
if (ns != null) {
return new QName(ns, str);
}
}
return new QName("", str);
}
}
/**
* 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();
String prefix = getPrefixNS(uri, e);
if (prefix == null) {
int i = 1;
prefix = "ns" + i;
while (getNamespace(prefix, e) != null) {
i++;
prefix = "ns" + i;
}
e.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:" + prefix, uri);
}
return prefix + ":" + qname.getLocalPart();
}
/* up to here */
/**
* Search for an element given its wsu:id.
*
* @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) {
if (id == null) {
return null;
}
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) {
return null;
}
if (id.charAt(0) == '#') {
id = id.substring(1);
}
return id;
}
/**
* 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
* @deprecated use getIDFromReference instead
*/
public static String getIDfromReference(String ref) {
return getIDFromReference(ref);
}
/**
* Search for an element given its generic id.
*
* @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) {
if (id == null) {
return null;
}
id = getIDFromReference(id);
return WSSecurityUtil.findElementById(doc.getDocumentElement(), id, null);
}
/**
* create a new element in the same namespace
*
* @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 qName = localName;
String prefix = parent.getPrefix();
if (prefix != null && prefix.length() > 0) {
qName = prefix + ":" + localName;
}
String nsUri = parent.getNamespaceURI();
return parent.getOwnerDocument().createElementNS(nsUri, qName);
}
/**
* find a child element with given namespace and local name
*
* @param parent the node to start the search
* @param namespaceUri of the element
* @param localName of the element
* @return the found element or null if the element does not exist
*/
private static Element findChildElement(Element parent, 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())) {
return elementChild;
}
}
}
return null;
}
/**
* append a child element
*
* @param doc the DOM document (SOAP request)
* @param parent element of this child element
* @param child the element to append
* @deprecated use {@link Node#appendChild(Node)} instead
* @return the child element
*/
public static Element appendChildElement(
Document doc,
Element parent,
Element child
) {
Node whitespaceText = doc.createTextNode("\n");
parent.appendChild(whitespaceText);
parent.appendChild(child);
return child;
}
/**
* prepend a child element
*
* @param parent element of this child element
* @param child the element to append
* @return the child element
*/
public static Element prependChildElement(
Element parent,
Element child
) {
Node firstChild = parent.getFirstChild();
if (firstChild == null) {
return (Element)parent.appendChild(child);
} else {
return (Element)parent.insertBefore(child, firstChild);
}
}
/**
* prepend a child element
*
* @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
* @deprecated use {@link WSSecurityUtil#prependChildElement(Element, Element)}
* instead
* @return the child element
*/
public static Element prependChildElement(
Document doc,
Element parent,
Element child,
boolean addWhitespace
) {
Node firstChild = parent.getFirstChild();
Node addedChild;
if (firstChild == null) {
addedChild = parent.appendChild(child);
} else {
addedChild = parent.insertBefore(child, firstChild);
}
if (addWhitespace) {
Node whitespaceText = doc.createTextNode("\n");
parent.insertBefore(whitespaceText, addedChild);
}
return (Element)addedChild;
}
/**
* find the first ws-security header block
*
* @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
) {
return findWsseSecurityHeaderBlock(doc, envelope, null, doCreate);
}
/**
* find a ws-security header block for a given actor
*
* @param doc the DOM document (SOAP request)
* @param envelope the SOAP envelope
* @param actot the actor (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
) {
SOAPConstants sc = getSOAPConstants(envelope);
Element wsseSecurity = getSecurityHeader(doc, actor, sc);
if (wsseSecurity != null) {
return wsseSecurity;
}
Element header =
findChildElement(envelope, sc.getEnvelopeURI(), sc.getHeaderQName().getLocalPart());
if (header == null && doCreate) {
header =
createElementInSameNamespace(envelope, sc.getHeaderQName().getLocalPart());
header = prependChildElement(envelope, header);
}
if (doCreate) {
wsseSecurity =
header.getOwnerDocument().createElementNS(WSConstants.WSSE_NS, "wsse:Security");
wsseSecurity.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:wsse", WSConstants.WSSE_NS);
return prependChildElement(header, wsseSecurity);
}
return null;
}
/**
* create a base64 test node
*
* @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));
return (SecretKey) keySpec;
}
public static SOAPConstants getSOAPConstants(Element startElement) {
Document doc = startElement.getOwnerDocument();
String ns = doc.getDocumentElement().getNamespaceURI();
if (WSConstants.URI_SOAP12_ENV.equals(ns)) {
return new SOAP12Constants();
}
return new SOAP11Constants();
}
public static Cipher getCipherInstance(String cipherAlgo)
throws WSSecurityException {
String jceid = JCEMapper.translateURItoJCEID(cipherAlgo);
Cipher cipher = null;
try {
if (jceid == null) {
if (cipherAlgo.equalsIgnoreCase(WSConstants.KEYTRANSPORT_RSA15)) {
cipher = Cipher.getInstance("RSA/NONE/PKCS1PADDING");
} else if (cipherAlgo.equalsIgnoreCase(WSConstants.KEYTRANSPORT_RSAOEP)) {
cipher = Cipher.getInstance("RSA/NONE/OAEPPADDING");
} else {
throw new WSSecurityException(
WSSecurityException.UNSUPPORTED_ALGORITHM,
"unsupportedKeyTransp", new Object[] {cipherAlgo}
);
}
} else {
cipher = Cipher.getInstance(jceid);
}
} catch (NoSuchPaddingException ex) {
throw new WSSecurityException(
WSSecurityException.UNSUPPORTED_ALGORITHM, "unsupportedKeyTransp",
new Object[] { "No such padding: " + cipherAlgo }, ex
);
} catch (NoSuchAlgorithmException ex) {
throw new WSSecurityException(
WSSecurityException.UNSUPPORTED_ALGORITHM, "unsupportedKeyTransp",
new Object[] { "No such algorithm: " + cipherAlgo }, ex
);
}
return cipher;
}
/**
* Fetch the result of a given action from a given result vector
*
* @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
WSSecurityEngineResult result =
(WSSecurityEngineResult) wsResultVector.get(i);
int resultAction =
((java.lang.Integer)result.get(WSSecurityEngineResult.TAG_ACTION)).intValue();
if (resultAction == action) {
wsResult = (WSSecurityEngineResult) wsResultVector.get(i);
}
}
return wsResult;
}
/**
* Fetch the result of a given action from a given result vector
*
* @param wsResultVector 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
* @return The result fetched from the result vector, null if the result
* could not be found
*/
public static Vector fetchAllActionResults(
Vector wsResultVector,
int action,
Vector results
) {
// Find the parts 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
WSSecurityEngineResult result =
(WSSecurityEngineResult) wsResultVector.get(i);
int resultAction =
((java.lang.Integer)result.get(WSSecurityEngineResult.TAG_ACTION)).intValue();
if (resultAction == action) {
results.add(wsResultVector.get(i));
}
}
return results;
}
public static int decodeAction(String action, Vector actions) throws WSSecurityException {
int doAction = 0;
if (action == null) {
return doAction;
}
String single[] = StringUtil.split(action, ' ');
for (int i = 0; i < single.length; i++) {
if (single[i].equals(WSHandlerConstants.NO_SECURITY)) {
doAction = WSConstants.NO_SECURITY;
return doAction;
} else if (single[i].equals(WSHandlerConstants.USERNAME_TOKEN)) {
doAction |= WSConstants.UT;
actions.add(new Integer(WSConstants.UT));
} else if (single[i].equals(WSHandlerConstants.SIGNATURE)) {
doAction |= WSConstants.SIGN;
actions.add(new Integer(WSConstants.SIGN));
} else if (single[i].equals(WSHandlerConstants.ENCRYPT)) {
doAction |= WSConstants.ENCR;
actions.add(new Integer(WSConstants.ENCR));
} else if (single[i].equals(WSHandlerConstants.SAML_TOKEN_UNSIGNED)) {
doAction |= WSConstants.ST_UNSIGNED;
actions.add(new Integer(WSConstants.ST_UNSIGNED));
} else if (single[i].equals(WSHandlerConstants.SAML_TOKEN_SIGNED)) {
doAction |= WSConstants.ST_SIGNED;
actions.add(new Integer(WSConstants.ST_SIGNED));
} else if (single[i].equals(WSHandlerConstants.TIMESTAMP)) {
doAction |= WSConstants.TS;
actions.add(new Integer(WSConstants.TS));
} else if (single[i].equals(WSHandlerConstants.NO_SERIALIZATION)) {
doAction |= WSConstants.NO_SERIALIZE;
actions.add(new Integer(WSConstants.NO_SERIALIZE));
} else if (single[i].equals(WSHandlerConstants.SIGN_WITH_UT_KEY)) {
doAction |= WSConstants.UT_SIGN;
actions.add(new Integer(WSConstants.UT_SIGN));
} else if (single[i].equals(WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION)) {
doAction |= WSConstants.SC;
actions.add(new Integer(WSConstants.SC));
} else {
throw new WSSecurityException(
"Unknown action defined: " + single[i]
);
}
}
return doAction;
}
/**
* Decode an action String. This method should only be called on the outbound side.
* @param action The initial String of actions to perform
* @param actions The vector of created actions that will be performed
* @param wssConfig This object holds the list of custom actions to be performed.
* @return The or'd integer of all the actions (apart from the custom actions)
* @throws WSSecurityException
*/
public static int decodeAction(
String action,
Vector actions,
WSSConfig wssConfig
) throws WSSecurityException {
int doAction = 0;
if (action == null) {
return doAction;
}
String single[] = StringUtil.split(action, ' ');
for (int i = 0; i < single.length; i++) {
if (single[i].equals(WSHandlerConstants.NO_SECURITY)) {
doAction = WSConstants.NO_SECURITY;
return doAction;
} else if (single[i].equals(WSHandlerConstants.USERNAME_TOKEN)) {
doAction |= WSConstants.UT;
actions.add(new Integer(WSConstants.UT));
} else if (single[i].equals(WSHandlerConstants.SIGNATURE)) {
doAction |= WSConstants.SIGN;
actions.add(new Integer(WSConstants.SIGN));
} else if (single[i].equals(WSHandlerConstants.ENCRYPT)) {
doAction |= WSConstants.ENCR;
actions.add(new Integer(WSConstants.ENCR));
} else if (single[i].equals(WSHandlerConstants.SAML_TOKEN_UNSIGNED)) {
doAction |= WSConstants.ST_UNSIGNED;
actions.add(new Integer(WSConstants.ST_UNSIGNED));
} else if (single[i].equals(WSHandlerConstants.SAML_TOKEN_SIGNED)) {
doAction |= WSConstants.ST_SIGNED;
actions.add(new Integer(WSConstants.ST_SIGNED));
} else if (single[i].equals(WSHandlerConstants.TIMESTAMP)) {
doAction |= WSConstants.TS;
actions.add(new Integer(WSConstants.TS));
} else if (single[i].equals(WSHandlerConstants.NO_SERIALIZATION)) {
doAction |= WSConstants.NO_SERIALIZE;
actions.add(new Integer(WSConstants.NO_SERIALIZE));
} else if (single[i].equals(WSHandlerConstants.SIGN_WITH_UT_KEY)) {
doAction |= WSConstants.UT_SIGN;
actions.add(new Integer(WSConstants.UT_SIGN));
} else if (single[i].equals(WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION)) {
doAction |= WSConstants.SC;
actions.add(new Integer(WSConstants.SC));
} else {
try {
int parsedAction = Integer.parseInt(single[i]);
if (wssConfig.getAction(parsedAction) == null) {
throw new WSSecurityException(
"Unknown action defined: " + single[i]
);
}
actions.add(new Integer(parsedAction));
} catch (NumberFormatException ex) {
throw new WSSecurityException(
"Unknown action defined: " + single[i]
);
}
}
}
return doAction;
}
/**
* Returns the length of the key in # of bytes
*
* @param algorithm
* @return the key length
*/
public static int getKeyLength(String algorithm) throws WSSecurityException {
if (algorithm.equals(WSConstants.TRIPLE_DES)) {
return 24;
} else if (algorithm.equals(WSConstants.AES_128)) {
return 16;
} else if (algorithm.equals(WSConstants.AES_192)) {
return 24;
} else if (algorithm.equals(WSConstants.AES_256)) {
return 32;
} else if (XMLSignature.ALGO_ID_MAC_HMAC_SHA1.equals(algorithm)) {
return 20;
} else if (XMLSignature.ALGO_ID_MAC_HMAC_SHA256.equals(algorithm)) {
return 32;
} else if (XMLSignature.ALGO_ID_MAC_HMAC_SHA384.equals(algorithm)) {
return 48;
} else if (XMLSignature.ALGO_ID_MAC_HMAC_SHA512.equals(algorithm)) {
return 64;
} else if (XMLSignature.ALGO_ID_MAC_HMAC_NOT_RECOMMENDED_MD5.equals(algorithm)) {
return 16;
} else {
throw new WSSecurityException(
WSSecurityException.UNSUPPORTED_ALGORITHM, null, null, null
);
}
}
/**
* Generate a nonce of the given length
*
* @return a nonce of the given length
* @throws Exception
*/
public static byte[] generateNonce(int length) throws WSSecurityException {
try {
final SecureRandom r = resolveSecureRandom();
if (r == null) {
throw new WSSecurityException("Random generator is not initialized.");
}
byte[] temp = new byte[length];
r.nextBytes(temp);
return temp;
} catch (Exception e) {
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.
*
* NOTE: it is important that the given elements are those that are
* referenced using wsu:Id. When the signed element is referenced using a
* transformation such as XPath filtering the validation is carried out
* in signature verification itself.
*
* @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();
int resultAction =
((java.lang.Integer)resultItem.get(WSSecurityEngineResult.TAG_ACTION)).intValue();
if (resultAction == WSConstants.SIGN) {
try {
checkSignsAllElements(resultItem, requiredIDs);
return
(X509Certificate)resultItem.get(
WSSecurityEngineResult.TAG_X509_CERTIFICATE
);
} 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 {
int resultAction =
((java.lang.Integer)resultItem.get(WSSecurityEngineResult.TAG_ACTION)).intValue();
if (resultAction != WSConstants.SIGN) {
throw new IllegalArgumentException("Not a SIGN result");
}
Set sigElems = (Set)resultItem.get(WSSecurityEngineResult.TAG_SIGNED_ELEMENT_IDS);
if (sigElems == null) {
throw new RuntimeException(
"Missing signedElements set in WSSecurityEngineResult!"
);
}
log.debug("Found SIGN result...");
for (Iterator i = sigElems.iterator(); i.hasNext();) {
Object sigElement = i.next();
if(sigElement instanceof String) {
log.debug("Signature includes element with ID " + sigElement);
} else {
log.debug("Signature includes element with null uri " + sigElement.toString());
}
}
log.debug("Checking required elements are in the signature...");
for (int i = 0; i < requiredIDs.length; i++) {
if (!sigElems.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");
}
/**
* @return a SecureRandom instance initialized with the "SHA1PRNG"
* algorithm identifier
*/
public static SecureRandom
resolveSecureRandom() throws NoSuchAlgorithmException {
return resolveSecureRandom("SHA1PRNG");
}
/**
* @param algorithm
*
* @return a SecureRandom instance initialized with the identifier
* specified in algorithm
*/
public synchronized static SecureRandom
resolveSecureRandom(
final String algorithm
) throws NoSuchAlgorithmException {
if (random == null || !algorithm.equals(randomAlgorithm)) {
random = SecureRandom.getInstance(algorithm);
randomAlgorithm = algorithm;
random.setSeed(System.currentTimeMillis());
}
return random;
}
}