org.opensaml.xmlsec.encryption.support.Encrypter Maven / Gradle / Ivy
/*
* Licensed to the University Corporation for Advanced Internet Development,
* Inc. (UCAID) under one or more contributor license agreements. See the
* NOTICE file distributed with this work for additional information regarding
* copyright ownership. The UCAID 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.opensaml.xmlsec.encryption.support;
import java.security.Key;
import java.security.KeyException;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.ECPublicKey;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.crypto.SecretKey;
import net.shibboleth.utilities.java.support.codec.Base64Support;
import net.shibboleth.utilities.java.support.logic.Constraint;
import org.apache.xml.security.Init;
import org.apache.xml.security.encryption.XMLCipher;
import org.apache.xml.security.encryption.XMLEncryptionException;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.core.xml.XMLObjectBuilderFactory;
import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
import org.opensaml.core.xml.io.Marshaller;
import org.opensaml.core.xml.io.MarshallingException;
import org.opensaml.core.xml.io.Unmarshaller;
import org.opensaml.core.xml.io.UnmarshallerFactory;
import org.opensaml.core.xml.io.UnmarshallingException;
import org.opensaml.security.SecurityException;
import org.opensaml.security.credential.CredentialSupport;
import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
import org.opensaml.xmlsec.encryption.EncryptedData;
import org.opensaml.xmlsec.encryption.EncryptedKey;
import org.opensaml.xmlsec.keyinfo.KeyInfoGenerator;
import org.opensaml.xmlsec.signature.KeyInfo;
import org.opensaml.xmlsec.signature.XMLSignatureBuilder;
import org.opensaml.xmlsec.signature.support.SignatureConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.google.common.base.Strings;
/**
* Supports encryption of XMLObjects, their content and keys, according to the XML Encryption specification, version
* 20021210.
*
*
* Various overloaded method variants are supplied for encrypting XMLObjects and their contents (with or without
* encryption of the associated data encryption key), as well as for encrypting keys separately.
*
*
*
* The parameters for data encryption are specified with an instance of {@link DataEncryptionParameters}. The parameters
* for key encryption are specified with one or more instances of {@link KeyEncryptionParameters}.
*
*
*
* The data encryption credential supplied by {@link DataEncryptionParameters#getEncryptionCredential()} is mandatory
* unless key encryption is also being performed and all associated key encryption parameters contain a valid key
* encryption credential containing a valid key encryption key. In this case the data encryption key will be randomly
* generated based on the algorithm URI supplied by {@link DataEncryptionParameters#getAlgorithm()}.
*
*
*
* If encryption of the data encryption key is being performed using the overloaded methods for elements or content, the
* resulting EncryptedKey(s) will be placed inline within the KeyInfo of the resulting EncryptedData. If this is not the
* desired behavior, the XMLObject and the data encryption key should be encrypted separately, and the placement of
* EncryptedKey(s) handled by the caller. Specialized subclasses of this class maybe also handle key placement in an
* application-specific manner.
*
*
*/
public class Encrypter {
/** Class logger. */
private final Logger log = LoggerFactory.getLogger(Encrypter.class);
/** Unmarshaller used to create EncryptedData objects from DOM element. */
private final Unmarshaller encryptedDataUnmarshaller;
/** Unmarshaller used to create EncryptedData objects from DOM element. */
private final Unmarshaller encryptedKeyUnmarshaller;
/** Builder instance for building KeyInfo objects. */
private final XMLSignatureBuilder keyInfoBuilder;
/** The name of the JCA security provider to use. */
private String jcaProviderName;
/**
* Constructor.
*
*/
public Encrypter() {
UnmarshallerFactory unmarshallerFactory = XMLObjectProviderRegistrySupport.getUnmarshallerFactory();
encryptedDataUnmarshaller = unmarshallerFactory.getUnmarshaller(EncryptedData.DEFAULT_ELEMENT_NAME);
encryptedKeyUnmarshaller = unmarshallerFactory.getUnmarshaller(EncryptedKey.DEFAULT_ELEMENT_NAME);
Constraint.isNotNull(encryptedDataUnmarshaller, "EncryptedData unmarshaller not configured");
Constraint.isNotNull(encryptedKeyUnmarshaller, "EncryptedKey unmarshaller not configured");
XMLObjectBuilderFactory builderFactory = XMLObjectProviderRegistrySupport.getBuilderFactory();
keyInfoBuilder = (XMLSignatureBuilder) builderFactory.getBuilder(KeyInfo.DEFAULT_ELEMENT_NAME);
Constraint.isNotNull(keyInfoBuilder, "KeyInfo builder not configured");
}
/**
* Get the Java Cryptography Architecture (JCA) security provider name that should be used to provide the encryption
* support.
*
* Defaults to null
, which means that the first registered provider which supports the requested
* encryption algorithm URI will be used.
*
* @return the JCA provider name to use
*/
@Nullable public String getJCAProviderName() {
return jcaProviderName;
}
/**
* Set the Java Cryptography Architecture (JCA) security provider name that should be used to provide the encryption
* support.
*
* Defaults to null
, which means that the first registered provider which supports the requested
* encryption algorithm URI will be used.
*
* @param providerName the JCA provider name to use
*/
public void setJCAProviderName(@Nullable final String providerName) {
jcaProviderName = providerName;
}
/**
* Encrypts the DOM representation of the XMLObject.
*
* @param xmlObject the XMLObject to be encrypted
* @param encParams parameters for encrypting the data
*
* @return the resulting EncryptedData element
* @throws EncryptionException exception thrown on encryption errors
*/
@Nonnull public EncryptedData encryptElement(@Nonnull final XMLObject xmlObject,
@Nonnull final DataEncryptionParameters encParams) throws EncryptionException {
List emptyKEKParamsList = new ArrayList<>();
return encryptElement(xmlObject, encParams, emptyKEKParamsList, false);
}
/**
* Encrypts the DOM representation of the XMLObject, encrypts the encryption key using the specified key encryption
* parameters and places the resulting EncryptedKey within the EncryptedData's KeyInfo.
*
* @param xmlObject the XMLObject to be encrypted
* @param encParams parameters for encrypting the data
* @param kekParams parameters for encrypting the encryption key
*
* @return the resulting EncryptedData element
* @throws EncryptionException exception thrown on encryption errors
*/
@Nonnull public EncryptedData encryptElement(@Nonnull final XMLObject xmlObject,
@Nonnull final DataEncryptionParameters encParams, @Nonnull final KeyEncryptionParameters kekParams)
throws EncryptionException {
List kekParamsList = new ArrayList<>();
kekParamsList.add(kekParams);
return encryptElement(xmlObject, encParams, kekParamsList, false);
}
/**
* Encrypts the DOM representation of the XMLObject, encrypts the encryption key using the specified key encryption
* parameters and places the resulting EncryptedKey(s) within the EncryptedData's KeyInfo.
*
* @param xmlObject the XMLObject to be encrypted
* @param encParams parameters for encrypting the data
* @param kekParamsList parameters for encrypting the encryption key
*
* @return the resulting EncryptedData element
* @throws EncryptionException exception thrown on encryption errors
*/
@Nonnull public EncryptedData encryptElement(@Nonnull final XMLObject xmlObject,
@Nonnull final DataEncryptionParameters encParams,
@Nonnull final List kekParamsList) throws EncryptionException {
return encryptElement(xmlObject, encParams, kekParamsList, false);
}
/**
* Encrypts the DOM representation of the content of an XMLObject.
*
* @param xmlObject the XMLObject to be encrypted
* @param encParams parameters for encrypting the data
*
* @return the resulting EncryptedData element
* @throws EncryptionException exception thrown on encryption errors
*/
@Nonnull public EncryptedData encryptElementContent(@Nonnull final XMLObject xmlObject,
@Nonnull final DataEncryptionParameters encParams) throws EncryptionException {
List emptyKEKParamsList = new ArrayList<>();
return encryptElement(xmlObject, encParams, emptyKEKParamsList, true);
}
/**
* Encrypts the DOM representation of the content of an XMLObject, encrypts the encryption key using the specified
* key encryption parameters and places the resulting EncryptedKey within the EncryptedData's KeyInfo..
*
* @param xmlObject the XMLObject to be encrypted
* @param encParams parameters for encrypting the data
* @param kekParams parameters for encrypting the encryption key
*
* @return the resulting EncryptedData element
* @throws EncryptionException exception thrown on encryption errors
*/
@Nonnull public EncryptedData encryptElementContent(@Nonnull final XMLObject xmlObject,
@Nonnull final DataEncryptionParameters encParams, @Nonnull final KeyEncryptionParameters kekParams)
throws EncryptionException {
List kekParamsList = new ArrayList<>();
kekParamsList.add(kekParams);
return encryptElement(xmlObject, encParams, kekParamsList, true);
}
/**
* Encrypts the DOM representation of the content of an XMLObject, encrypts the encryption key using the specified
* key encryption parameters and places the resulting EncryptedKey(s) within the EncryptedData's KeyInfo..
*
* @param xmlObject the XMLObject to be encrypted
* @param encParams parameters for encrypting the data
* @param kekParamsList parameters for encrypting the encryption key
*
* @return the resulting EncryptedData element
* @throws EncryptionException exception thrown on encryption errors
*/
@Nonnull public EncryptedData encryptElementContent(@Nonnull final XMLObject xmlObject,
@Nonnull final DataEncryptionParameters encParams,
@Nonnull final List kekParamsList) throws EncryptionException {
return encryptElement(xmlObject, encParams, kekParamsList, true);
}
/**
* Encrypts a key once for each key encryption parameters set that is supplied.
*
* @param key the key to encrypt
* @param kekParamsList a list parameters for encrypting the key
* @param containingDocument the document that will own the DOM element underlying the resulting EncryptedKey
* objects
*
* @return the resulting list of EncryptedKey objects
*
* @throws EncryptionException exception thrown on encryption errors
*/
@Nonnull public List encryptKey(@Nonnull final Key key,
@Nonnull final List kekParamsList, @Nonnull final Document containingDocument)
throws EncryptionException {
checkParams(kekParamsList, false);
List encKeys = new ArrayList<>();
for (KeyEncryptionParameters kekParam : kekParamsList) {
encKeys.add(encryptKey(key, kekParam, containingDocument));
}
return encKeys;
}
/**
* Encrypts a key.
*
* @param key the key to encrypt
* @param kekParams parameters for encrypting the key
* @param containingDocument the document that will own the DOM element underlying the resulting EncryptedKey object
*
* @return the resulting EncryptedKey object
*
* @throws EncryptionException exception thrown on encryption errors
*/
@Nonnull public EncryptedKey encryptKey(@Nonnull final Key key, @Nonnull final KeyEncryptionParameters kekParams,
@Nonnull final Document containingDocument) throws EncryptionException {
checkParams(kekParams, false);
Key encryptionKey = CredentialSupport.extractEncryptionKey(kekParams.getEncryptionCredential());
EncryptedKey encryptedKey = encryptKey(key, encryptionKey, kekParams.getAlgorithm(),
kekParams.getRSAOAEPParameters(), containingDocument);
if (kekParams.getKeyInfoGenerator() != null) {
KeyInfoGenerator generator = kekParams.getKeyInfoGenerator();
log.debug("Dynamically generating KeyInfo from Credential for EncryptedKey using generator: {}", generator
.getClass().getName());
try {
encryptedKey.setKeyInfo(generator.generate(kekParams.getEncryptionCredential()));
} catch (SecurityException e) {
log.error("Error during EncryptedKey KeyInfo generation", e);
throw new EncryptionException("Error during EncryptedKey KeyInfo generation", e);
}
}
if (kekParams.getRecipient() != null) {
encryptedKey.setRecipient(kekParams.getRecipient());
}
return encryptedKey;
}
/**
* Encrypts a key using the specified encryption key and algorithm URI.
*
* @param targetKey the key to encrypt
* @param encryptionKey the key with which to encrypt the target key
* @param encryptionAlgorithmURI the XML Encryption algorithm URI corresponding to the encryption key
* @param rsaOAEPParams the RSA-OAEP params instance (may be null)
* @param containingDocument the document that will own the resulting element
* @return the new EncryptedKey object
* @throws EncryptionException exception thrown on encryption errors
*/
@Nonnull protected EncryptedKey encryptKey(@Nonnull final Key targetKey, @Nonnull final Key encryptionKey,
@Nonnull final String encryptionAlgorithmURI, @Nullable final RSAOAEPParameters rsaOAEPParams,
@Nonnull final Document containingDocument) throws EncryptionException {
Constraint.isNotNull(encryptionAlgorithmURI, "Encryption algorithm URI cannot be null");
Constraint.isNotNull(containingDocument, "Containing document cannot be null");
if (targetKey == null) {
log.error("Target key for key encryption was null");
throw new EncryptionException("Target key was null");
} else if (encryptionKey == null) {
log.error("Encryption key for key encryption was null");
throw new EncryptionException("Encryption key was null");
}
log.debug("Encrypting encryption key with algorithm: {}", encryptionAlgorithmURI);
XMLCipher xmlCipher;
try {
xmlCipher = buildXMLCipher(encryptionKey, encryptionAlgorithmURI, rsaOAEPParams);
} catch (XMLEncryptionException e) {
log.error("Error initializing cipher instance on key encryption", e);
throw new EncryptionException("Error initializing cipher instance on key encryption", e);
}
org.apache.xml.security.encryption.EncryptedKey apacheEncryptedKey;
try {
if (AlgorithmSupport.isRSAOAEP(encryptionAlgorithmURI) && rsaOAEPParams != null) {
apacheEncryptedKey = xmlCipher.encryptKey(containingDocument, targetKey,
getEffectiveMGF(encryptionAlgorithmURI, rsaOAEPParams),
decodeOAEPParams(rsaOAEPParams.getOAEPParams()));
} else {
apacheEncryptedKey = xmlCipher.encryptKey(containingDocument, targetKey);
}
postProcessApacheEncryptedKey(apacheEncryptedKey, targetKey, encryptionKey, encryptionAlgorithmURI,
containingDocument);
} catch (XMLEncryptionException e) {
log.error("Error encrypting element on key encryption", e);
throw new EncryptionException("Error encrypting element on key encryption", e);
}
try {
Element encKeyElement = xmlCipher.martial(containingDocument, apacheEncryptedKey);
return (EncryptedKey) encryptedKeyUnmarshaller.unmarshall(encKeyElement);
} catch (UnmarshallingException e) {
log.error("Error unmarshalling EncryptedKey element", e);
throw new EncryptionException("Error unmarshalling EncryptedKey element");
}
}
/**
* Construct and return an instance of {@link XMLCipher} based on the given inputs.
*
* @param encryptionKey the key transport encryption key with which to initialize the XMLCipher
* @param encryptionAlgorithmURI the key transport encryption algorithm URI
* @param rsaOAEPParams the optional RSA OAEP parameters instance
* @return new XMLCipher instance
* @throws XMLEncryptionException if there is a problem constructing the XMLCipher instance
*/
@Nonnull protected XMLCipher buildXMLCipher(@Nonnull final Key encryptionKey,
@Nonnull final String encryptionAlgorithmURI, @Nullable final RSAOAEPParameters rsaOAEPParams)
throws XMLEncryptionException {
XMLCipher xmlCipher;
if (getJCAProviderName() != null) {
if (AlgorithmSupport.isRSAOAEP(encryptionAlgorithmURI) && rsaOAEPParams != null
&& rsaOAEPParams.getDigestMethod() != null) {
xmlCipher = XMLCipher.getProviderInstance(encryptionAlgorithmURI, getJCAProviderName(),
null, rsaOAEPParams.getDigestMethod());
} else {
xmlCipher = XMLCipher.getProviderInstance(encryptionAlgorithmURI, getJCAProviderName());
}
} else {
if (AlgorithmSupport.isRSAOAEP(encryptionAlgorithmURI) && rsaOAEPParams != null
&& rsaOAEPParams.getDigestMethod() != null) {
xmlCipher = XMLCipher.getInstance(encryptionAlgorithmURI, null, rsaOAEPParams.getDigestMethod());
} else {
xmlCipher = XMLCipher.getInstance(encryptionAlgorithmURI);
}
}
xmlCipher.init(XMLCipher.WRAP_MODE, encryptionKey);
return xmlCipher;
}
/**
* Get the effective RSA OAEP mask generation function (MGF) to use.
*
* @param encryptionAlgorithmURI the key transport encryption algorithm URI
* @param rsaOAEPParams the optional RSA OAEP params instance
* @return the effective MGF algorithm URI to use, may be null
*/
@Nullable protected String getEffectiveMGF(@Nonnull final String encryptionAlgorithmURI,
@Nullable final RSAOAEPParameters rsaOAEPParams) {
if (EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP11.equals(encryptionAlgorithmURI)
&& rsaOAEPParams != null) {
return rsaOAEPParams.getMaskGenerationFunction();
} else {
return null;
}
}
/**
* Safely decode and normalize base64-encoded OAEPParams data.
*
* @param base64Params the base64-encoded parameters
* @return the decoded parameters or null
* @throws EncryptionException if there is a problem base64-decoding the OAEPParams data
*/
@Nullable protected byte[] decodeOAEPParams(@Nullable final String base64Params) throws EncryptionException {
try {
if (base64Params != null) {
byte[] oaepParams = Base64Support.decode(base64Params);
if (oaepParams.length == 0) {
return null;
} else {
return oaepParams;
}
} else {
return null;
}
} catch (RuntimeException e) {
throw new EncryptionException(String.format("Error decoding OAEPParams data '%s'", base64Params), e);
}
}
/**
*
* Post-process the Apache EncryptedKey, prior to marshalling to DOM and unmarshalling into an XMLObject.
*
* @param apacheEncryptedKey the Apache EncryptedKeyObject to post-process
* @param targetKey the key to encrypt
* @param encryptionKey the key with which to encrypt the target key
* @param encryptionAlgorithmURI the XML Encryption algorithm URI corresponding to the encryption key
* @param containingDocument the document that will own the resulting element
*
* @throws EncryptionException exception thrown on encryption errors
*/
protected void postProcessApacheEncryptedKey(
@Nonnull final org.apache.xml.security.encryption.EncryptedKey apacheEncryptedKey,
@Nonnull final Key targetKey, @Nonnull final Key encryptionKey,
@Nonnull final String encryptionAlgorithmURI, @Nonnull final Document containingDocument)
throws EncryptionException {
// To maximize interop, explicitly express the defaults of SHA-1 digest method and MGF-1 w/ SHA-1 input
// parameters to RSA-OAEP key transport algorithm. The latter only applies to the XML Encryption 1.1 variant.
// Check and only add if the library hasn't already done so.
if (AlgorithmSupport.isRSAOAEP(encryptionAlgorithmURI)) {
org.apache.xml.security.encryption.EncryptionMethod apacheEncryptionMethod =
apacheEncryptedKey.getEncryptionMethod();
if (apacheEncryptionMethod.getDigestAlgorithm() == null) {
apacheEncryptionMethod.setDigestAlgorithm(SignatureConstants.ALGO_ID_DIGEST_SHA1);
}
if (!EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP.equals(encryptionAlgorithmURI)) {
if (apacheEncryptionMethod.getMGFAlgorithm() == null) {
apacheEncryptionMethod.setMGFAlgorithm(EncryptionConstants.ALGO_ID_MGF1_SHA1);
}
}
}
}
/**
* Encrypts the given XMLObject using the specified encryption key, algorithm URI and content mode flag.
*
* @param xmlObject the XMLObject to be encrypted
* @param encryptionKey the key with which to encrypt the XMLObject
* @param encryptionAlgorithmURI the XML Encryption algorithm URI corresponding to the encryption key
* @param encryptContentMode whether just the content of the XMLObject should be encrypted
* @return the resulting EncryptedData object
* @throws EncryptionException exception thrown on encryption errors
*/
@Nonnull protected EncryptedData encryptElement(@Nonnull final XMLObject xmlObject,
@Nonnull final Key encryptionKey, @Nonnull final String encryptionAlgorithmURI, boolean encryptContentMode)
throws EncryptionException {
if (xmlObject == null) {
log.error("XMLObject for encryption was null");
throw new EncryptionException("XMLObject cannot be null");
} else if (encryptionKey == null) {
log.error("Encryption key for key encryption was null");
throw new EncryptionException("Encryption key cannot be null");
}
log.debug("Encrypting XMLObject using algorithm URI {} with content mode {}", encryptionAlgorithmURI,
encryptContentMode);
checkAndMarshall(xmlObject);
Element targetElement = xmlObject.getDOM();
Document ownerDocument = targetElement.getOwnerDocument();
XMLCipher xmlCipher;
try {
if (getJCAProviderName() != null) {
xmlCipher = XMLCipher.getProviderInstance(encryptionAlgorithmURI, getJCAProviderName());
} else {
xmlCipher = XMLCipher.getInstance(encryptionAlgorithmURI);
}
xmlCipher.init(XMLCipher.ENCRYPT_MODE, encryptionKey);
} catch (XMLEncryptionException e) {
log.error("Error initializing cipher instance on XMLObject encryption", e);
throw new EncryptionException("Error initializing cipher instance", e);
}
org.apache.xml.security.encryption.EncryptedData apacheEncryptedData;
try {
apacheEncryptedData = xmlCipher.encryptData(ownerDocument, targetElement, encryptContentMode);
} catch (Exception e) {
log.error("Error encrypting XMLObject", e);
throw new EncryptionException("Error encrypting XMLObject", e);
}
try {
Element encDataElement = xmlCipher.martial(ownerDocument, apacheEncryptedData);
return (EncryptedData) encryptedDataUnmarshaller.unmarshall(encDataElement);
} catch (UnmarshallingException e) {
log.error("Error unmarshalling EncryptedData element", e);
throw new EncryptionException("Error unmarshalling EncryptedData element", e);
}
}
/**
* Encrypts the given XMLObject using the specified encryption key, algorithm URI and content mode flag.
* EncryptedKeys, if any, are placed inline within the KeyInfo of the resulting EncryptedData.
*
* @param xmlObject the XMLObject to be encrypted
* @param encParams the encryption parameters to use
* @param kekParamsList the key encryption parameters to use
* @param encryptContentMode whether just the content of the XMLObject should be encrypted
*
* @return the resulting EncryptedData object
* @throws EncryptionException exception thrown on encryption errors
*/
@Nonnull private EncryptedData encryptElement(@Nonnull final XMLObject xmlObject,
@Nonnull final DataEncryptionParameters encParams,
@Nonnull final List kekParamsList, boolean encryptContentMode)
throws EncryptionException {
checkParams(encParams, kekParamsList);
String encryptionAlgorithmURI = encParams.getAlgorithm();
Key encryptionKey = CredentialSupport.extractEncryptionKey(encParams.getEncryptionCredential());
if (encryptionKey == null) {
encryptionKey = generateEncryptionKey(encryptionAlgorithmURI);
}
EncryptedData encryptedData =
encryptElement(xmlObject, encryptionKey, encryptionAlgorithmURI, encryptContentMode);
Document ownerDocument = encryptedData.getDOM().getOwnerDocument();
if (encParams.getKeyInfoGenerator() != null) {
KeyInfoGenerator generator = encParams.getKeyInfoGenerator();
log.debug("Dynamically generating KeyInfo from Credential for EncryptedData using generator: {}", generator
.getClass().getName());
try {
encryptedData.setKeyInfo(generator.generate(encParams.getEncryptionCredential()));
} catch (SecurityException e) {
log.error("Error during EncryptedData KeyInfo generation", e);
throw new EncryptionException("Error during EncryptedData KeyInfo generation", e);
}
}
for (KeyEncryptionParameters kekParams : kekParamsList) {
EncryptedKey encryptedKey = encryptKey(encryptionKey, kekParams, ownerDocument);
if (encryptedData.getKeyInfo() == null) {
KeyInfo keyInfo = keyInfoBuilder.buildObject();
encryptedData.setKeyInfo(keyInfo);
}
encryptedData.getKeyInfo().getEncryptedKeys().add(encryptedKey);
}
return encryptedData;
}
/**
* Ensure that the XMLObject is marshalled.
*
* @param xmlObject the object to check and marshall
* @throws EncryptionException thrown if there is an error when marshalling the XMLObject
*/
protected void checkAndMarshall(@Nonnull final XMLObject xmlObject) throws EncryptionException {
Element targetElement = xmlObject.getDOM();
if (targetElement == null) {
try {
Marshaller marshaller =
XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(xmlObject);
if (marshaller == null) {
throw new MarshallingException("No marshaller available for " + xmlObject.getElementQName());
}
targetElement = marshaller.marshall(xmlObject);
} catch (MarshallingException e) {
log.error("Error marshalling target XMLObject", e);
throw new EncryptionException("Error marshalling target XMLObject", e);
}
}
}
/**
* Check data encryption parameters for consistency and required values.
*
* @param encParams the data encryption parameters to check
*
* @throws EncryptionException thrown if any parameters are missing or have invalid values
*/
protected void checkParams(@Nonnull final DataEncryptionParameters encParams) throws EncryptionException {
if (encParams == null) {
log.error("Data encryption parameters are required");
throw new EncryptionException("Data encryption parameters are required");
} else if (Strings.isNullOrEmpty(encParams.getAlgorithm())) {
log.error("Data encryption algorithm URI is required");
throw new EncryptionException("Data encryption algorithm URI is required");
}
}
/**
* Check key encryption parameters for consistency and required values.
*
* @param kekParams the key encryption parameters to check
* @param allowEmpty if false, a null parameter is treated as an error
*
* @throws EncryptionException thrown if any parameters are missing or have invalid values
*/
protected void checkParams(@Nullable final KeyEncryptionParameters kekParams, boolean allowEmpty)
throws EncryptionException {
if (kekParams == null) {
if (allowEmpty) {
return;
} else {
log.error("Key encryption parameters are required");
throw new EncryptionException("Key encryption parameters are required");
}
}
Key key = CredentialSupport.extractEncryptionKey(kekParams.getEncryptionCredential());
if (key == null) {
log.error("Key encryption credential and contained key are required");
throw new EncryptionException("Key encryption credential and contained key are required");
} else if (key instanceof DSAPublicKey) {
log.error("Attempt made to use DSA key for encrypted key transport");
throw new EncryptionException("DSA keys may not be used for encrypted key transport");
} else if (key instanceof ECPublicKey) {
log.error("Attempt made to use EC key for encrypted key transport");
throw new EncryptionException("EC keys may not be used for encrypted key transport");
} else if (Strings.isNullOrEmpty(kekParams.getAlgorithm())) {
log.error("Key encryption algorithm URI is required");
throw new EncryptionException("Key encryption algorithm URI is required");
}
}
/**
* Check a list of key encryption parameters for consistency and required values.
*
* @param kekParamsList the key encryption parameters list to check
* @param allowEmpty if false, a null or empty list is treated as an error
*
* @throws EncryptionException thrown if any parameters are missing or have invalid values
*/
protected void checkParams(@Nullable final List kekParamsList, boolean allowEmpty)
throws EncryptionException {
if (kekParamsList == null || kekParamsList.isEmpty()) {
if (allowEmpty) {
return;
} else {
log.error("Key encryption parameters list may not be empty");
throw new EncryptionException("Key encryption parameters list may not be empty");
}
}
for (KeyEncryptionParameters kekParams : kekParamsList) {
checkParams(kekParams, false);
}
}
/**
* Check the encryption parameters and key encryption parameters for valid combinations of options.
*
* @param encParams the encryption parameters to use
* @param kekParamsList the key encryption parameters to use
* @throws EncryptionException exception thrown on encryption errors
*/
protected void checkParams(@Nonnull final DataEncryptionParameters encParams,
@Nullable final List kekParamsList) throws EncryptionException {
checkParams(encParams);
checkParams(kekParamsList, true);
if (CredentialSupport.extractEncryptionKey(encParams.getEncryptionCredential()) == null
&& (kekParamsList == null || kekParamsList.isEmpty())) {
log.error("Using a generated encryption key requires a KeyEncryptionParameters "
+ "object and key encryption key");
throw new EncryptionException("Using a generated encryption key requires a KeyEncryptionParameters "
+ "object and key encryption key");
}
}
/**
* Generate a random symmetric encryption key.
*
* @param encryptionAlgorithmURI the encryption algorithm URI
* @return a randomly generated symmetric key
* @throws EncryptionException thrown if the key cannot be generated based on the specified algorithm URI
*/
@Nonnull protected SecretKey generateEncryptionKey(@Nonnull final String encryptionAlgorithmURI)
throws EncryptionException {
try {
log.debug("Generating random symmetric data encryption key from algorithm URI: {}", encryptionAlgorithmURI);
return AlgorithmSupport.generateSymmetricKey(encryptionAlgorithmURI);
} catch (NoSuchAlgorithmException e) {
log.error("Could not generate encryption key, algorithm URI was invalid: " + encryptionAlgorithmURI);
throw new EncryptionException("Could not generate encryption key, algorithm URI was invalid: "
+ encryptionAlgorithmURI);
} catch (KeyException e) {
log.error("Could not generate encryption key from algorithm URI: " + encryptionAlgorithmURI);
throw new EncryptionException("Could not generate encryption key from algorithm URI: "
+ encryptionAlgorithmURI);
}
}
/*
* Initialize the Apache XML security library if it hasn't been already
*/
static {
if (!Init.isInitialized()) {
Init.init();
}
}
}