/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.wss4j.dom.message.token; import java.security.Principal; import java.util.Base64; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import javax.xml.namespace.QName; import org.apache.wss4j.dom.WSConstants; import org.apache.wss4j.common.ext.WSSecurityException; import org.apache.wss4j.common.bsp.BSPEnforcer; import org.apache.wss4j.common.derivedKey.ConversationConstants; import org.apache.wss4j.common.derivedKey.DerivedKeyUtils; import org.apache.wss4j.common.principal.WSDerivedKeyTokenPrincipal; import org.apache.wss4j.common.token.SecurityTokenReference; import org.apache.wss4j.common.util.DOM2Writer; import org.apache.wss4j.common.util.XMLUtils; import org.apache.wss4j.dom.util.WSSecurityUtil; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.Text; /** <DerivedKeyToken wsu:Id="..." wsc:Algorithm="..."> <SecurityTokenReference>...</SecurityTokenReference> <Properties>...</Properties> <Generation>...</Generation> <Offset>...</Offset> <Length>...</Length> <Label>...</Label> <Nonce>...</Nonce> </DerivedKeyToken> */ public class DerivedKeyToken { private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(DerivedKeyToken.class); // These are the elements that are used to create the SecurityContextToken private Element element; private Element elementSecurityTokenReference; private Element elementProperties; private Element elementGeneration; private Element elementOffset; private Element elementLength; private Element elementLabel; private Element elementNonce; private String ns; private final BSPEnforcer bspEnforcer; /** * This will create an empty DerivedKeyToken * * @param doc The DOM document */ public DerivedKeyToken(Document doc) throws WSSecurityException { this(ConversationConstants.DEFAULT_VERSION, doc); } /** * This will create an empty DerivedKeyToken * * @param doc The DOM document */ public DerivedKeyToken(int version, Document doc) throws WSSecurityException { LOG.debug("DerivedKeyToken: created"); ns = ConversationConstants.getWSCNs(version); element = doc.createElementNS(ns, ConversationConstants.WSC_PREFIX + ":" + ConversationConstants.DERIVED_KEY_TOKEN_LN); XMLUtils.setNamespace(element, ns, ConversationConstants.WSC_PREFIX); bspEnforcer = new BSPEnforcer(); } /** * This will create a DerivedKeyToken object with the given DerivedKeyToken element * * @param elem The DerivedKeyToken DOM element * @param bspEnforcer a BSPEnforcer instance to enforce BSP rules * @throws WSSecurityException If the element is not a derived key token */ public DerivedKeyToken(Element elem, BSPEnforcer bspEnforcer) throws WSSecurityException { LOG.debug("DerivedKeyToken: created : element constructor"); element = elem; this.bspEnforcer = bspEnforcer; QName el = new QName(element.getNamespaceURI(), element.getLocalName()); if (!(el.equals(ConversationConstants.DERIVED_KEY_TOKEN_QNAME_05_02) || el.equals(ConversationConstants.DERIVED_KEY_TOKEN_QNAME_05_12))) { throw new WSSecurityException( WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN ); } elementSecurityTokenReference = XMLUtils.getDirectChildElement( element, ConversationConstants.SECURITY_TOKEN_REFERENCE_LN, WSConstants.WSSE_NS ); ns = el.getNamespaceURI(); elementProperties = XMLUtils.getDirectChildElement( element, ConversationConstants.PROPERTIES_LN, ns ); elementGeneration = XMLUtils.getDirectChildElement( element, ConversationConstants.GENERATION_LN, ns ); elementOffset = XMLUtils.getDirectChildElement( element, ConversationConstants.OFFSET_LN, ns ); elementLength = XMLUtils.getDirectChildElement( element, ConversationConstants.LENGTH_LN, ns ); elementLabel = XMLUtils.getDirectChildElement( element, ConversationConstants.LABEL_LN, ns ); elementNonce = XMLUtils.getDirectChildElement( element, ConversationConstants.NONCE_LN, ns ); } /** * Add the WSU Namespace to this DKT. The namespace is not added by default for * efficiency purposes. */ public void addWSUNamespace() { XMLUtils.setNamespace(element, WSConstants.WSU_NS, WSConstants.WSU_PREFIX); } /** * Sets the security token reference of the derived key token * This is the reference to the shared secret used in the conversation/context * * @param ref Security token reference */ public void setSecurityTokenReference(SecurityTokenReference ref) { elementSecurityTokenReference = ref.getElement(); WSSecurityUtil.prependChildElement(element, ref.getElement()); } public void setSecurityTokenReference(Element elem) { elementSecurityTokenReference = elem; WSSecurityUtil.prependChildElement(element, elem); } /** * Returns the SecurityTokenReference of the derived key token * * @return the Security Token Reference of the derived key token * @throws WSSecurityException */ public SecurityTokenReference getSecurityTokenReference() throws WSSecurityException { if (elementSecurityTokenReference != null) { return new SecurityTokenReference(elementSecurityTokenReference, bspEnforcer); } return null; } /** * Returns the SecurityTokenReference element of the derived key token * * @return the Security Token Reference element of the derived key token */ public Element getSecurityTokenReferenceElement() { return elementSecurityTokenReference; } /** * This adds a property into * /DerivedKeyToken/Properties * * @param propName Name of the property * @param propValue Value of the property */ private void addProperty(String propName, String propValue) { if (elementProperties == null) { //Create the properties element if it is not there elementProperties = element.getOwnerDocument().createElementNS( ns, ConversationConstants.WSC_PREFIX + ":" + ConversationConstants.PROPERTIES_LN ); element.appendChild(elementProperties); } Element tempElement = element.getOwnerDocument().createElementNS(ns, ConversationConstants.WSC_PREFIX + ":" + propName); tempElement.appendChild(element.getOwnerDocument().createTextNode(propValue)); elementProperties.appendChild(tempElement); } /** * This is used to set the Name, Label and Nonce element values in the properties element * <b>At this point I'm not sure if these are the only properties that will appear in the * <code>Properties</code> element. There fore this method is provided * If this is not required feel free to remove this :D * </b> * * @param name Value of the Properties/Name element * @param label Value of the Properties/Label element * @param nonce Value of the Properties/Nonce element */ public void setProperties(String name, String label, String nonce) { Map<String, String> table = new HashMap<>(); table.put("Name", name); table.put("Label", label); table.put("Nonce", nonce); setProperties(table); } /** * If there are other types of properties other than Name, Label and Nonce * This is provided for extensibility purposes * * @param properties The properties and values in a Map */ public void setProperties(Map<String, String> properties) { if (properties != null && !properties.isEmpty()) { for (Entry<String, String> entry : properties.entrySet()) { String propertyName = entry.getValue(); //Check whether this property is already there //If so change the value Element node = XMLUtils.findElement(elementProperties, propertyName, ns); if (node != null) { //If the node is not null Text node1 = getFirstNode(node); if (node1 != null) { node1.setData(properties.get(propertyName)); } } else { addProperty(propertyName, properties.get(propertyName)); } } } } public Map<String, String> getProperties() { if (elementProperties != null) { Map<String, String> table = new HashMap<>(); Node node = elementProperties.getFirstChild(); while (node != null) { if (Node.ELEMENT_NODE == node.getNodeType()) { Text text = getFirstNode((Element) node); if (text != null) { table.put(node.getNodeName(), text.getData()); } } node = node.getNextSibling(); } return table; } return null; } /** * Sets the length of the derived key * * @param length The length of the derived key as a long */ public void setLength(int length) { elementLength = element.getOwnerDocument().createElementNS( ns, ConversationConstants.WSC_PREFIX + ":" + ConversationConstants.LENGTH_LN ); elementLength.appendChild( element.getOwnerDocument().createTextNode(Long.toString(length)) ); element.appendChild(elementLength); } public int getLength() { if (elementLength != null) { Text text = getFirstNode(elementLength); if (text != null) { return Integer.parseInt(text.getData()); } } return 32; } /** * Sets the offset * * @param offset The offset value as an integer */ public void setOffset(int offset) throws WSSecurityException { //This element MUST NOT be used if the <Generation> element is specified if (elementGeneration == null) { elementOffset = element.getOwnerDocument().createElementNS( ns, ConversationConstants.WSC_PREFIX + ":" + ConversationConstants.OFFSET_LN ); elementOffset.appendChild( element.getOwnerDocument().createTextNode(Integer.toString(offset)) ); element.appendChild(elementOffset); } else { throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "offsetError"); } } public int getOffset() { if (elementOffset != null) { Text text = getFirstNode(elementOffset); if (text != null) { return Integer.parseInt(text.getData()); } } return 0; } /** * Sets the generation of the derived key * * @param generation generation value as an integer */ public void setGeneration(int generation) throws WSSecurityException { //This element MUST NOT be used if the <Offset> element is specified if (elementOffset == null) { elementGeneration = element.getOwnerDocument().createElementNS( ns, ConversationConstants.WSC_PREFIX + ":" + ConversationConstants.GENERATION_LN ); elementGeneration.appendChild( element.getOwnerDocument().createTextNode(Integer.toString(generation)) ); element.appendChild(elementGeneration); } else { throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "offsetError"); } } public int getGeneration() { if (elementGeneration != null) { Text text = getFirstNode(elementGeneration); if (text != null) { return Integer.parseInt(text.getData()); } } return -1; } /** * Sets the label of the derived key * * @param label Label value as a string */ public void setLabel(String label) { elementLabel = element.getOwnerDocument().createElementNS( ns, ConversationConstants.WSC_PREFIX + ":" + ConversationConstants.LABEL_LN ); elementLabel.appendChild(element.getOwnerDocument().createTextNode(label)); element.appendChild(elementLabel); } /** * Sets the nonce value of the derived key * * @param nonce Nonce value as a string */ public void setNonce(String nonce) { elementNonce = element.getOwnerDocument().createElementNS( ns, ConversationConstants.WSC_PREFIX + ":" + ConversationConstants.NONCE_LN ); elementNonce.appendChild(element.getOwnerDocument().createTextNode(nonce)); element.appendChild(elementNonce); } /** * Returns the label of the derived key token * * @return Label of the derived key token */ public String getLabel() { if (elementLabel != null) { Text text = getFirstNode(elementLabel); if (text != null) { return text.getData(); } } return null; } /** * Return the nonce of the derived key token * * @return Nonce of the derived key token */ public String getNonce() { if (elementNonce != null) { Text text = getFirstNode(elementNonce); if (text != null) { return text.getData(); } } return null; } /** * Returns the first text node of an element. * * @param e the element to get the node from * @return the first text node or <code>null</code> if node * is null or is not a text node */ private Text getFirstNode(Element e) { Node node = e.getFirstChild(); return node != null && Node.TEXT_NODE == node.getNodeType() ? (Text) node : null; } /** * Returns the dom element of this <code>SecurityContextToken</code> object. * * @return the DerivedKeyToken element */ public Element getElement() { return element; } /** * Returns the string representation of the token. * * @return a XML string representation */ public String toString() { return DOM2Writer.nodeToString(element); } /** * Gets the id. * * @return the value of the <code>wsu:Id</code> attribute of this * DerivedKeyToken */ public String getID() { return element.getAttributeNS(WSConstants.WSU_NS, "Id"); } /** * Set the id of this derived key token. * * @param id the value for the <code>wsu:Id</code> attribute of this * DerivedKeyToken */ public void setID(String id) { element.setAttributeNS(WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":Id", id); } /** * Gets the derivation algorithm * * @return the value of the <code>wsc:Algorithm</code> attribute of this * DerivedKeyToken */ public String getAlgorithm() { String algo = element.getAttributeNS(ns, "Algorithm"); if ("".equals(algo)) { return ConversationConstants.DerivationAlgorithm.P_SHA_1; } else { return algo; } } /** * Create a WSDerivedKeyTokenPrincipal from this DerivedKeyToken object */ public Principal createPrincipal() throws WSSecurityException { WSDerivedKeyTokenPrincipal principal = new WSDerivedKeyTokenPrincipal(getID()); principal.setNonce(getNonce()); principal.setLabel(getLabel()); principal.setLength(getLength()); principal.setOffset(getOffset()); principal.setAlgorithm(getAlgorithm()); String basetokenId = null; SecurityTokenReference securityTokenReference = getSecurityTokenReference(); if (securityTokenReference != null && securityTokenReference.getReference() != null) { basetokenId = securityTokenReference.getReference().getURI(); basetokenId = XMLUtils.getIDFromReference(basetokenId); } else if (securityTokenReference != null) { // KeyIdentifier basetokenId = securityTokenReference.getKeyIdentifierValue(); } principal.setBasetokenId(basetokenId); return principal; } /** * Set the derivation algorithm of this derived key token. * * @param algo the value for the <code>Algorithm</code> attribute of this * DerivedKeyToken */ public void setAlgorithm(String algo) { if (algo != null) { element.setAttributeNS(ns, "Algorithm", algo); } } /** * Derive a key from this DerivedKeyToken instance * @param length * @param secret * @throws WSSecurityException */ public byte[] deriveKey(int length, byte[] secret) throws WSSecurityException { try { byte[] nonce = Base64.getMimeDecoder().decode(getNonce()); return DerivedKeyUtils.deriveKey(getAlgorithm(), getLabel(), length, secret, nonce, getOffset()); } catch (Exception e) { throw new WSSecurityException( WSSecurityException.ErrorCode.FAILURE, e ); } } @Override public int hashCode() { int result = 17; String algorithm = getAlgorithm(); if (algorithm != null) { result = 31 * result + algorithm.hashCode(); } try { SecurityTokenReference tokenReference = getSecurityTokenReference(); if (tokenReference != null) { result = 31 * result + tokenReference.hashCode(); } } catch (WSSecurityException e) { LOG.error(e.getMessage(), e); } Map<String, String> properties = getProperties(); if (properties != null) { result = 31 * result + properties.hashCode(); } int generation = getGeneration(); if (generation != -1) { result = 31 * result + generation; } int offset = getOffset(); if (offset != -1) { result = 31 * result + offset; } int length = getLength(); if (length != -1) { result = 31 * result + length; } String label = getLabel(); if (label != null) { result = 31 * result + label.hashCode(); } String nonce = getNonce(); if (nonce != null) { result = 31 * result + nonce.hashCode(); } return result; } @Override public boolean equals(Object object) { if (!(object instanceof DerivedKeyToken)) { return false; } DerivedKeyToken token = (DerivedKeyToken)object; if (!compare(getAlgorithm(), token.getAlgorithm())) { return false; } try { if (getSecurityTokenReference() != null && !getSecurityTokenReference().equals(token.getSecurityTokenReference()) || getSecurityTokenReference() == null && token.getSecurityTokenReference() != null) { return false; } } catch (WSSecurityException e) { LOG.error(e.getMessage(), e); return false; } if (!compare(getProperties(), token.getProperties())) { return false; } if (getGeneration() != token.getGeneration()) { return false; } if (getOffset() != token.getOffset()) { return false; } if (getLength() != token.getLength()) { return false; } if (!compare(getLabel(), token.getLabel())) { return false; } if (!compare(getNonce(), token.getNonce())) { return false; } return true; } private boolean compare(String item1, String item2) { if (item1 == null && item2 != null) { return false; } else if (item1 != null && !item1.equals(item2)) { return false; } return true; } private boolean compare(Map<String, String> item1, Map<String, String> item2) { if (item1 == null && item2 != null) { return false; } else if (item1 != null && !item1.equals(item2)) { return false; } return true; } }