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

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

Go to download

The Apache WSS4J project provides a Java implementation of the primary security standards for Web Services, namely the OASIS Web Services Security (WS-Security) specifications from the OASIS Web Services Security TC.

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 java.security.InvalidKeyException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ws.security.WSConstants;
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.UUIDGenerator;
import org.apache.ws.security.util.WSSecurityUtil;
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;

/**
 * Builder class to build an EncryptedKey.
 * 
 * This is expecially useful in the case where the same
 * EncryptedKey has to be used to sign and encrypt the message In
 * such a situation this builder will add the EncryptedKey to the
 * security header and we can use the information form the builder to provide to
 * other builders to reference to the token
 */
public class WSSecEncryptedKey extends WSSecBase {

    private static Log log = LogFactory.getLog(WSSecEncryptedKey.class
            .getName());

    protected Document document;

    /**
     * soap:Envelope element
     */
    protected Element envelope = null;

    /**
     * Session key used as the secret in key derivation
     */
    protected byte[] ephemeralKey;

    /**
     * Encrypted bytes of the ephemeral key
     */
    protected byte[] encryptedEphemeralKey;
    
    /**
     * Remote user's alias to obtain the cert to encrypt the ephemeral key
     */
    protected String encrUser = null;

    /**
     * Algorithm used to encrypt the ephemeral key
     */
    protected String keyEncAlgo = WSConstants.KEYTRANSPORT_RSA15;

    /**
     * xenc:EncryptedKey element
     */
    protected Element encryptedKeyElement = null;

    /**
     * The Token identifier of the token that the DerivedKeyToken
     * is (or to be) derived from.
     */
    protected String encKeyId = null;

    /**
     * BinarySecurityToken to be included in the case where BST_DIRECT_REFERENCE
     * is used to refer to the asymm encryption cert
     */
    protected BinarySecurity bstToken = null;
    
    protected X509Certificate useThisCert = null;
    
    /**
     * Key size in bits
     * Defaults to 128
     */
    protected int keySize = 128;

    /**
     * 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
     */
    public void setUserInfo(String user) {
        this.user = user;
    }

    /**
     * Get the id generated during prepare().
     * 
     * Returns the the value of wsu:Id attribute of the EncryptedKey element.
     * 
     * @return Return the wsu:Id of this token or null if prepare()
     *         was not called before.
     */
    public String getId() {
        return encKeyId;
    }

    /**
     * Prepare the ephemeralKey and the tokens required to be added to the
     * security header
     * 
     * @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;

        /*
         * Set up the ephemeral key
         */
        if (this.ephemeralKey == null) {
            this.ephemeralKey = generateEphemeralKey();
        }

        /*
         * Get the certificate that contains the public key for the public key
         * algorithm that will encrypt the generated symmetric (session) key.
         */
        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(ephemeralKey, remoteCert, crypto);
    }

    /**
     * Encrypt the symmetric key data and prepare the EncryptedKey element
     * 
     * This method does the most work for to prepare the EncryptedKey element.
     * It is also used by the WSSecEncrypt sub-class.
     * 
     * @param keyBytes
     *            The bytes that represent the symmetric key
     * @param remoteCert
     *            The certificate that contains the public key to encrypt the
     *            seymmetric key data
     * @param crypto
     *            An instance of the Crypto API to handle keystore and
     *            certificates
     * @throws WSSecurityException
     */
    protected void prepareInternal(byte[] keyBytes, X509Certificate remoteCert,
            Crypto crypto) throws WSSecurityException {
        String certUri = UUIDGenerator.getUUID();
        Cipher cipher = WSSecurityUtil.getCipherInstance(keyEncAlgo);
        try {
            cipher.init(Cipher.ENCRYPT_MODE, remoteCert.getPublicKey());
        } catch (InvalidKeyException e) {
            throw new WSSecurityException(WSSecurityException.FAILED_ENCRYPTION,
                    null, null, e);
        }
        if (doDebug) {
            log.debug("cipher blksize: " + cipher.getBlockSize()
                    + ", symm key length: " + keyBytes.length);
        }
        int blockSize = cipher.getBlockSize();
        if (blockSize > 0 && blockSize < keyBytes.length) {
            throw new WSSecurityException(WSSecurityException.FAILURE,
                    "unsupportedKeyTransp",
                    new Object[] { "public key algorithm too weak to encrypt "
                            + "symmetric key" });
        }
        
        try {
            this.encryptedEphemeralKey = cipher.doFinal(keyBytes);
        } 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(document,
                this.encryptedEphemeralKey);

        /*
         * Now we need to setup the EncryptedKey header block 1) create a
         * EncryptedKey element and set a wsu:Id for it 2) Generate ds:KeyInfo
         * element, this wraps the wsse:SecurityTokenReference 3) Create and set
         * up the SecurityTokenReference according to the keyIdentifer parameter
         * 4) Create the CipherValue element structure and insert the encrypted
         * session key
         */
        encryptedKeyElement = createEnrcyptedKey(document, keyEncAlgo);
        if(this.encKeyId == null || "".equals(this.encKeyId)) {
            this.encKeyId = "EncKeyId-" + UUIDGenerator.getUUID();
        }
        encryptedKeyElement.setAttributeNS(null, "Id", this.encKeyId);

        KeyInfo keyInfo = new KeyInfo(document);

        SecurityTokenReference secToken = new SecurityTokenReference(document);

        switch (keyIdentifierType) {
        case WSConstants.X509_KEY_IDENTIFIER:
            secToken.setKeyIdentifier(remoteCert);
            break;

        case WSConstants.SKI_KEY_IDENTIFIER:
            secToken.setKeyIdentifierSKI(remoteCert, crypto);
            break;

        case WSConstants.THUMBPRINT_IDENTIFIER:
            secToken.setKeyIdentifierThumb(remoteCert);
            break;
            
        case WSConstants.ENCRYPTED_KEY_SHA1_IDENTIFIER:
            //
            // This identifier is not applicable for this case, so fall back to
            // ThumbprintRSA.
            //
            secToken.setKeyIdentifierThumb(remoteCert);
            break;

        case WSConstants.ISSUER_SERIAL:
            XMLX509IssuerSerial data = new XMLX509IssuerSerial(document,
                    remoteCert);
            X509Data x509Data = new X509Data(document);
            x509Data.add(data);
            secToken.setX509IssuerSerial(x509Data);
            break;

        case WSConstants.BST_DIRECT_REFERENCE:
            Reference ref = new Reference(document);
            ref.setURI("#" + certUri);
            bstToken = new X509Security(document);
            ((X509Security) bstToken).setX509Certificate(remoteCert);
            bstToken.setID(certUri);
            ref.setValueType(bstToken.getValueType());
            secToken.setReference(ref);
            break;

        default:
            throw new WSSecurityException(WSSecurityException.FAILURE,
                    "unsupportedKeyId");
        }
        keyInfo.addUnknownElement(secToken.getElement());
        WSSecurityUtil.appendChildElement(document, encryptedKeyElement,
                keyInfo.getElement());

        Element xencCipherValue = createCipherValue(document,
                encryptedKeyElement);
        xencCipherValue.appendChild(keyText);

        envelope = document.getDocumentElement();
        envelope.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:"
                + WSConstants.ENC_PREFIX, WSConstants.ENC_NS);
    }

    /**
     * Create an ephemeral key
     * 
     * @return
     * @throws WSSecurityException
     */
    protected byte[] generateEphemeralKey() throws WSSecurityException {
        try {     
            final SecureRandom r = WSSecurityUtil.resolveSecureRandom();
            if (r == null) {
                throw new WSSecurityException("Random generator is not initialzed.");
            }
            byte[] temp = new byte[this.keySize / 8];
            r.nextBytes(temp);
            return temp;
        } catch (Exception e) {
            throw new WSSecurityException(
                    "Error in creating the ephemeral key", e);
        }
    }

    /**
     * Create DOM subtree for xenc:EncryptedKey
     * 
     * @param doc
     *            the SOAP enevelope parent document
     * @param keyTransportAlgo
     *            specifies which alogrithm to use to encrypt the symmetric key
     * @return an xenc:EncryptedKey element
     */

    protected Element createEnrcyptedKey(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);
        WSSecurityUtil.appendChildElement(doc, encryptedKey, encryptionMethod);
        return encryptedKey;
    }

    protected 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);
        WSSecurityUtil.appendChildElement(doc, encryptedKey, cipherData);
        return cipherValue;
    }

    /**
     * Prepend the EncryptedKey element to the elements already in the Security
     * header.
     * 
     * The method can be called any time after prepare(). This
     * allows to insert the EncryptedKey element at any position in the Security
     * header.
     * 
     * @param secHeader
     *            The security header that holds the Signature element.
     */
    public void prependToHeader(WSSecHeader secHeader) {
        WSSecurityUtil.prependChildElement(document, secHeader
                .getSecurityHeader(), encryptedKeyElement, false);
    }

    /**
     * Append the EncryptedKey element to the elements already in the Security
     * header.
     * 
     * The method can be called any time after prepare(). This
     * allows to insert the EncryptedKey element at any position in the Security
     * header.
     * 
     * @param secHeader
     *            The security header that holds the Signature element.
     */
    public void appendToHeader(WSSecHeader secHeader) {
        WSSecurityUtil.appendChildElement(document, secHeader
                .getSecurityHeader(), encryptedKeyElement);
    }
    
    /**
     * Prepend the BinarySecurityToken to the elements already in the Security
     * header.
     * 
     * The method can be called any time after prepare(). This
     * allows to insert the BST element at any position in the Security header.
     * 
     * @param secHeader
     *            The security header that holds the BST element.
     */
    public void prependBSTElementToHeader(WSSecHeader secHeader) {
        if (bstToken != null) {
            WSSecurityUtil.prependChildElement(document, secHeader
                    .getSecurityHeader(), bstToken.getElement(), false);
        }
        bstToken = null;
    }

    /**
     * Append the BinarySecurityToken to the elements already in the Security
     * header.
     * 
     * The method can be called any time after prepare(). This
     * allows to insert the BST element at any position in the Security header.
     * 
     * @param secHeader
     *            The security header that holds the BST element.
     */
    public void appendBSTElementToHeader(WSSecHeader secHeader) {
        if (bstToken != null) {
            WSSecurityUtil.appendChildElement(document, secHeader
                    .getSecurityHeader(), bstToken.getElement());
        }
        bstToken = null;
    }
    
    /**
     * @return Returns the ephemeralKey.
     */
    public byte[] getEphemeralKey() {
        return ephemeralKey;
    }
    
    /**
     * 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
     */
    public void setUseThisCert(X509Certificate cert) {
        useThisCert = cert;
    }

    /**
     * @return Returns the encryptedKeyElement.
     */
    public Element getEncryptedKeyElement() {
        return encryptedKeyElement;
    }
    
    /**
     * Set the encrypted key element when a pre prepared encrypted key is used
     * @param encryptedKeyElement EncryptedKey element of the encrypted key used
     */
    public void setEncryptedKeyElement(Element encryptedKeyElement) {
        this.encryptedKeyElement = encryptedKeyElement;
    }
    
    /**
     * @return Returns the BinarySecurityToken element.
     */
    public Element getBinarySecurityTokenElement() {
        if(this.bstToken != null) {
            return this.bstToken.getElement();
        } else  {
            return null;
        }
    }

    public void setKeySize(int keySize) throws WSSecurityException {
        if(keySize < 64) {
            //Minimum size has to be 64 bits - E.g. A DES key
            throw new WSSecurityException("invalidKeySize");
        }
        this.keySize = keySize;
    }

    public void setKeyEncAlgo(String keyEncAlgo) {
        this.keyEncAlgo = keyEncAlgo;
    }

    /**
     * @param ephemeralKey The ephemeralKey to set.
     */
    public void setEphemeralKey(byte[] ephemeralKey) {
        this.ephemeralKey = ephemeralKey;
    }
    
    /**
     * Get the id of the BSt generated  during prepare().
     * 
     * @return Returns the the value of wsu:Id attribute of the 
     * BinaruSecurityToken element.
     */
    public String getBSTTokenId() {
        if(this.bstToken == null) {
            return null;
        }
        
        return this.bstToken.getID();
    }

    /**
     * @param document The document to set.
     */
    public void setDocument(Document document) {
        this.document = document;
    }

    /**
     * @param encKeyId The encKeyId to set.
     */
    public void setEncKeyId(String encKeyId) {
        this.encKeyId = encKeyId;
    }
    
    public boolean isCertSet() {
        return (useThisCert == null ? true : false) ;
    }

    public byte[] getEncryptedEphemeralKey() {
        return encryptedEphemeralKey;
    }
    
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy