blob: adb92f81f2a947bf4d4617c5508d120311e6427d [file] [log] [blame]
/**
* 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.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 int length = 32;
private int offset = 0;
private int generation = -1;
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
);
if (elementLength != null) {
Text text = getFirstNode(elementLength);
if (text != null) {
try {
length = Integer.parseInt(text.getData());
} catch (NumberFormatException ex) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.FAILURE, ex, "decoding.general"
);
}
}
}
if (elementOffset != null) {
Text text = getFirstNode(elementOffset);
if (text != null) {
try {
offset = Integer.parseInt(text.getData());
} catch (NumberFormatException ex) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.FAILURE, ex, "decoding.general"
);
}
}
}
if (elementGeneration != null) {
Text text = getFirstNode(elementGeneration);
if (text != null) {
try {
generation = Integer.parseInt(text.getData());
} catch (NumberFormatException ex) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.FAILURE, ex, "decoding.general"
);
}
}
}
}
/**
* Add the WSU Namespace to this DKT. The namespace is not added by default for
* efficiency purposes.
*/
public void addWSUNamespace() {
element.setAttributeNS(XMLUtils.XMLNS_NS, "xmlns:" + WSConstants.WSU_PREFIX, WSConstants.WSU_NS);
}
/**
* 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);
this.length = length;
}
public int getLength() {
return length;
}
/**
* 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");
}
this.offset = offset;
}
public int getOffset() {
return offset;
}
/**
* 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");
}
this.generation = generation;
}
public int getGeneration() {
return generation;
}
/**
* 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 = org.apache.xml.security.utils.XMLUtils.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;
}
}