| /* |
| * 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.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. <p/> |
| * |
| * @author Davanum Srinivas (dims@yahoo.com). |
| */ |
| 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; |
| |
| /** |
| * 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 |
| */ |
| 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 = null; |
| int len = 0; |
| list = soapHeaderElement.getElementsByTagNameNS(WSConstants.WSSE_NS, |
| WSConstants.WSSE_LN); |
| if (list == null) { |
| return null; |
| } else { |
| len = list.getLength(); |
| } |
| Element elem; |
| Attr attr; |
| String hActor; |
| for (int i = 0; i < len; i++) { |
| elem = (Element) list.item(i); |
| attr = elem.getAttributeNodeNS(sc.getEnvelopeURI(), sc |
| .getRoleAttributeQName().getLocalPart()); |
| 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 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))) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * 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())) { |
| return currentChild; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * 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()); |
| 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 |
| * @return The found element or <code>null</code> |
| */ |
| public static Node findElement(Node startNode, String name, String namespace) { |
| |
| /* |
| * Replace the formely 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 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 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 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) { |
| 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 aixs.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; |
| if ((name = a.getName()).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. <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) { |
| |
| 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) || (id.charAt(0) != '#')) { |
| return null; |
| } |
| return id.substring(1); |
| } |
| |
| /** |
| * 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. <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) { |
| if (id == null) { |
| return null; |
| } |
| id = id.trim(); |
| if ((id.length() == 0) || (id.charAt(0) != '#')) { |
| return null; |
| } |
| id = id.substring(1); |
| return WSSecurityUtil.findElementById(doc.getDocumentElement(), id, |
| null); |
| } |
| |
| /** |
| * 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 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 <p/> |
| * |
| * @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 <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) { |
| Node whitespaceText = doc.createTextNode("\n"); |
| parent.appendChild(whitespaceText); |
| parent.appendChild(child); |
| return 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) { |
| Node firstChild = parent.getFirstChild(); |
| if (firstChild == null) { |
| parent.appendChild(child); |
| } else { |
| parent.insertBefore(child, firstChild); |
| } |
| if (addWhitespace) { |
| Node whitespaceText = doc.createTextNode("\n"); |
| parent.insertBefore(whitespaceText, child); |
| } |
| return child; |
| } |
| |
| /** |
| * 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) { |
| 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 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(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); |
| 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 |
| * @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(); |
| } else { |
| return new SOAP11Constants(); |
| } |
| } |
| |
| public static Cipher getCipherInstance(String cipherAlgo) |
| throws WSSecurityException { |
| Cipher cipher = null; |
| try { |
| 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 }); |
| } |
| } 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 <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 |
| 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 <p/> |
| * |
| * @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; |
| } |
| |
| static public 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 { |
| throw new WSSecurityException( |
| "WSDoAllSender: 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 initialzed."); |
| } |
| 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 initialize with the identifier |
| * specified in algorithm |
| */ |
| public synchronized static SecureRandom |
| resolveSecureRandom( |
| final String algorithm |
| ) throws NoSuchAlgorithmException { |
| if (random == null) { |
| random = SecureRandom.getInstance(algorithm); |
| random.setSeed(System.currentTimeMillis()); |
| } |
| return random; |
| } |
| } |