All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.ws.security.message.WSSecEncrypt Maven / Gradle / Ivy

Go to download

Apache WSS4J is an implementation of the Web Services Security (WS-Security) being developed at OASIS Web Services Security TC. WSS4J is a primarily a Java library that can be used to sign and verify SOAP Messages with WS-Security information. WSS4J will use Apache Axis and Apache XML-Security projects and will be interoperable with JAX-RPC based server/clients and .NET server/clients.

There is a newer version: 1.6.19
Show newest version
/*
 * 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.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.components.crypto.Crypto;
import org.apache.ws.security.message.token.Reference;
import org.apache.ws.security.message.token.SecurityTokenReference;
import org.apache.ws.security.util.Base64;
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.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Vector;

/**
 * Encrypts a parts of a message according to WS Specification, X509 profile,
 * and adds the encryption data.
 * 
 * @author Davanum Srinivas ([email protected]).
 * @author Werner Dittmann ([email protected]).
 */
public class WSSecEncrypt extends WSSecEncryptedKey {
    private static Log log = LogFactory.getLog(WSSecEncrypt.class.getName());

    private static Log tlog = LogFactory.getLog("org.apache.ws.security.TIME");

    protected String symEncAlgo = WSConstants.AES_128;

    protected String encCanonAlgo = null;

    protected byte[] embeddedKey = null;

    protected String embeddedKeyName = null;

    protected boolean useKeyIdentifier;

    /**
     * Symmetric key used in the EncrytpedKey.
     */
    protected SecretKey symmetricKey = null;

    /**
     * SecurityTokenReference to be inserted into EncryptedData/keyInfo element.
     */
    protected SecurityTokenReference securityTokenReference = null;

    /**
     * Indicates whether to encrypt the symmetric key into an EncryptedKey 
     * or not.
     */
    private boolean encryptSymmKey = true;
    
    /**
     * Custom reference value
     */
    private String customReferenceValue;

    /**
     * Constructor.
     */
    public WSSecEncrypt() {
    }

