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

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

/**
 * 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.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.BinarySecurity;
import org.apache.ws.security.message.token.Reference;
import org.apache.ws.security.message.token.SecurityTokenReference;
import org.apache.ws.security.message.token.X509Security;
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.apache.xml.security.keys.content.X509Data;
import org.apache.xml.security.keys.content.x509.XMLX509IssuerSerial;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Vector;

/**
 * Encrypts a SOAP body inside a SOAP envelope according to WS Specification,
 * X509 profile, and adds the encryption data.
 * 

* * @author Davanum Srinivas ([email protected]). * @author Werner Dittmann ([email protected]). */ public class WSEncryptBody extends WSBaseMessage { private static Log log = LogFactory.getLog(WSEncryptBody.class.getName()); private static Log tlog = LogFactory.getLog("org.apache.ws.security.TIME"); protected String symEncAlgo = WSConstants.AES_128; protected String keyEncAlgo = WSConstants.KEYTRANSPORT_RSA15; protected String encCanonAlgo = null; protected byte[] embeddedKey = null; protected String embeddedKeyName = null; protected X509Certificate useThisCert = null; /** * Symmetric key used in the EncrytpedKey. */ protected SecretKey symmetricKey = null; /** * Symmetric key that's actually used. */ protected SecretKey encryptionKey = null; /** * Parent node to which the EncryptedKeyElement should be added. */ protected Element parentNode = null; /** * SecurityTokenReference to be inserted into EncryptedData/keyInfo element. */ protected SecurityTokenReference securityTokenReference = null; /** * Constructor. * @deprecated replaced by {@link WSSecEncrypt#WSSecEncrypt()} */ public WSEncryptBody() { } /** * Constructor. *

* * @param actor The actor name of the wsse:Security * header * * @deprecated replaced by {@link WSSecEncrypt#WSSecEncrypt()} * and {@link WSSecHeader} for actor specification. */ public WSEncryptBody(String actor) { super(actor); } /** * Constructor. *

* * @param actor The actor name of the wsse:Security header * @param mu Set mustUnderstand to true or false * * @deprecated replaced by {@link WSSecEncrypt#WSSecEncrypt()} * and {@link WSSecHeader} for actor and mustunderstand * specification. */ public WSEncryptBody(String actor, boolean mu) { super(actor, mu); } /** * Sets the key to use during embedded encryption. *

* * @param key to use during encryption. The key must fit the * selected symmetrical encryption algorithm * @deprecated replaced by * {@link WSSecEncrypt#setKey(byte[])} */ 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 * @deprecated replaced by * {@link WSSecEncrypt#setKeyEnc(String)} */ public void setKeyEnc(String keyEnc) { keyEncAlgo = keyEnc; } /** * Set the user name to get the encryption certificate. The public * key of this certificate is used, thus no password necessary. * The user name is a keystore alias usually. *

* * @param user * @deprecated replaced by * {@link WSSecEncrypt#setUserInfo(String)} */ public void setUserInfo(String user) { this.user = user; } /** * Set the key name for EMBEDDED_KEYNAME * * @param embeddedKeyName * @deprecated replaced by * {@link WSSecEncrypt#setEmbeddedKeyName(String)} */ public void setEmbeddedKeyName(String embeddedKeyName) { this.embeddedKeyName = embeddedKeyName; } /** * Set the X509 Certificate to use for encryption. * If this is set and the key identifier is set * to DirectReference then use this certificate * to get the public key for encryption. * * @param cert is the X509 certificate to use for encryption * @deprecated replaced by * {@link WSSecEncrypt#setUseThisCert(X509Certificate)} */ public void setUseThisCert(X509Certificate cert) { useThisCert = cert; } /** * Set the name of the symmetric encryption algorithm to use. *

* This encryption alogrithm is used to encrypt * the data, i.e. the SOAP Body. If the algorithm * is not set then Triple DES 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 * @deprecated replaced by * {@link WSSecEncrypt#setSymmetricEncAlgorithm(String)} */ public void setSymmetricEncAlgorithm(String algo) { symEncAlgo = algo; } /** * Set the name of an optional canonicalization algorithm to use * before encryption. *

* This c14n alogrithm is used to serialize the data before * encryption, i.e. the SOAP Body. 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 * @deprecated replaced by * {@link WSSecEncrypt#setEncCanonicalization(String)} */ public void setEncCanonicalization(String algo) { encCanonAlgo = algo; } /** * Get the name of symmetric encryption algorithm to use. *

* The name of the encryption alogrithm 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 * @deprecated replaced by * {@link WSSecEncrypt#getSymmetricEncAlgorithm()} */ public String getSymmetricEncAlgorithm() { return symEncAlgo; } /** * Builds the SOAP envelope with encrypted Body and adds encrypted key. *

* This function performs several steps: *

*

    *
  • First step: set the encoding namespace in the SOAP:Envelope
  • *
  • Second step: generate a symmetric key (session key) for * the selected symmetric encryption algorithm, and set the cipher * into encryption mode. *
  • *
  • Third step: get the data to encrypt. * We always encrypt the complete first child element of * the SOAP Body element *
  • *
  • Forth step: encrypt data, and set neccessary attributes in * xenc:EncryptedData *
  • *
  • Fifth step: get the certificate that contains the public key for * the public key algorithm that will encrypt the generated symmetric * (session) key. Up to now we support RSA 1-5 as public key * algorithm. *
  • *
  • Sixth step: setup the wsse:Security header block
  • *
* * @param doc the SOAP envelope as Document with * plaintext Body * @param crypto an instance of the Crypto API to handle keystore and * Certificates * @return the SOAP envelope with encrypted Body as Document * * @throws WSSecurityException * @deprecated replaced by * {@link WSSecEncrypt#build(Document, Crypto, WSSecHeader)} */ public Document build(Document doc, Crypto crypto) throws WSSecurityException { doDebug = log.isDebugEnabled(); if (keyIdentifierType == WSConstants.EMBEDDED_KEYNAME || keyIdentifierType == WSConstants.EMBED_SECURITY_TOKEN_REF) { return buildEmbedded(doc); } long t0 = 0, t1 = 0, t2 = 0, t3 = 0; if (tlog.isDebugEnabled()) { t0 = System.currentTimeMillis(); } if (doDebug) { log.debug("Beginning Encryption..."); } /* * Second step: generate a symmetric key (session key) for * this alogrithm, and set the cipher into encryption mode. */ // This variable is made a classs attribute :: SecretKey symmetricKey = null; this.encryptionKey = this.symmetricKey; if (encryptionKey == null) { KeyGenerator keyGen = getKeyGenerator(); this.encryptionKey = keyGen.generateKey(); } Vector encDataRefs = doEncryption(doc, this.encryptionKey); if (tlog.isDebugEnabled()) { t1 = System.currentTimeMillis(); } /* * At this point data is encrypted with the symmetric key and can be * referenced via the above Id */ /* * Fifth step: get the certificate that contains the public key for the * public key algorithm that will encrypt * the generated symmetric (session) key. * Up to now we support RSA 1-5 as public key algorithm */ 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, "noUserCertsFound", new Object[] { user, "encryption" } ); } remoteCert = certs[0]; } if (tlog.isDebugEnabled()) { t2 = System.currentTimeMillis(); } Cipher cipher = WSSecurityUtil.getCipherInstance(keyEncAlgo); try { cipher.init(Cipher.ENCRYPT_MODE, remoteCert); } catch (InvalidKeyException e) { throw new WSSecurityException(WSSecurityException.FAILED_ENCRYPTION, null, null, e); } byte[] encKey = this.encryptionKey.getEncoded(); if (doDebug) { log.debug("cipher blksize: " + cipher.getBlockSize() + ", symm key length: " + encKey.length); } if (cipher.getBlockSize() < encKey.length) { throw new WSSecurityException(WSSecurityException.FAILURE, "unsupportedKeyTransp", new Object[]{"public key algorithm too weak to encrypt symmetric key"}); } byte[] encryptedKey = null; try { encryptedKey = cipher.doFinal(encKey); } catch (IllegalStateException e1) { throw new WSSecurityException(WSSecurityException.FAILED_ENCRYPTION, null, null, e1); } catch (IllegalBlockSizeException e1) { throw new WSSecurityException(WSSecurityException.FAILED_ENCRYPTION, null, null, e1); } catch (BadPaddingException e1) { throw new WSSecurityException(WSSecurityException.FAILED_ENCRYPTION, null, null, e1); } Text keyText = WSSecurityUtil.createBase64EncodedTextNode(doc, encryptedKey); /* * Now we need to setup the wsse:Security header block * 1) get (or create) the wsse:Security header block * 2) create the xenc:EncryptedKey element. This already includes * the ExcrpytionMethod element with attributes that define * the key transport encryption algorithm * 3) Generate ds:KeyInfo element, this wraps the wsse:SecurityTokenReference * 4) set up the SecurityTokenReference, either with KeyIdentifier or * X509IssuerSerial. The SecTokenRef defines how to get to security * token used to encrypt the session key (this security token usually * contains a public key) * 5) Create the CipherValue element structure and insert the encrypted * session key * 6) The last step sets up the reference list that pints to the encrypted * data that was encrypted with this encrypted session key :-) */ Element wsseSecurity = insertSecurityHeader(doc); Element xencEncryptedKey = createEncryptedKey(doc, keyEncAlgo); if (parentNode == null) { WSSecurityUtil.prependChildElement(wsseSecurity, xencEncryptedKey); } else { WSSecurityUtil.prependChildElement(parentNode, xencEncryptedKey); } SecurityTokenReference secToken = new SecurityTokenReference(doc); switch (keyIdentifierType) { case WSConstants.X509_KEY_IDENTIFIER: secToken.setKeyIdentifier(remoteCert); // build a key id class?? break; case WSConstants.SKI_KEY_IDENTIFIER: secToken.setKeyIdentifierSKI(remoteCert, crypto); break; case WSConstants.THUMBPRINT_IDENTIFIER: secToken.setKeyIdentifierThumb(remoteCert); break; case WSConstants.ISSUER_SERIAL: XMLX509IssuerSerial data = new XMLX509IssuerSerial(doc, remoteCert); X509Data x509Data = new X509Data(doc); x509Data.add(data); secToken.setX509IssuerSerial(x509Data); break; case WSConstants.BST_DIRECT_REFERENCE: Reference ref = new Reference(doc); String certUri = wssConfig.getIdAllocator().createId("EncCertId-", remoteCert); ref.setURI("#" + certUri); BinarySecurity bstToken = null; bstToken = new X509Security(doc); ((X509Security) bstToken).setX509Certificate(remoteCert); bstToken.setID(certUri); ref.setValueType(bstToken.getValueType()); secToken.setReference(ref); WSSecurityUtil.prependChildElement(wsseSecurity, bstToken.getElement()); break; default : throw new WSSecurityException(WSSecurityException.FAILURE, "unsupportedKeyId"); } KeyInfo keyInfo = new KeyInfo(doc); keyInfo.addUnknownElement(secToken.getElement()); xencEncryptedKey.appendChild(keyInfo.getElement()); Element xencCipherValue = createCipherValue(doc, xencEncryptedKey); xencCipherValue.appendChild(keyText); createDataRefList(doc, xencEncryptedKey, encDataRefs); log.debug("Encryption complete."); if (tlog.isDebugEnabled()) { t3 = System.currentTimeMillis(); tlog.debug("EncryptBody: symm-enc " + (t1 - t0) + " cert " + (t2 - t1) + " key-encrypt " + (t3 - t2)); } return doc; } private Vector doEncryption(Document doc, SecretKey encryptKey) throws WSSecurityException { return doEncryption(doc, encryptKey, null); } private Vector doEncryption(Document doc, SecretKey encryptKey, KeyInfo keyInfo) throws WSSecurityException { /* * First step: set the encryption encoding namespace in the SOAP:Envelope */ Element envelope = doc.getDocumentElement(); envelope.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:" + WSConstants.ENC_PREFIX, WSConstants.ENC_NS); SOAPConstants soapConstants = WSSecurityUtil.getSOAPConstants(envelope); XMLCipher xmlCipher = null; try { xmlCipher = XMLCipher.getInstance(symEncAlgo); } catch (XMLEncryptionException e3) { throw new WSSecurityException(WSSecurityException.UNSUPPORTED_ALGORITHM, null, null, e3); } // if no encryption parts set - use the default if (parts == null) { parts = new Vector(); WSEncryptionPart encP = new WSEncryptionPart(soapConstants.getBodyQName().getLocalPart(), soapConstants.getEnvelopeURI(), "Content"); parts.add(encP); } Vector encDataRefs = new Vector(); for (int part = 0; part < parts.size(); part++) { WSEncryptionPart encPart = (WSEncryptionPart) parts.get(part); String elemName = encPart.getName(); String nmSpace = encPart.getNamespace(); String modifier = encPart.getEncModifier(); /* * Third step: get the data to encrypt. */ Element 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 = wssConfig.getIdAllocator().createId("EncDataId-", body); /* * Forth step: encrypt data, and set neccessary attributes in * xenc:EncryptedData */ try { xmlCipher.init(XMLCipher.ENCRYPT_MODE, encryptKey); 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; } private Document buildEmbedded(Document doc) throws WSSecurityException { doDebug = log.isDebugEnabled(); long t0 = 0, t1 = 0; if (tlog.isDebugEnabled()) { t0 = System.currentTimeMillis(); } if (doDebug) { log.debug("Beginning Encryption embedded..."); } /* * Second step: generate a symmetric key from the specified * key (password) for this algorithm, and set the cipher into * encryption mode. */ this.encryptionKey = this.symmetricKey; if (this.encryptionKey == null) { if (embeddedKey == null) { throw new WSSecurityException(WSSecurityException.FAILURE, "noKeySupplied"); } this.encryptionKey = 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()); } } Vector encDataRefs = doEncryption(doc, this.encryptionKey, keyInfo); /* * 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 that was encrypted with this encrypted session key :-) */ Element wsseSecurity = insertSecurityHeader(doc); Element tmpE = doc.createElement("temp"); Element refList = createDataRefList(doc, tmpE, encDataRefs); WSSecurityUtil.prependChildElement(wsseSecurity, refList); if (tlog.isDebugEnabled()) { tlog.debug("EncryptBody embedded: symm-enc " + (t1 - t0)); } 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 createEncryptedKey(Document doc, String keyTransportAlgo) { Element encryptedKey = doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":EncryptedKey"); WSSecurityUtil.setNamespace(encryptedKey, WSConstants.ENC_NS, WSConstants.ENC_PREFIX); Element encryptionMethod = doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":EncryptionMethod"); encryptionMethod.setAttributeNS(null, "Algorithm", keyTransportAlgo); encryptedKey.appendChild(encryptionMethod); return encryptedKey; } /** * 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 * @deprecated use createEncryptedKey(Document doc, String keyTransportAlgo) instead */ public static Element createEnrcyptedKey(Document doc, String keyTransportAlgo) { return createEncryptedKey(doc, keyTransportAlgo); } public static Element createCipherValue(Document doc, Element encryptedKey) { Element cipherData = doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":CipherData"); Element cipherValue = doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":CipherValue"); cipherData.appendChild(cipherValue); encryptedKey.appendChild(cipherData); return cipherValue; } public static Element createDataRefList(Document doc, Element encryptedKey, Vector encDataRefs) { Element referenceList = doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":ReferenceList"); 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); } encryptedKey.appendChild(referenceList); return referenceList; } /** * Sets the parent node of the EncryptedKeyElement * * @param element * @deprecated */ public void setParentNode(Element element) { parentNode = element; } /** * @return TODO * @deprecated replaced by * {@link WSSecEncrypt#getSymmetricKey()} */ public SecretKey getSymmetricKey() { return symmetricKey; } /** * Set the symmetric key to be used for encryption * @param key * @deprecated replaced by * {@link WSSecEncrypt#setSymmetricKey(SecretKey)} */ public void setSymmetricKey(SecretKey key) { this.symmetricKey = key; } /** * Get the symmetric key used for encryption. This may be the same as * the symmetric key field. * * @return The symmetric key * @deprecated replaced by * {@link WSSecEncrypt#getEncryptedEphemeralKey()} */ public SecretKey getEncryptionKey() { return this.encryptionKey; } /** * @return TODO * @deprecated replaced by * {@link WSSecEncrypt#getSecurityTokenReference()} */ public SecurityTokenReference getSecurityTokenReference() { return securityTokenReference; } /** * @param reference * @deprecated replaced by * {@link WSSecEncrypt#setSecurityTokenReference(SecurityTokenReference)} */ public void setSecurityTokenReference(SecurityTokenReference reference) { securityTokenReference = reference; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy