blob: 2d68f1a349a74666f69629a5bcde21c57ba9073d [file] [log] [blame]
/*
* Copyright 2003-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.ws.security.message;
import org.apache.ws.security.SOAPConstants;
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.WSEncryptionPart;
import org.apache.ws.security.WSSecurityException;
import org.apache.ws.security.conversation.ConversationException;
import org.apache.ws.security.message.token.Reference;
import org.apache.ws.security.message.token.SecurityTokenReference;
import org.apache.ws.security.util.WSSecurityUtil;
import org.apache.xml.security.encryption.EncryptedData;
import org.apache.xml.security.encryption.XMLCipher;
import org.apache.xml.security.encryption.XMLEncryptionException;
import org.apache.xml.security.keys.KeyInfo;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import javax.crypto.SecretKey;
import java.util.Vector;
/**
* Encrypts and signs parts of a message with derived keys derived from a
* symmetric key. This symmetric key will be included as an EncryptedKey
*
* @author Ruchith Fernando (ruchith.fernando@gmail.com)
*/
public class WSSecDKEncrypt extends WSSecDerivedKeyBase {
protected String symEncAlgo = WSConstants.AES_128;
public Document build(Document doc, WSSecHeader secHeader)
throws WSSecurityException, ConversationException {
/*
* Setup the encrypted key
*/
prepare(doc);
this.envelope = doc.getDocumentElement();
/*
* prepend elements in the right order to the security header
*/
prependDKElementToHeader(secHeader);
SOAPConstants soapConstants = WSSecurityUtil.getSOAPConstants(envelope);
if (parts == null) {
parts = new Vector();
WSEncryptionPart encP = new WSEncryptionPart(soapConstants
.getBodyQName().getLocalPart(), soapConstants
.getEnvelopeURI(), "Content");
parts.add(encP);
}
Element externRefList = encryptForExternalRef(null, parts);
addExternalRefElement(externRefList, secHeader);
return doc;
}
private Vector doEncryption(Document doc, byte[] secretKey, Vector references) throws WSSecurityException {
SecretKey key = WSSecurityUtil.prepareSecretKey(this.symEncAlgo, secretKey);
XMLCipher xmlCipher = null;
try {
xmlCipher = XMLCipher.getInstance(symEncAlgo);
} catch (XMLEncryptionException e3) {
throw new WSSecurityException(
WSSecurityException.UNSUPPORTED_ALGORITHM, null, null, e3);
}
Vector encDataRefs = new Vector();
if(envelope == null) {
envelope = doc.getDocumentElement();
}
for (int part = 0; part < references.size(); part++) {
WSEncryptionPart encPart = (WSEncryptionPart) references.get(part);
String idToEnc = encPart.getId();
String elemName = encPart.getName();
String nmSpace = encPart.getNamespace();
String modifier = encPart.getEncModifier();
/*
* Third step: get the data to encrypt.
*/
Element body = null;
if (idToEnc != null) {
body = WSSecurityUtil.findElementById(document
.getDocumentElement(), idToEnc, WSConstants.WSU_NS);
if (body == null) {
body = WSSecurityUtil.findElementById(document
.getDocumentElement(), idToEnc, null);
}
} else {
body = (Element) WSSecurityUtil.findElement(envelope, elemName,
nmSpace);
}
if (body == null) {
throw new WSSecurityException(WSSecurityException.FAILURE,
"noEncElement", new Object[] { "{" + nmSpace + "}"
+ elemName });
}
boolean content = modifier.equals("Content") ? true : false;
String xencEncryptedDataId = "EncDataId-" + body.hashCode();
/*
* Forth step: encrypt data, and set necessary attributes in
* xenc:EncryptedData
*/
try {
//Create the SecurityTokenRef to the DKT
KeyInfo keyInfo = new KeyInfo(document);
SecurityTokenReference secToken = new SecurityTokenReference(document);
Reference ref = new Reference(document);
ref.setURI("#" + dktId);
secToken.setReference(ref);
keyInfo.addUnknownElement(secToken.getElement());
Element keyInfoElement = keyInfo.getElement();
keyInfoElement.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:"
+ WSConstants.SIG_PREFIX, WSConstants.SIG_NS);
xmlCipher.init(XMLCipher.ENCRYPT_MODE, key);
EncryptedData encData = xmlCipher.getEncryptedData();
encData.setId(xencEncryptedDataId);
encData.setKeyInfo(keyInfo);
xmlCipher.doFinal(doc, body, content);
} catch (Exception e2) {
throw new WSSecurityException(
WSSecurityException.FAILED_ENCRYPTION, null, null, e2);
}
encDataRefs.add(new String("#" + xencEncryptedDataId));
}
return encDataRefs;
}
/**
* Encrypt one or more parts or elements of the message (external).
*
* This method takes a vector of <code>WSEncryptionPart</code> object that
* contain information about the elements to encrypt. The method call the
* encryption method, takes the reference information generated during
* encryption and add this to the <code>xenc:Reference</code> element.
* This method can be called after <code>prepare()</code> and can be
* called multiple times to encrypt a number of parts or elements.
*
* </p>
*
* The method generates a <code>xenc:Reference</code> element that <i>must</i>
* be added to the SecurityHeader. See <code>addExternalRefElement()</code>.
*
* </p>
*
* If the <code>dataRef</code> parameter is <code>null</code> the method
* creates and initializes a new Reference element.
*
* @param dataRef
* A <code>xenc:Reference</code> element or <code>null</code>
* @param references
* A vector containing WSEncryptionPart objects
* @return Returns the updated <code>xenc:Reference</code> element
* @throws WSSecurityException
*/
public Element encryptForExternalRef(Element dataRef, Vector references)
throws WSSecurityException {
Vector encDataRefs = doEncryption(document, derivedKeyBytes,
references);
Element referenceList = dataRef;
if (referenceList == null) {
referenceList = document.createElementNS(WSConstants.ENC_NS,
WSConstants.ENC_PREFIX + ":ReferenceList");
}
createDataRefList(document, referenceList, encDataRefs);
return referenceList;
}
/**
* Adds (prepends) the external Reference element to the Security header.
*
* The reference element <i>must</i> be created by the
* <code>encryptForExternalRef() </code> method. The method adds the
* reference element in the SecurityHeader.
*
* @param referenceList
* The external <code>enc:Reference</code> element
* @param secHeader
* The security header.
*/
public void addExternalRefElement(Element referenceList, WSSecHeader secHeader) {
Node node = dkt.getElement().getNextSibling();
if(node == null || (node != null && !(node instanceof Element))) {
//If (at this moment) DerivedKeyToken is the LAST element of
//the security header
secHeader.getSecurityHeader().appendChild(referenceList);
} else {
secHeader.getSecurityHeader().insertBefore(referenceList, node);
}
}
public static Element createDataRefList(Document doc,
Element referenceList, Vector encDataRefs) {
for (int i = 0; i < encDataRefs.size(); i++) {
String dataReferenceUri = (String) encDataRefs.get(i);
Element dataReference = doc.createElementNS(WSConstants.ENC_NS,
WSConstants.ENC_PREFIX + ":DataReference");
dataReference.setAttributeNS(null, "URI", dataReferenceUri);
referenceList.appendChild(dataReference);
}
return referenceList;
}
public void setSymmetricEncAlgorithm(String algo) {
symEncAlgo = algo;
}
/**
* @see org.apache.ws.security.message.WSSecDerivedKeyBase#getDerivedKeyLength()
*/
protected int getDerivedKeyLength() throws WSSecurityException{
return (this.derivedKeyLength > 0) ? this.derivedKeyLength :
WSSecurityUtil.getKeyLength(this.symEncAlgo);
}
}