    /**
     * Sets the key to use during embedded encryption.
     * 
     * 

* * @param key * to use during encryption. The key must fit the selected * symmetrical encryption algorithm */ public void setKey(byte[] key) { this.embeddedKey = key; } /** * Sets the algorithm to encode the symmetric key. * * Default is the WSConstants.KEYTRANSPORT_RSA15 algorithm. * * @param keyEnc * specifies the key encoding algorithm. * @see WSConstants#KEYTRANSPORT_RSA15 * @see WSConstants#KEYTRANSPORT_RSAOEP */ public void setKeyEnc(String keyEnc) { keyEncAlgo = keyEnc; } /** * Set the key name for EMBEDDED_KEYNAME * * @param embeddedKeyName */ public void setEmbeddedKeyName(String embeddedKeyName) { this.embeddedKeyName = embeddedKeyName; } /** * Set this true if a key identifier must be used in the KeyInfo * * @param useKeyIdentifier */ public void setUseKeyIdentifier(boolean useKeyIdentifier) { this.useKeyIdentifier = useKeyIdentifier; } /** * Set the name of the symmetric encryption algorithm to use. * * This encryption algorithm is used to encrypt the data. If the algorithm * is not set then AES128 is used. Refer to WSConstants which algorithms are * supported. * * @param algo * Is the name of the encryption algorithm * @see WSConstants#TRIPLE_DES * @see WSConstants#AES_128 * @see WSConstants#AES_192 * @see WSConstants#AES_256 */ public void setSymmetricEncAlgorithm(String algo) { symEncAlgo = algo; } /** * Set the name of an optional canonicalization algorithm to use before * encryption. * * This c14n algorithm is used to serialize the data before encryption. If * the algorithm is not set then a standard serialization is used (provided * by XMLCipher, usually a XMLSerializer according to DOM 3 specification). * * @param algo * Is the name of the canonicalization algorithm */ public void setEncCanonicalization(String algo) { encCanonAlgo = algo; } /** * Get the name of symmetric encryption algorithm to use. * * The name of the encryption algorithm to encrypt the data, i.e. the SOAP * Body. Refer to WSConstants which algorithms are supported. * * @return the name of the currently selected symmetric encryption algorithm * @see WSConstants#TRIPLE_DES * @see WSConstants#AES_128 * @see WSConstants#AES_192 * @see WSConstants#AES_256 */ public String getSymmetricEncAlgorithm() { return symEncAlgo; } /** * Returns if Key Identifiers should be used in KeyInfo * @return */ public boolean getUseKeyIdentifier() { return useKeyIdentifier; } /** * Initialize a WSSec Encrypt. * * The method prepares and initializes a WSSec Encrypt structure after the * relevant information was set. After preparation of the token references * can be added and encrypted. * *

* * This method does not add any element to the security header. This must be * done explicitly. * * @param doc * The SOAP envelope as Document * @param crypto * An instance of the Crypto API to handle keystore and * certificates * @throws WSSecurityException */ public void prepare(Document doc, Crypto crypto) throws WSSecurityException { document = doc; /* * If no external key (symmetricalKey) was set generate an encryption * key (session key) for this Encrypt element. This key will be * encrypted using the public key of the receiver */ if(this.ephemeralKey == null) { if (symmetricKey == null) { KeyGenerator keyGen = getKeyGenerator(); this.symmetricKey = keyGen.generateKey(); } this.ephemeralKey = this.symmetricKey.getEncoded(); } if (this.symmetricKey == null) { this.symmetricKey = WSSecurityUtil.prepareSecretKey(symEncAlgo, this.ephemeralKey); } /* * Get the certificate that contains the public key for the public key * algorithm that will encrypt the generated symmetric (session) key. */ if(this.encryptSymmKey) { X509Certificate remoteCert = null; if (useThisCert != null) { remoteCert = useThisCert; } else { X509Certificate[] certs = crypto.getCertificates(user); if (certs == null || certs.length <= 0) { throw new WSSecurityException(WSSecurityException.FAILURE, "invalidX509Data", new Object[] { "for Encryption" }); } remoteCert = certs[0]; } prepareInternal(this.ephemeralKey, remoteCert, crypto); } } /** * Builds the SOAP envelope with encrypted Body and adds encrypted key. * * This is a convenience method and for backward compatibility. The method * calls the single function methods in order to perform a one shot * encryption. This method is compatible with the build method of the * previous version with the exception of the additional WSSecHeader * parameter. * * @param doc * the SOAP envelope as Document with plain text * Body * @param crypto * an instance of the Crypto API to handle keystore and * Certificates * @param secHeader * the security header element to hold the encrypted key element. * @return the SOAP envelope with encrypted Body as Document * * @throws WSSecurityException */ public Document build(Document doc, Crypto crypto, WSSecHeader secHeader) throws WSSecurityException { doDebug = log.isDebugEnabled(); if (keyIdentifierType == WSConstants.EMBEDDED_KEYNAME || keyIdentifierType == WSConstants.EMBED_SECURITY_TOKEN_REF) { return buildEmbedded(doc, crypto, secHeader); } if (doDebug) { log.debug("Beginning Encryption..."); } prepare(doc, crypto); if (envelope == null) { envelope = document.getDocumentElement(); } 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 refs = encryptForInternalRef(null, parts); addInternalRefElement(refs); prependToHeader(secHeader); if (bstToken != null) { prependBSTElementToHeader(secHeader); } log.debug("Encryption complete."); return doc; } /** * Encrypt one or more parts or elements of the message (internal). * * This method takes a vector of WSEncryptionPart 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 xenc:Reference element. * This method can be called after prepare() and can be * called multiple times to encrypt a number of parts or elements. * *

* * The method generates a xenc:Reference element that must * be added to this token. See addInternalRefElement(). * *

* * If the dataRef parameter is null the method * creates and initializes a new Reference element. * * @param dataRef * A xenc:Reference element or null * @param references * A vector containing WSEncryptionPart objects * @return Returns the updated xenc:Reference element * @throws WSSecurityException */ public Element encryptForInternalRef(Element dataRef, Vector references) throws WSSecurityException { Vector encDataRefs = doEncryption(document, this.symmetricKey, references); Element referenceList = dataRef; if (referenceList == null) { referenceList = document.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":ReferenceList"); } createDataRefList(document, referenceList, encDataRefs); return referenceList; } /** * Encrypt one or more parts or elements of the message (external). * * This method takes a vector of WSEncryptionPart 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 xenc:Reference element. * This method can be called after prepare() and can be * called multiple times to encrypt a number of parts or elements. * *

* * The method generates a xenc:Reference element that must * be added to the SecurityHeader. See addExternalRefElement(). * *

* * If the dataRef parameter is null the method * creates and initializes a new Reference element. * * @param dataRef * A xenc:Reference element or null * @param references * A vector containing WSEncryptionPart objects * @return Returns the updated xenc:Reference element * @throws WSSecurityException */ public Element encryptForExternalRef(Element dataRef, Vector references) throws WSSecurityException { Vector encDataRefs = doEncryption(document, this.symmetricKey, references); Element referenceList = dataRef; if (referenceList == null) { referenceList = document.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":ReferenceList"); } createDataRefList(document, referenceList, encDataRefs); return referenceList; } /** * Adds the internal Reference element to this Encrypt data. * * The reference element must be created by the * encryptForInternalRef() method. The reference element is * added to the EncryptedKey element of this encrypt block. * * @param dataRef * The internal enc:Reference element */ public void addInternalRefElement(Element dataRef) { WSSecurityUtil.appendChildElement(document, encryptedKeyElement, dataRef); } /** * Adds (prepends) the external Reference element to the Security header. * * The reference element must be created by the * encryptForExternalRef() method. The method prepends the * reference element in the SecurityHeader. * * @param dataRef * The external enc:Reference element * @param secHeader * The security header. */ public void addExternalRefElement(Element dataRef, WSSecHeader secHeader) { WSSecurityUtil.prependChildElement(document, secHeader .getSecurityHeader(), dataRef, false); } private Vector doEncryption(Document doc, SecretKey secretKey, Vector references) throws WSSecurityException { KeyInfo keyInfo = null; // Prepare KeyInfo if useKeyIdentifier is set if (useKeyIdentifier && keyIdentifierType == WSConstants.ENCRYPTED_KEY_SHA1_IDENTIFIER) { keyInfo = new KeyInfo(document); SecurityTokenReference secToken = new SecurityTokenReference(document); if(this.customReferenceValue != null) { secToken.setKeyIdentifierEncKeySHA1(this.customReferenceValue); } else { secToken.setKeyIdentifierEncKeySHA1(getSHA1(encryptedEphemeralKey)); } keyInfo.addUnknownElement(secToken.getElement()); } return doEncryption(doc, secretKey, keyInfo, references); } private Vector doEncryption(Document doc, SecretKey secretKey, KeyInfo keyInfo, Vector references) throws WSSecurityException { XMLCipher xmlCipher = null; try { xmlCipher = XMLCipher.getInstance(symEncAlgo); } catch (XMLEncryptionException e3) { throw new WSSecurityException( WSSecurityException.UNSUPPORTED_ALGORITHM, null, null, e3); } Vector encDataRef = new Vector(); boolean cloneKeyInfo = false; 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(document, 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(); encPart.setEncId(xencEncryptedDataId); cloneKeyInfo = true; if(keyInfo == null) { keyInfo = new KeyInfo(document); SecurityTokenReference secToken = new SecurityTokenReference(document); Reference ref = new Reference(document); ref.setURI("#" + encKeyId); secToken.setReference(ref); keyInfo.addUnknownElement(secToken.getElement()); } /* * Forth step: encrypt data, and set necessary attributes in * xenc:EncryptedData */ try { if (modifier.equals("Header")) { Element elem = doc.createElementNS(WSConstants.WSSE11_NS,"wsse11:"+WSConstants.ENCRYPTED_HEADER); WSSecurityUtil.setNamespace(elem, WSConstants.WSSE11_NS, WSConstants.WSSE11_PREFIX); String wsuPrefix = WSSecurityUtil.setNamespace(elem, WSConstants.WSU_NS, WSConstants.WSU_PREFIX); elem.setAttributeNS(WSConstants.WSU_NS, wsuPrefix + ":Id", "EncHeader-" + body.hashCode()); NamedNodeMap map = body.getAttributes(); for (int i = 0 ; i < map.getLength() ; i++) { Attr attr = (Attr)map.item(i); if (attr.getNamespaceURI().equals(WSConstants.URI_SOAP11_ENV) || attr.getNamespaceURI().equals(WSConstants.URI_SOAP12_ENV)) { String soapEnvPrefix = WSSecurityUtil.setNamespace(elem, attr.getNamespaceURI(), "soapevn"); elem.setAttributeNS(attr.getNamespaceURI(), soapEnvPrefix +":"+attr.getLocalName(), attr.getValue()); } } xmlCipher.init(XMLCipher.ENCRYPT_MODE, secretKey); EncryptedData encData = xmlCipher.getEncryptedData(); encData.setId(xencEncryptedDataId); encData.setKeyInfo(keyInfo); xmlCipher.doFinal(doc, body, content); Element encDataElem = WSSecurityUtil.findElementById(document .getDocumentElement(), xencEncryptedDataId, null); Node clone = encDataElem.cloneNode(true); elem.appendChild(clone); encDataElem.getParentNode().appendChild(elem); encDataElem.getParentNode().removeChild(encDataElem); } else { xmlCipher.init(XMLCipher.ENCRYPT_MODE, secretKey); EncryptedData encData = xmlCipher.getEncryptedData(); encData.setId(xencEncryptedDataId); encData.setKeyInfo(keyInfo); xmlCipher.doFinal(doc, body, content); } if(cloneKeyInfo) { keyInfo = new KeyInfo((Element) keyInfo.getElement() .cloneNode(true), null); } } catch (Exception e2) { throw new WSSecurityException( WSSecurityException.FAILED_ENCRYPTION, null, null, e2); } encDataRef.add(new String("#" + xencEncryptedDataId)); } return encDataRef; } private Document buildEmbedded(Document doc, Crypto crypto, WSSecHeader secHeader) throws WSSecurityException { doDebug = log.isDebugEnabled(); if (doDebug) { log.debug("Beginning Encryption embedded..."); } envelope = doc.getDocumentElement(); envelope.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:" + WSConstants.ENC_PREFIX, WSConstants.ENC_NS); /* * Second step: generate a symmetric key from the specified key * (password) for this algorithm, and set the cipher into encryption * mode. */ if (this.symmetricKey == null) { if (embeddedKey == null) { throw new WSSecurityException(WSSecurityException.FAILURE, "noKeySupplied"); } this.symmetricKey = WSSecurityUtil.prepareSecretKey(symEncAlgo, embeddedKey); } KeyInfo keyInfo = null; if (this.keyIdentifierType == WSConstants.EMBEDDED_KEYNAME) { keyInfo = new KeyInfo(doc); keyInfo .addKeyName(embeddedKeyName == null ? user : embeddedKeyName); } else if (this.keyIdentifierType == WSConstants.EMBED_SECURITY_TOKEN_REF) { /* * This means that we want to embed a * into keyInfo element. If we need this functionality, this.secRef * MUST be set before calling the build(doc, crypto) method. So if * secRef is null then throw an exception. */ if (this.securityTokenReference == null) { throw new WSSecurityException( WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, "You must set keyInfo element, if the keyIdentifier " + "== EMBED_SECURITY_TOKEN_REF"); } else { keyInfo = new KeyInfo(doc); Element tmpE = securityTokenReference.getElement(); tmpE.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:" + tmpE.getPrefix(), tmpE.getNamespaceURI()); keyInfo.addUnknownElement(securityTokenReference.getElement()); } } SOAPConstants soapConstants = WSSecurityUtil.getSOAPConstants(envelope); if (parts == null) { parts = new Vector(); WSEncryptionPart encP = new WSEncryptionPart(soapConstants .getBodyQName().getLocalPart(), soapConstants .getEnvelopeURI(), "Content"); parts.add(encP); } Vector encDataRefs = doEncryption(doc, this.symmetricKey, keyInfo, parts); /* * At this point data is encrypted with the symmetric key and can be * referenced via the above Id */ /* * Now we need to setup the wsse:Security header block 1) get (or * create) the wsse:Security header block 2) The last step sets up the * reference list that pints to the encrypted data */ Element wsseSecurity = secHeader.getSecurityHeader(); Element referenceList = doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":ReferenceList"); referenceList = createDataRefList(doc, referenceList, encDataRefs); WSSecurityUtil.prependChildElement(doc, wsseSecurity, referenceList, true); return doc; } private KeyGenerator getKeyGenerator() throws WSSecurityException { KeyGenerator keyGen = null; try { /* * Assume AES as default, so initialize it */ keyGen = KeyGenerator.getInstance("AES"); if (symEncAlgo.equalsIgnoreCase(WSConstants.TRIPLE_DES)) { keyGen = KeyGenerator.getInstance("DESede"); } else if (symEncAlgo.equalsIgnoreCase(WSConstants.AES_128)) { keyGen.init(128); } else if (symEncAlgo.equalsIgnoreCase(WSConstants.AES_192)) { keyGen.init(192); } else if (symEncAlgo.equalsIgnoreCase(WSConstants.AES_256)) { keyGen.init(256); } else { return null; } } catch (NoSuchAlgorithmException e) { throw new WSSecurityException( WSSecurityException.UNSUPPORTED_ALGORITHM, null, null, e); } return keyGen; } /** * Create DOM subtree for xenc:EncryptedKey * * @param doc * the SOAP envelope parent document * @param keyTransportAlgo * specifies which algorithm to use to encrypt the symmetric key * @return an xenc:EncryptedKey element */ 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; } /** * @return The symmetric key */ public SecretKey getSymmetricKey() { return symmetricKey; } /** * Set the symmetric key to be used for encryption * * @param key */ public void setSymmetricKey(SecretKey key) { this.symmetricKey = key; } /** * @return Return the SecurityTokenRefernce */ public SecurityTokenReference getSecurityTokenReference() { return securityTokenReference; } /** * @param reference */ public void setSecurityTokenReference(SecurityTokenReference reference) { securityTokenReference = reference; } public boolean isEncryptSymmKey() { return encryptSymmKey; } public void setEncryptSymmKey(boolean encryptSymmKey) { this.encryptSymmKey = encryptSymmKey; } private String getSHA1(byte[] input) throws WSSecurityException { try { MessageDigest sha = null; sha = MessageDigest.getInstance("SHA-1"); sha.reset(); sha.update(input); byte[] data = sha.digest(); return Base64.encode(data); } catch (NoSuchAlgorithmException e) { throw new WSSecurityException( WSSecurityException.UNSUPPORTED_ALGORITHM, null, null, e); } } public void setCustomReferenceValue(String customReferenceValue) { this.customReferenceValue = customReferenceValue; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy