org.apache.ws.security.message.WSSecEncryptedKey Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of wss4j Show documentation
Show all versions of wss4j Show documentation
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.
/*
* 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;
}
}