org.apache.xml.security.encryption.XMLCipher 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.xml.security.encryption;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.spec.MGF1ParameterSpec;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import org.apache.xml.security.algorithms.JCEMapper;
import org.apache.xml.security.algorithms.MessageDigestAlgorithm;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.c14n.InvalidCanonicalizerException;
import org.apache.xml.security.exceptions.Base64DecodingException;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.keys.KeyInfo;
import org.apache.xml.security.keys.keyresolver.KeyResolverException;
import org.apache.xml.security.keys.keyresolver.KeyResolverSpi;
import org.apache.xml.security.keys.keyresolver.implementations.EncryptedKeyResolver;
import org.apache.xml.security.signature.XMLSignatureException;
import org.apache.xml.security.transforms.InvalidTransformException;
import org.apache.xml.security.transforms.TransformationException;
import org.apache.xml.security.utils.Base64;
import org.apache.xml.security.utils.Constants;
import org.apache.xml.security.utils.ElementProxy;
import org.apache.xml.security.utils.EncryptionConstants;
import org.apache.xml.security.utils.XMLUtils;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* XMLCipher
encrypts and decrypts the contents of
* Document
s, Element
s and Element
* contents. It was designed to resemble javax.crypto.Cipher
in
* order to facilitate understanding of its functioning.
*
* @author Axl Mattheus (Sun Microsystems)
* @author Christian Geuer-Pollmann
*/
public class XMLCipher {
private static org.apache.commons.logging.Log log =
org.apache.commons.logging.LogFactory.getLog(XMLCipher.class);
/** Triple DES EDE (192 bit key) in CBC mode */
public static final String TRIPLEDES =
EncryptionConstants.ALGO_ID_BLOCKCIPHER_TRIPLEDES;
/** AES 128 Cipher */
public static final String AES_128 =
EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128;
/** AES 256 Cipher */
public static final String AES_256 =
EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256;
/** AES 192 Cipher */
public static final String AES_192 =
EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES192;
/** AES 128 GCM Cipher */
public static final String AES_128_GCM =
EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM;
/** AES 192 GCM Cipher */
public static final String AES_192_GCM =
EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES192_GCM;
/** AES 256 GCM Cipher */
public static final String AES_256_GCM =
EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256_GCM;
/** RSA 1.5 Cipher */
public static final String RSA_v1dot5 =
EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSA15;
/** RSA OAEP Cipher */
public static final String RSA_OAEP =
EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP;
/** RSA OAEP Cipher */
public static final String RSA_OAEP_11 =
EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP_11;
/** DIFFIE_HELLMAN Cipher */
public static final String DIFFIE_HELLMAN =
EncryptionConstants.ALGO_ID_KEYAGREEMENT_DH;
/** Triple DES EDE (192 bit key) in CBC mode KEYWRAP*/
public static final String TRIPLEDES_KeyWrap =
EncryptionConstants.ALGO_ID_KEYWRAP_TRIPLEDES;
/** AES 128 Cipher KeyWrap */
public static final String AES_128_KeyWrap =
EncryptionConstants.ALGO_ID_KEYWRAP_AES128;
/** AES 256 Cipher KeyWrap */
public static final String AES_256_KeyWrap =
EncryptionConstants.ALGO_ID_KEYWRAP_AES256;
/** AES 192 Cipher KeyWrap */
public static final String AES_192_KeyWrap =
EncryptionConstants.ALGO_ID_KEYWRAP_AES192;
/** SHA1 Cipher */
public static final String SHA1 =
Constants.ALGO_ID_DIGEST_SHA1;
/** SHA256 Cipher */
public static final String SHA256 =
MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA256;
/** SHA512 Cipher */
public static final String SHA512 =
MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA512;
/** RIPEMD Cipher */
public static final String RIPEMD_160 =
MessageDigestAlgorithm.ALGO_ID_DIGEST_RIPEMD160;
/** XML Signature NS */
public static final String XML_DSIG =
Constants.SignatureSpecNS;
/** N14C_XML */
public static final String N14C_XML =
Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS;
/** N14C_XML with comments*/
public static final String N14C_XML_WITH_COMMENTS =
Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS;
/** N14C_XML exclusive */
public static final String EXCL_XML_N14C =
Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;
/** N14C_XML exclusive with comments*/
public static final String EXCL_XML_N14C_WITH_COMMENTS =
Canonicalizer.ALGO_ID_C14N_EXCL_WITH_COMMENTS;
/** N14C_PHYSICAL preserve the physical representation*/
public static final String PHYSICAL_XML_N14C =
Canonicalizer.ALGO_ID_C14N_PHYSICAL;
/** Base64 encoding */
public static final String BASE64_ENCODING =
org.apache.xml.security.transforms.Transforms.TRANSFORM_BASE64_DECODE;
/** ENCRYPT Mode */
public static final int ENCRYPT_MODE = Cipher.ENCRYPT_MODE;
/** DECRYPT Mode */
public static final int DECRYPT_MODE = Cipher.DECRYPT_MODE;
/** UNWRAP Mode */
public static final int UNWRAP_MODE = Cipher.UNWRAP_MODE;
/** WRAP Mode */
public static final int WRAP_MODE = Cipher.WRAP_MODE;
private static final String ENC_ALGORITHMS = TRIPLEDES + "\n" +
AES_128 + "\n" + AES_256 + "\n" + AES_192 + "\n" + RSA_v1dot5 + "\n" +
RSA_OAEP + "\n" + RSA_OAEP_11 + "\n" + TRIPLEDES_KeyWrap + "\n" +
AES_128_KeyWrap + "\n" + AES_256_KeyWrap + "\n" + AES_192_KeyWrap + "\n" +
AES_128_GCM + "\n" + AES_192_GCM + "\n" + AES_256_GCM + "\n";
/** Cipher created during initialisation that is used for encryption */
private Cipher contextCipher;
/** Mode that the XMLCipher object is operating in */
private int cipherMode = Integer.MIN_VALUE;
/** URI of algorithm that is being used for cryptographic operation */
private String algorithm = null;
/** Cryptographic provider requested by caller */
private String requestedJCEProvider = null;
/** Holds c14n to serialize, if initialized then _always_ use this c14n to serialize */
private Canonicalizer canon;
/** Used for creation of DOM nodes in WRAP and ENCRYPT modes */
private Document contextDocument;
/** Instance of factory used to create XML Encryption objects */
private Factory factory;
/** Serializer class for going to/from UTF-8 */
private Serializer serializer;
/** Local copy of user's key */
private Key key;
/** Local copy of the kek (used to decrypt EncryptedKeys during a
* DECRYPT_MODE operation */
private Key kek;
// The EncryptedKey being built (part of a WRAP operation) or read
// (part of an UNWRAP operation)
private EncryptedKey ek;
// The EncryptedData being built (part of a WRAP operation) or read
// (part of an UNWRAP operation)
private EncryptedData ed;
private SecureRandom random;
private boolean secureValidation = true;
private String digestAlg;
/** List of internal KeyResolvers for DECRYPT and UNWRAP modes. */
private List internalKeyResolvers;
/**
* Set the Serializer algorithm to use
*/
public void setSerializer(Serializer serializer) {
this.serializer = serializer;
serializer.setCanonicalizer(this.canon);
}
/**
* Get the Serializer algorithm to use
*/
public Serializer getSerializer() {
return serializer;
}
/**
* Creates a new XMLCipher
.
*
* @param transformation the name of the transformation, e.g.,
* XMLCipher.TRIPLEDES
. If null the XMLCipher can only
* be used for decrypt or unwrap operations where the encryption method
* is defined in the EncryptionMethod
element.
* @param provider the JCE provider that supplies the transformation,
* if null use the default provider.
* @param canon the name of the c14n algorithm, if
* null
use standard serializer
* @param digestMethod An optional digestMethod to use.
*/
private XMLCipher(
String transformation,
String provider,
String canonAlg,
String digestMethod
) throws XMLEncryptionException {
if (log.isDebugEnabled()) {
log.debug("Constructing XMLCipher...");
}
factory = new Factory();
algorithm = transformation;
requestedJCEProvider = provider;
digestAlg = digestMethod;
// Create a canonicalizer - used when serializing DOM to octets
// prior to encryption (and for the reverse)
try {
if (canonAlg == null) {
// The default is to preserve the physical representation.
this.canon = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_PHYSICAL);
} else {
this.canon = Canonicalizer.getInstance(canonAlg);
}
} catch (InvalidCanonicalizerException ice) {
throw new XMLEncryptionException("empty", ice);
}
if (serializer == null) {
serializer = new DocumentSerializer();
}
serializer.setCanonicalizer(this.canon);
if (transformation != null) {
contextCipher = constructCipher(transformation, digestMethod);
}
}
/**
* Checks to ensure that the supplied algorithm is valid.
*
* @param algorithm the algorithm to check.
* @return true if the algorithm is valid, otherwise false.
* @since 1.0.
*/
private static boolean isValidEncryptionAlgorithm(String algorithm) {
return (
algorithm.equals(TRIPLEDES) ||
algorithm.equals(AES_128) ||
algorithm.equals(AES_256) ||
algorithm.equals(AES_192) ||
algorithm.equals(AES_128_GCM) ||
algorithm.equals(AES_192_GCM) ||
algorithm.equals(AES_256_GCM) ||
algorithm.equals(RSA_v1dot5) ||
algorithm.equals(RSA_OAEP) ||
algorithm.equals(RSA_OAEP_11) ||
algorithm.equals(TRIPLEDES_KeyWrap) ||
algorithm.equals(AES_128_KeyWrap) ||
algorithm.equals(AES_256_KeyWrap) ||
algorithm.equals(AES_192_KeyWrap)
);
}
/**
* Validate the transformation argument of getInstance or getProviderInstance
*
* @param transformation the name of the transformation, e.g.,
* XMLCipher.TRIPLEDES
which is shorthand for
* "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"
*/
private static void validateTransformation(String transformation) {
if (null == transformation) {
throw new NullPointerException("Transformation unexpectedly null...");
}
if (!isValidEncryptionAlgorithm(transformation)) {
log.warn("Algorithm non-standard, expected one of " + ENC_ALGORITHMS);
}
}
/**
* Returns an XMLCipher
that implements the specified
* transformation and operates on the specified context document.
*
* If the default provider package supplies an implementation of the
* requested transformation, an instance of Cipher containing that
* implementation is returned. If the transformation is not available in
* the default provider package, other provider packages are searched.
*
* NOTE1: The transformation name does not follow the same
* pattern as that outlined in the Java Cryptography Extension Reference
* Guide but rather that specified by the XML Encryption Syntax and
* Processing document. The rational behind this is to make it easier for a
* novice at writing Java Encryption software to use the library.
*
* NOTE2: getInstance()
does not follow the
* same pattern regarding exceptional conditions as that used in
* javax.crypto.Cipher
. Instead, it only throws an
* XMLEncryptionException
which wraps an underlying exception.
* The stack trace from the exception should be self explanatory.
*
* @param transformation the name of the transformation, e.g.,
* XMLCipher.TRIPLEDES
which is shorthand for
* "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"
* @throws XMLEncryptionException
* @return the XMLCipher
* @see javax.crypto.Cipher#getInstance(java.lang.String)
*/
public static XMLCipher getInstance(String transformation) throws XMLEncryptionException {
if (log.isDebugEnabled()) {
log.debug("Getting XMLCipher with transformation");
}
validateTransformation(transformation);
return new XMLCipher(transformation, null, null, null);
}
/**
* Returns an XMLCipher
that implements the specified
* transformation, operates on the specified context document and serializes
* the document with the specified canonicalization algorithm before it
* encrypts the document.
*
*
* @param transformation the name of the transformation
* @param canon the name of the c14n algorithm, if null
use
* standard serializer
* @return the XMLCipher
* @throws XMLEncryptionException
*/
public static XMLCipher getInstance(String transformation, String canon)
throws XMLEncryptionException {
if (log.isDebugEnabled()) {
log.debug("Getting XMLCipher with transformation and c14n algorithm");
}
validateTransformation(transformation);
return new XMLCipher(transformation, null, canon, null);
}
/**
* Returns an XMLCipher
that implements the specified
* transformation, operates on the specified context document and serializes
* the document with the specified canonicalization algorithm before it
* encrypts the document.
*
*
* @param transformation the name of the transformation
* @param canon the name of the c14n algorithm, if null
use
* standard serializer
* @param digestMethod An optional digestMethod to use
* @return the XMLCipher
* @throws XMLEncryptionException
*/
public static XMLCipher getInstance(String transformation, String canon, String digestMethod)
throws XMLEncryptionException {
if (log.isDebugEnabled()) {
log.debug("Getting XMLCipher with transformation and c14n algorithm");
}
validateTransformation(transformation);
return new XMLCipher(transformation, null, canon, digestMethod);
}
/**
* Returns an XMLCipher
that implements the specified
* transformation and operates on the specified context document.
*
* @param transformation the name of the transformation
* @param provider the JCE provider that supplies the transformation
* @return the XMLCipher
* @throws XMLEncryptionException
*/
public static XMLCipher getProviderInstance(String transformation, String provider)
throws XMLEncryptionException {
if (log.isDebugEnabled()) {
log.debug("Getting XMLCipher with transformation and provider");
}
if (null == provider) {
throw new NullPointerException("Provider unexpectedly null..");
}
validateTransformation(transformation);
return new XMLCipher(transformation, provider, null, null);
}
/**
* Returns an XMLCipher
that implements the specified
* transformation, operates on the specified context document and serializes
* the document with the specified canonicalization algorithm before it
* encrypts the document.
*
*
* @param transformation the name of the transformation
* @param provider the JCE provider that supplies the transformation
* @param canon the name of the c14n algorithm, if null
use standard
* serializer
* @return the XMLCipher
* @throws XMLEncryptionException
*/
public static XMLCipher getProviderInstance(
String transformation, String provider, String canon
) throws XMLEncryptionException {
if (log.isDebugEnabled()) {
log.debug("Getting XMLCipher with transformation, provider and c14n algorithm");
}
if (null == provider) {
throw new NullPointerException("Provider unexpectedly null..");
}
validateTransformation(transformation);
return new XMLCipher(transformation, provider, canon, null);
}
/**
* Returns an XMLCipher
that implements the specified
* transformation, operates on the specified context document and serializes
* the document with the specified canonicalization algorithm before it
* encrypts the document.
*
*
* @param transformation the name of the transformation
* @param provider the JCE provider that supplies the transformation
* @param canon the name of the c14n algorithm, if null
use standard
* serializer
* @param digestMethod An optional digestMethod to use
* @return the XMLCipher
* @throws XMLEncryptionException
*/
public static XMLCipher getProviderInstance(
String transformation, String provider, String canon, String digestMethod
) throws XMLEncryptionException {
if (log.isDebugEnabled()) {
log.debug("Getting XMLCipher with transformation, provider and c14n algorithm");
}
if (null == provider) {
throw new NullPointerException("Provider unexpectedly null..");
}
validateTransformation(transformation);
return new XMLCipher(transformation, provider, canon, digestMethod);
}
/**
* Returns an XMLCipher
that implements no specific
* transformation, and can therefore only be used for decrypt or
* unwrap operations where the encryption method is defined in the
* EncryptionMethod
element.
*
* @return The XMLCipher
* @throws XMLEncryptionException
*/
public static XMLCipher getInstance() throws XMLEncryptionException {
if (log.isDebugEnabled()) {
log.debug("Getting XMLCipher with no arguments");
}
return new XMLCipher(null, null, null, null);
}
/**
* Returns an XMLCipher
that implements no specific
* transformation, and can therefore only be used for decrypt or
* unwrap operations where the encryption method is defined in the
* EncryptionMethod
element.
*
* Allows the caller to specify a provider that will be used for
* cryptographic operations.
*
* @param provider the JCE provider that supplies the transformation
* @return the XMLCipher
* @throws XMLEncryptionException
*/
public static XMLCipher getProviderInstance(String provider) throws XMLEncryptionException {
if (log.isDebugEnabled()) {
log.debug("Getting XMLCipher with provider");
}
return new XMLCipher(null, provider, null, null);
}
/**
* Initializes this cipher with a key.
*
* The cipher is initialized for one of the following four operations:
* encryption, decryption, key wrapping or key unwrapping, depending on the
* value of opmode.
*
* For WRAP and ENCRYPT modes, this also initialises the internal
* EncryptedKey or EncryptedData (with a CipherValue)
* structure that will be used during the ensuing operations. This
* can be obtained (in order to modify KeyInfo elements etc. prior to
* finalising the encryption) by calling
* {@link #getEncryptedData} or {@link #getEncryptedKey}.
*
* @param opmode the operation mode of this cipher (this is one of the
* following: ENCRYPT_MODE, DECRYPT_MODE, WRAP_MODE or UNWRAP_MODE)
* @param key
* @see javax.crypto.Cipher#init(int, java.security.Key)
* @throws XMLEncryptionException
*/
public void init(int opmode, Key key) throws XMLEncryptionException {
// sanity checks
if (log.isDebugEnabled()) {
log.debug("Initializing XMLCipher...");
}
ek = null;
ed = null;
switch (opmode) {
case ENCRYPT_MODE :
if (log.isDebugEnabled()) {
log.debug("opmode = ENCRYPT_MODE");
}
ed = createEncryptedData(CipherData.VALUE_TYPE, "NO VALUE YET");
break;
case DECRYPT_MODE :
if (log.isDebugEnabled()) {
log.debug("opmode = DECRYPT_MODE");
}
break;
case WRAP_MODE :
if (log.isDebugEnabled()) {
log.debug("opmode = WRAP_MODE");
}
ek = createEncryptedKey(CipherData.VALUE_TYPE, "NO VALUE YET");
break;
case UNWRAP_MODE :
if (log.isDebugEnabled()) {
log.debug("opmode = UNWRAP_MODE");
}
break;
default :
log.error("Mode unexpectedly invalid");
throw new XMLEncryptionException("Invalid mode in init");
}
cipherMode = opmode;
this.key = key;
}
/**
* Set whether secure validation is enabled or not. The default is false.
*/
public void setSecureValidation(boolean secureValidation) {
this.secureValidation = secureValidation;
}
/**
* This method is used to add a custom {@link KeyResolverSpi} to an XMLCipher.
* These KeyResolvers are used in KeyInfo objects in DECRYPT and
* UNWRAP modes.
*
* @param keyResolver
*/
public void registerInternalKeyResolver(KeyResolverSpi keyResolver) {
if (internalKeyResolvers == null) {
internalKeyResolvers = new ArrayList();
}
internalKeyResolvers.add(keyResolver);
}
/**
* Get the EncryptedData being built
*
* Returns the EncryptedData being built during an ENCRYPT operation.
* This can then be used by applications to add KeyInfo elements and
* set other parameters.
*
* @return The EncryptedData being built
*/
public EncryptedData getEncryptedData() {
// Sanity checks
if (log.isDebugEnabled()) {
log.debug("Returning EncryptedData");
}
return ed;
}
/**
* Get the EncryptedData being build
*
* Returns the EncryptedData being built during an ENCRYPT operation.
* This can then be used by applications to add KeyInfo elements and
* set other parameters.
*
* @return The EncryptedData being built
*/
public EncryptedKey getEncryptedKey() {
// Sanity checks
if (log.isDebugEnabled()) {
log.debug("Returning EncryptedKey");
}
return ek;
}
/**
* Set a Key Encryption Key.
*
* The Key Encryption Key (KEK) is used for encrypting/decrypting
* EncryptedKey elements. By setting this separately, the XMLCipher
* class can know whether a key applies to the data part or wrapped key
* part of an encrypted object.
*
* @param kek The key to use for de/encrypting key data
*/
public void setKEK(Key kek) {
this.kek = kek;
}
/**
* Martial an EncryptedData
*
* Takes an EncryptedData object and returns a DOM Element that
* represents the appropriate EncryptedData
*
* Note: This should only be used in cases where the context
* document has been passed in via a call to doFinal.
*
* @param encryptedData EncryptedData object to martial
* @return the DOM Element
representing the passed in
* object
*/
public Element martial(EncryptedData encryptedData) {
return factory.toElement(encryptedData);
}
/**
* Martial an EncryptedData
*
* Takes an EncryptedData object and returns a DOM Element that
* represents the appropriate EncryptedData
*
* @param context The document that will own the returned nodes
* @param encryptedData EncryptedData object to martial
* @return the DOM Element
representing the passed in
* object
*/
public Element martial(Document context, EncryptedData encryptedData) {
contextDocument = context;
return factory.toElement(encryptedData);
}
/**
* Martial an EncryptedKey
*
* Takes an EncryptedKey object and returns a DOM Element that
* represents the appropriate EncryptedKey
*
*
* Note: This should only be used in cases where the context
* document has been passed in via a call to doFinal.
*
* @param encryptedKey EncryptedKey object to martial
* @return the DOM Element
representing the passed in
* object
*/
public Element martial(EncryptedKey encryptedKey) {
return factory.toElement(encryptedKey);
}
/**
* Martial an EncryptedKey
*
* Takes an EncryptedKey object and returns a DOM Element that
* represents the appropriate EncryptedKey
*
* @param context The document that will own the created nodes
* @param encryptedKey EncryptedKey object to martial
* @return the DOM Element
representing the passed in
* object
*/
public Element martial(Document context, EncryptedKey encryptedKey) {
contextDocument = context;
return factory.toElement(encryptedKey);
}
/**
* Martial a ReferenceList
*
* Takes a ReferenceList object and returns a DOM Element that
* represents the appropriate ReferenceList
*
*
* Note: This should only be used in cases where the context
* document has been passed in via a call to doFinal.
*
* @param referenceList ReferenceList object to martial
* @return the DOM Element
representing the passed in
* object
*/
public Element martial(ReferenceList referenceList) {
return factory.toElement(referenceList);
}
/**
* Martial a ReferenceList
*
* Takes a ReferenceList object and returns a DOM Element that
* represents the appropriate ReferenceList
*
* @param context The document that will own the created nodes
* @param referenceList ReferenceList object to martial
* @return the DOM Element
representing the passed in
* object
*/
public Element martial(Document context, ReferenceList referenceList) {
contextDocument = context;
return factory.toElement(referenceList);
}
/**
* Encrypts an Element
and replaces it with its encrypted
* counterpart in the context Document
, that is, the
* Document
specified when one calls
* {@link #getInstance(String) getInstance}.
*
* @param element the Element
to encrypt.
* @return the context Document
with the encrypted
* Element
having replaced the source Element
.
* @throws Exception
*/
private Document encryptElement(Element element) throws Exception{
if (log.isDebugEnabled()) {
log.debug("Encrypting element...");
}
if (null == element) {
log.error("Element unexpectedly null...");
}
if (cipherMode != ENCRYPT_MODE && log.isDebugEnabled()) {
log.debug("XMLCipher unexpectedly not in ENCRYPT_MODE...");
}
if (algorithm == null) {
throw new XMLEncryptionException("XMLCipher instance without transformation specified");
}
encryptData(contextDocument, element, false);
Element encryptedElement = factory.toElement(ed);
Node sourceParent = element.getParentNode();
sourceParent.replaceChild(encryptedElement, element);
return contextDocument;
}
/**
* Encrypts a NodeList
(the contents of an
* Element
) and replaces its parent Element
's
* content with this the resulting EncryptedType
within the
* context Document
, that is, the Document
* specified when one calls
* {@link #getInstance(String) getInstance}.
*
* @param element the NodeList
to encrypt.
* @return the context Document
with the encrypted
* NodeList
having replaced the content of the source
* Element
.
* @throws Exception
*/
private Document encryptElementContent(Element element) throws /* XMLEncryption */Exception {
if (log.isDebugEnabled()) {
log.debug("Encrypting element content...");
}
if (null == element) {
log.error("Element unexpectedly null...");
}
if (cipherMode != ENCRYPT_MODE && log.isDebugEnabled()) {
log.debug("XMLCipher unexpectedly not in ENCRYPT_MODE...");
}
if (algorithm == null) {
throw new XMLEncryptionException("XMLCipher instance without transformation specified");
}
encryptData(contextDocument, element, true);
Element encryptedElement = factory.toElement(ed);
removeContent(element);
element.appendChild(encryptedElement);
return contextDocument;
}
/**
* Process a DOM Document
node. The processing depends on the
* initialization parameters of {@link #init(int, Key) init()}.
*
* @param context the context Document
.
* @param source the Document
to be encrypted or decrypted.
* @return the processed Document
.
* @throws Exception to indicate any exceptional conditions.
*/
public Document doFinal(Document context, Document source) throws /* XMLEncryption */Exception {
if (log.isDebugEnabled()) {
log.debug("Processing source document...");
}
if (null == context) {
log.error("Context document unexpectedly null...");
}
if (null == source) {
log.error("Source document unexpectedly null...");
}
contextDocument = context;
Document result = null;
switch (cipherMode) {
case DECRYPT_MODE:
result = decryptElement(source.getDocumentElement());
break;
case ENCRYPT_MODE:
result = encryptElement(source.getDocumentElement());
break;
case UNWRAP_MODE:
case WRAP_MODE:
break;
default:
throw new XMLEncryptionException("empty", new IllegalStateException());
}
return result;
}
/**
* Process a DOM Element
node. The processing depends on the
* initialization parameters of {@link #init(int, Key) init()}.
*
* @param context the context Document
.
* @param element the Element
to be encrypted.
* @return the processed Document
.
* @throws Exception to indicate any exceptional conditions.
*/
public Document doFinal(Document context, Element element) throws /* XMLEncryption */Exception {
if (log.isDebugEnabled()) {
log.debug("Processing source element...");
}
if (null == context) {
log.error("Context document unexpectedly null...");
}
if (null == element) {
log.error("Source element unexpectedly null...");
}
contextDocument = context;
Document result = null;
switch (cipherMode) {
case DECRYPT_MODE:
result = decryptElement(element);
break;
case ENCRYPT_MODE:
result = encryptElement(element);
break;
case UNWRAP_MODE:
case WRAP_MODE:
break;
default:
throw new XMLEncryptionException("empty", new IllegalStateException());
}
return result;
}
/**
* Process the contents of a DOM Element
node. The processing
* depends on the initialization parameters of
* {@link #init(int, Key) init()}.
*
* @param context the context Document
.
* @param element the Element
which contents is to be
* encrypted.
* @param content
* @return the processed Document
.
* @throws Exception to indicate any exceptional conditions.
*/
public Document doFinal(Document context, Element element, boolean content)
throws /* XMLEncryption*/ Exception {
if (log.isDebugEnabled()) {
log.debug("Processing source element...");
}
if (null == context) {
log.error("Context document unexpectedly null...");
}
if (null == element) {
log.error("Source element unexpectedly null...");
}
contextDocument = context;
Document result = null;
switch (cipherMode) {
case DECRYPT_MODE:
if (content) {
result = decryptElementContent(element);
} else {
result = decryptElement(element);
}
break;
case ENCRYPT_MODE:
if (content) {
result = encryptElementContent(element);
} else {
result = encryptElement(element);
}
break;
case UNWRAP_MODE:
case WRAP_MODE:
break;
default:
throw new XMLEncryptionException("empty", new IllegalStateException());
}
return result;
}
/**
* Returns an EncryptedData
interface. Use this operation if
* you want to have full control over the contents of the
* EncryptedData
structure.
*
* This does not change the source document in any way.
*
* @param context the context Document
.
* @param element the Element
that will be encrypted.
* @return the EncryptedData
* @throws Exception
*/
public EncryptedData encryptData(Document context, Element element) throws
/* XMLEncryption */Exception {
return encryptData(context, element, false);
}
/**
* Returns an EncryptedData
interface. Use this operation if
* you want to have full control over the serialization of the element
* or element content.
*
* This does not change the source document in any way.
*
* @param context the context Document
.
* @param type a URI identifying type information about the plaintext form
* of the encrypted content (may be null
)
* @param serializedData the serialized data
* @return the EncryptedData
* @throws Exception
*/
public EncryptedData encryptData(
Document context, String type, InputStream serializedData
) throws Exception {
if (log.isDebugEnabled()) {
log.debug("Encrypting element...");
}
if (null == context) {
log.error("Context document unexpectedly null...");
}
if (null == serializedData) {
log.error("Serialized data unexpectedly null...");
}
if (cipherMode != ENCRYPT_MODE && log.isDebugEnabled()) {
log.debug("XMLCipher unexpectedly not in ENCRYPT_MODE...");
}
return encryptData(context, null, type, serializedData);
}
/**
* Returns an EncryptedData
interface. Use this operation if
* you want to have full control over the contents of the
* EncryptedData
structure.
*
* This does not change the source document in any way.
*
* @param context the context Document
.
* @param element the Element
that will be encrypted.
* @param contentMode true
to encrypt element's content only,
* false
otherwise
* @return the EncryptedData
* @throws Exception
*/
public EncryptedData encryptData(
Document context, Element element, boolean contentMode
) throws /* XMLEncryption */ Exception {
if (log.isDebugEnabled()) {
log.debug("Encrypting element...");
}
if (null == context) {
log.error("Context document unexpectedly null...");
}
if (null == element) {
log.error("Element unexpectedly null...");
}
if (cipherMode != ENCRYPT_MODE && log.isDebugEnabled()) {
log.debug("XMLCipher unexpectedly not in ENCRYPT_MODE...");
}
if (contentMode) {
return encryptData(context, element, EncryptionConstants.TYPE_CONTENT, null);
} else {
return encryptData(context, element, EncryptionConstants.TYPE_ELEMENT, null);
}
}
private EncryptedData encryptData(
Document context, Element element, String type, InputStream serializedData
) throws /* XMLEncryption */ Exception {
contextDocument = context;
if (algorithm == null) {
throw new XMLEncryptionException("XMLCipher instance without transformation specified");
}
if (serializer instanceof AbstractSerializer) {
((AbstractSerializer)serializer).setSecureValidation(secureValidation);
}
byte[] serializedOctets = null;
if (serializedData == null) {
if (type.equals(EncryptionConstants.TYPE_CONTENT)) {
NodeList children = element.getChildNodes();
if (null != children) {
serializedOctets = serializer.serializeToByteArray(children);
} else {
Object exArgs[] = { "Element has no content." };
throw new XMLEncryptionException("empty", exArgs);
}
} else {
serializedOctets = serializer.serializeToByteArray(element);
}
if (log.isDebugEnabled()) {
log.debug("Serialized octets:\n" + new String(serializedOctets, "UTF-8"));
}
}
byte[] encryptedBytes = null;
// Now create the working cipher if none was created already
Cipher c;
if (contextCipher == null) {
c = constructCipher(algorithm, null);
} else {
c = contextCipher;
}
// Now perform the encryption
try {
// The Spec mandates a 96-bit IV for GCM algorithms
if (AES_128_GCM.equals(algorithm) || AES_192_GCM.equals(algorithm)
|| AES_256_GCM.equals(algorithm)) {
if (random == null) {
random = SecureRandom.getInstance("SHA1PRNG");
}
byte[] temp = new byte[12];
random.nextBytes(temp);
IvParameterSpec paramSpec = new IvParameterSpec(temp);
c.init(cipherMode, key, paramSpec);
} else {
c.init(cipherMode, key);
}
} catch (InvalidKeyException ike) {
throw new XMLEncryptionException("empty", ike);
} catch (NoSuchAlgorithmException ex) {
throw new XMLEncryptionException("empty", ex);
}
try {
if (serializedData != null) {
int numBytes;
byte[] buf = new byte[8192];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((numBytes = serializedData.read(buf)) != -1) {
byte[] data = c.update(buf, 0, numBytes);
baos.write(data);
}
baos.write(c.doFinal());
encryptedBytes = baos.toByteArray();
} else {
encryptedBytes = c.doFinal(serializedOctets);
if (log.isDebugEnabled()) {
log.debug("Expected cipher.outputSize = " +
Integer.toString(c.getOutputSize(serializedOctets.length)));
}
}
if (log.isDebugEnabled()) {
log.debug("Actual cipher.outputSize = "
+ Integer.toString(encryptedBytes.length));
}
} catch (IllegalStateException ise) {
throw new XMLEncryptionException("empty", ise);
} catch (IllegalBlockSizeException ibse) {
throw new XMLEncryptionException("empty", ibse);
} catch (BadPaddingException bpe) {
throw new XMLEncryptionException("empty", bpe);
} catch (UnsupportedEncodingException uee) {
throw new XMLEncryptionException("empty", uee);
}
// Now build up to a properly XML Encryption encoded octet stream
// IvParameterSpec iv;
byte[] iv = c.getIV();
byte[] finalEncryptedBytes = new byte[iv.length + encryptedBytes.length];
System.arraycopy(iv, 0, finalEncryptedBytes, 0, iv.length);
System.arraycopy(encryptedBytes, 0, finalEncryptedBytes, iv.length, encryptedBytes.length);
String base64EncodedEncryptedOctets = Base64.encode(finalEncryptedBytes);
if (log.isDebugEnabled()) {
log.debug("Encrypted octets:\n" + base64EncodedEncryptedOctets);
log.debug("Encrypted octets length = " + base64EncodedEncryptedOctets.length());
}
try {
CipherData cd = ed.getCipherData();
CipherValue cv = cd.getCipherValue();
// cv.setValue(base64EncodedEncryptedOctets.getBytes());
cv.setValue(base64EncodedEncryptedOctets);
if (type != null) {
ed.setType(new URI(type).toString());
}
EncryptionMethod method =
factory.newEncryptionMethod(new URI(algorithm).toString());
method.setDigestAlgorithm(digestAlg);
ed.setEncryptionMethod(method);
} catch (URISyntaxException ex) {
throw new XMLEncryptionException("empty", ex);
}
return ed;
}
/**
* Returns an EncryptedData
interface. Use this operation if
* you want to load an EncryptedData
structure from a DOM
* structure and manipulate the contents.
*
* @param context the context Document
.
* @param element the Element
that will be loaded
* @throws XMLEncryptionException
* @return the EncryptedData
*/
public EncryptedData loadEncryptedData(Document context, Element element)
throws XMLEncryptionException {
if (log.isDebugEnabled()) {
log.debug("Loading encrypted element...");
}
if (null == context) {
throw new NullPointerException("Context document unexpectedly null...");
}
if (null == element) {
throw new NullPointerException("Element unexpectedly null...");
}
if (cipherMode != DECRYPT_MODE) {
throw new XMLEncryptionException("XMLCipher unexpectedly not in DECRYPT_MODE...");
}
contextDocument = context;
ed = factory.newEncryptedData(element);
return ed;
}
/**
* Returns an EncryptedKey
interface. Use this operation if
* you want to load an EncryptedKey
structure from a DOM
* structure and manipulate the contents.
*
* @param context the context Document
.
* @param element the Element
that will be loaded
* @return the EncryptedKey
* @throws XMLEncryptionException
*/
public EncryptedKey loadEncryptedKey(Document context, Element element)
throws XMLEncryptionException {
if (log.isDebugEnabled()) {
log.debug("Loading encrypted key...");
}
if (null == context) {
throw new NullPointerException("Context document unexpectedly null...");
}
if (null == element) {
throw new NullPointerException("Element unexpectedly null...");
}
if (cipherMode != UNWRAP_MODE && cipherMode != DECRYPT_MODE) {
throw new XMLEncryptionException(
"XMLCipher unexpectedly not in UNWRAP_MODE or DECRYPT_MODE..."
);
}
contextDocument = context;
ek = factory.newEncryptedKey(element);
return ek;
}
/**
* Returns an EncryptedKey
interface. Use this operation if
* you want to load an EncryptedKey
structure from a DOM
* structure and manipulate the contents.
*
* Assumes that the context document is the document that owns the element
*
* @param element the Element
that will be loaded
* @return the EncryptedKey
* @throws XMLEncryptionException
*/
public EncryptedKey loadEncryptedKey(Element element) throws XMLEncryptionException {
return loadEncryptedKey(element.getOwnerDocument(), element);
}
/**
* Encrypts a key to an EncryptedKey structure
*
* @param doc the Context document that will be used to general DOM
* @param key Key to encrypt (will use previously set KEK to
* perform encryption
* @return the EncryptedKey
* @throws XMLEncryptionException
*/
public EncryptedKey encryptKey(Document doc, Key key) throws XMLEncryptionException {
return encryptKey(doc, key, null, null);
}
/**
* Encrypts a key to an EncryptedKey structure
*
* @param doc the Context document that will be used to general DOM
* @param key Key to encrypt (will use previously set KEK to
* perform encryption
* @param mgfAlgorithm The xenc11 MGF Algorithm to use
* @param oaepParams The OAEPParams to use
* @return the EncryptedKey
* @throws XMLEncryptionException
*/
public EncryptedKey encryptKey(
Document doc,
Key key,
String mgfAlgorithm,
byte[] oaepParams
) throws XMLEncryptionException {
if (log.isDebugEnabled()) {
log.debug("Encrypting key ...");
}
if (null == key) {
log.error("Key unexpectedly null...");
}
if (cipherMode != WRAP_MODE) {
log.debug("XMLCipher unexpectedly not in WRAP_MODE...");
}
if (algorithm == null) {
throw new XMLEncryptionException("XMLCipher instance without transformation specified");
}
contextDocument = doc;
byte[] encryptedBytes = null;
Cipher c;
if (contextCipher == null) {
// Now create the working cipher
c = constructCipher(algorithm, null);
} else {
c = contextCipher;
}
// Now perform the encryption
try {
// Should internally generate an IV
// todo - allow user to set an IV
OAEPParameterSpec oaepParameters =
constructOAEPParameters(
algorithm, digestAlg, mgfAlgorithm, oaepParams
);
if (oaepParameters == null) {
c.init(Cipher.WRAP_MODE, this.key);
} else {
c.init(Cipher.WRAP_MODE, this.key, oaepParameters);
}
encryptedBytes = c.wrap(key);
} catch (InvalidKeyException ike) {
throw new XMLEncryptionException("empty", ike);
} catch (IllegalBlockSizeException ibse) {
throw new XMLEncryptionException("empty", ibse);
} catch (InvalidAlgorithmParameterException e) {
throw new XMLEncryptionException("empty", e);
}
String base64EncodedEncryptedOctets = Base64.encode(encryptedBytes);
if (log.isDebugEnabled()) {
log.debug("Encrypted key octets:\n" + base64EncodedEncryptedOctets);
log.debug("Encrypted key octets length = " + base64EncodedEncryptedOctets.length());
}
CipherValue cv = ek.getCipherData().getCipherValue();
cv.setValue(base64EncodedEncryptedOctets);
try {
EncryptionMethod method = factory.newEncryptionMethod(new URI(algorithm).toString());
method.setDigestAlgorithm(digestAlg);
method.setMGFAlgorithm(mgfAlgorithm);
method.setOAEPparams(oaepParams);
ek.setEncryptionMethod(method);
} catch (URISyntaxException ex) {
throw new XMLEncryptionException("empty", ex);
}
return ek;
}
/**
* Decrypt a key from a passed in EncryptedKey structure
*
* @param encryptedKey Previously loaded EncryptedKey that needs
* to be decrypted.
* @param algorithm Algorithm for the decryption
* @return a key corresponding to the given type
* @throws XMLEncryptionException
*/
public Key decryptKey(EncryptedKey encryptedKey, String algorithm)
throws XMLEncryptionException {
if (log.isDebugEnabled()) {
log.debug("Decrypting key from previously loaded EncryptedKey...");
}
if (cipherMode != UNWRAP_MODE && log.isDebugEnabled()) {
log.debug("XMLCipher unexpectedly not in UNWRAP_MODE...");
}
if (algorithm == null) {
throw new XMLEncryptionException("Cannot decrypt a key without knowing the algorithm");
}
if (key == null) {
if (log.isDebugEnabled()) {
log.debug("Trying to find a KEK via key resolvers");
}
KeyInfo ki = encryptedKey.getKeyInfo();
if (ki != null) {
ki.setSecureValidation(secureValidation);
try {
String keyWrapAlg = encryptedKey.getEncryptionMethod().getAlgorithm();
String keyType = JCEMapper.getJCEKeyAlgorithmFromURI(keyWrapAlg);
if ("RSA".equals(keyType)) {
key = ki.getPrivateKey();
} else {
key = ki.getSecretKey();
}
}
catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(e);
}
}
}
if (key == null) {
log.error("XMLCipher::decryptKey called without a KEK and cannot resolve");
throw new XMLEncryptionException("Unable to decrypt without a KEK");
}
}
// Obtain the encrypted octets
XMLCipherInput cipherInput = new XMLCipherInput(encryptedKey);
cipherInput.setSecureValidation(secureValidation);
byte[] encryptedBytes = cipherInput.getBytes();
String jceKeyAlgorithm = JCEMapper.getJCEKeyAlgorithmFromURI(algorithm);
if (log.isDebugEnabled()) {
log.debug("JCE Key Algorithm: " + jceKeyAlgorithm);
}
Cipher c;
if (contextCipher == null) {
// Now create the working cipher
c =
constructCipher(
encryptedKey.getEncryptionMethod().getAlgorithm(),
encryptedKey.getEncryptionMethod().getDigestAlgorithm()
);
} else {
c = contextCipher;
}
Key ret;
try {
EncryptionMethod encMethod = encryptedKey.getEncryptionMethod();
OAEPParameterSpec oaepParameters =
constructOAEPParameters(
encMethod.getAlgorithm(), encMethod.getDigestAlgorithm(),
encMethod.getMGFAlgorithm(), encMethod.getOAEPparams()
);
if (oaepParameters == null) {
c.init(Cipher.UNWRAP_MODE, key);
} else {
c.init(Cipher.UNWRAP_MODE, key, oaepParameters);
}
ret = c.unwrap(encryptedBytes, jceKeyAlgorithm, Cipher.SECRET_KEY);
} catch (InvalidKeyException ike) {
throw new XMLEncryptionException("empty", ike);
} catch (NoSuchAlgorithmException nsae) {
throw new XMLEncryptionException("empty", nsae);
} catch (InvalidAlgorithmParameterException e) {
throw new XMLEncryptionException("empty", e);
}
if (log.isDebugEnabled()) {
log.debug("Decryption of key type " + algorithm + " OK");
}
return ret;
}
/**
* Construct an OAEPParameterSpec object from the given parameters
*/
private OAEPParameterSpec constructOAEPParameters(
String encryptionAlgorithm,
String digestAlgorithm,
String mgfAlgorithm,
byte[] oaepParams
) {
if (XMLCipher.RSA_OAEP.equals(encryptionAlgorithm)
|| XMLCipher.RSA_OAEP_11.equals(encryptionAlgorithm)) {
String jceDigestAlgorithm = "SHA-1";
if (digestAlgorithm != null) {
jceDigestAlgorithm = JCEMapper.translateURItoJCEID(digestAlgorithm);
}
PSource.PSpecified pSource = PSource.PSpecified.DEFAULT;
if (oaepParams != null) {
pSource = new PSource.PSpecified(oaepParams);
}
MGF1ParameterSpec mgfParameterSpec = new MGF1ParameterSpec("SHA-1");
if (XMLCipher.RSA_OAEP_11.equals(encryptionAlgorithm)) {
if (EncryptionConstants.MGF1_SHA256.equals(mgfAlgorithm)) {
mgfParameterSpec = new MGF1ParameterSpec("SHA-256");
} else if (EncryptionConstants.MGF1_SHA384.equals(mgfAlgorithm)) {
mgfParameterSpec = new MGF1ParameterSpec("SHA-384");
} else if (EncryptionConstants.MGF1_SHA512.equals(mgfAlgorithm)) {
mgfParameterSpec = new MGF1ParameterSpec("SHA-512");
}
}
return new OAEPParameterSpec(jceDigestAlgorithm, "MGF1", mgfParameterSpec, pSource);
}
return null;
}
/**
* Construct a Cipher object
*/
private Cipher constructCipher(String algorithm, String digestAlgorithm) throws XMLEncryptionException {
String jceAlgorithm = JCEMapper.translateURItoJCEID(algorithm);
if (log.isDebugEnabled()) {
log.debug("JCE Algorithm = " + jceAlgorithm);
}
Cipher c;
try {
if (requestedJCEProvider == null) {
c = Cipher.getInstance(jceAlgorithm);
} else {
c = Cipher.getInstance(jceAlgorithm, requestedJCEProvider);
}
} catch (NoSuchAlgorithmException nsae) {
// Check to see if an RSA OAEP MGF-1 with SHA-1 algorithm was requested
// Some JDKs don't support RSA/ECB/OAEPPadding
if (XMLCipher.RSA_OAEP.equals(algorithm)
&& (digestAlgorithm == null
|| MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA1.equals(digestAlgorithm))) {
try {
if (requestedJCEProvider == null) {
c = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
} else {
c = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding", requestedJCEProvider);
}
} catch (Exception ex) {
throw new XMLEncryptionException("empty", ex);
}
} else {
throw new XMLEncryptionException("empty", nsae);
}
} catch (NoSuchProviderException nspre) {
throw new XMLEncryptionException("empty", nspre);
} catch (NoSuchPaddingException nspae) {
throw new XMLEncryptionException("empty", nspae);
}
return c;
}
/**
* Decrypt a key from a passed in EncryptedKey structure. This version
* is used mainly internally, when the cipher already has an
* EncryptedData loaded. The algorithm URI will be read from the
* EncryptedData
*
* @param encryptedKey Previously loaded EncryptedKey that needs
* to be decrypted.
* @return a key corresponding to the given type
* @throws XMLEncryptionException
*/
public Key decryptKey(EncryptedKey encryptedKey) throws XMLEncryptionException {
return decryptKey(encryptedKey, ed.getEncryptionMethod().getAlgorithm());
}
/**
* Removes the contents of a Node
.
*
* @param node the Node
to clear.
*/
private static void removeContent(Node node) {
while (node.hasChildNodes()) {
node.removeChild(node.getFirstChild());
}
}
/**
* Decrypts EncryptedData
in a single-part operation.
*
* @param element the EncryptedData
to decrypt.
* @return the Node
as a result of the decrypt operation.
* @throws XMLEncryptionException
*/
private Document decryptElement(Element element) throws XMLEncryptionException {
if (log.isDebugEnabled()) {
log.debug("Decrypting element...");
}
if (serializer instanceof AbstractSerializer) {
((AbstractSerializer)serializer).setSecureValidation(secureValidation);
}
if (cipherMode != DECRYPT_MODE) {
log.error("XMLCipher unexpectedly not in DECRYPT_MODE...");
}
byte[] octets = decryptToByteArray(element);
if (log.isDebugEnabled()) {
log.debug("Decrypted octets:\n" + new String(octets));
}
Node sourceParent = element.getParentNode();
Node decryptedNode = serializer.deserialize(octets, sourceParent);
// The de-serialiser returns a node whose children we need to take on.
if (sourceParent != null && Node.DOCUMENT_NODE == sourceParent.getNodeType()) {
// If this is a content decryption, this may have problems
contextDocument.removeChild(contextDocument.getDocumentElement());
contextDocument.appendChild(decryptedNode);
} else if (sourceParent != null) {
sourceParent.replaceChild(decryptedNode, element);
}
return contextDocument;
}
/**
*
* @param element
* @return the Node
as a result of the decrypt operation.
* @throws XMLEncryptionException
*/
private Document decryptElementContent(Element element) throws XMLEncryptionException {
Element e =
(Element) element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_ENCRYPTEDDATA
).item(0);
if (null == e) {
throw new XMLEncryptionException("No EncryptedData child element.");
}
return decryptElement(e);
}
/**
* Decrypt an EncryptedData element to a byte array.
*
* When passed in an EncryptedData node, returns the decryption
* as a byte array.
*
* Does not modify the source document.
* @param element
* @return the bytes resulting from the decryption
* @throws XMLEncryptionException
*/
public byte[] decryptToByteArray(Element element) throws XMLEncryptionException {
if (log.isDebugEnabled()) {
log.debug("Decrypting to ByteArray...");
}
if (cipherMode != DECRYPT_MODE) {
log.error("XMLCipher unexpectedly not in DECRYPT_MODE...");
}
EncryptedData encryptedData = factory.newEncryptedData(element);
if (key == null) {
KeyInfo ki = encryptedData.getKeyInfo();
if (ki != null) {
try {
// Add an EncryptedKey resolver
String encMethodAlgorithm = encryptedData.getEncryptionMethod().getAlgorithm();
EncryptedKeyResolver resolver = new EncryptedKeyResolver(encMethodAlgorithm, kek);
if (internalKeyResolvers != null) {
int size = internalKeyResolvers.size();
for (int i = 0; i < size; i++) {
resolver.registerInternalKeyResolver(internalKeyResolvers.get(i));
}
}
ki.registerInternalKeyResolver(resolver);
ki.setSecureValidation(secureValidation);
key = ki.getSecretKey();
} catch (KeyResolverException kre) {
if (log.isDebugEnabled()) {
log.debug(kre);
}
}
}
if (key == null) {
log.error(
"XMLCipher::decryptElement called without a key and unable to resolve"
);
throw new XMLEncryptionException("encryption.nokey");
}
}
// Obtain the encrypted octets
XMLCipherInput cipherInput = new XMLCipherInput(encryptedData);
cipherInput.setSecureValidation(secureValidation);
byte[] encryptedBytes = cipherInput.getBytes();
// Now create the working cipher
String jceAlgorithm =
JCEMapper.translateURItoJCEID(encryptedData.getEncryptionMethod().getAlgorithm());
if (log.isDebugEnabled()) {
log.debug("JCE Algorithm = " + jceAlgorithm);
}
Cipher c;
try {
if (requestedJCEProvider == null) {
c = Cipher.getInstance(jceAlgorithm);
} else {
c = Cipher.getInstance(jceAlgorithm, requestedJCEProvider);
}
} catch (NoSuchAlgorithmException nsae) {
throw new XMLEncryptionException("empty", nsae);
} catch (NoSuchProviderException nspre) {
throw new XMLEncryptionException("empty", nspre);
} catch (NoSuchPaddingException nspae) {
throw new XMLEncryptionException("empty", nspae);
}
// Calculate the IV length and copy out
// For now, we only work with Block ciphers, so this will work.
// This should probably be put into the JCE mapper.
int ivLen = c.getBlockSize();
String alg = encryptedData.getEncryptionMethod().getAlgorithm();
if (AES_128_GCM.equals(alg) || AES_192_GCM.equals(alg) || AES_256_GCM.equals(alg)) {
ivLen = 12;
}
byte[] ivBytes = new byte[ivLen];
// You may be able to pass the entire piece in to IvParameterSpec
// and it will only take the first x bytes, but no way to be certain
// that this will work for every JCE provider, so lets copy the
// necessary bytes into a dedicated array.
System.arraycopy(encryptedBytes, 0, ivBytes, 0, ivLen);
IvParameterSpec iv = new IvParameterSpec(ivBytes);
try {
c.init(cipherMode, key, iv);
} catch (InvalidKeyException ike) {
throw new XMLEncryptionException("empty", ike);
} catch (InvalidAlgorithmParameterException iape) {
throw new XMLEncryptionException("empty", iape);
}
try {
return c.doFinal(encryptedBytes, ivLen, encryptedBytes.length - ivLen);
} catch (IllegalBlockSizeException ibse) {
throw new XMLEncryptionException("empty", ibse);
} catch (BadPaddingException bpe) {
throw new XMLEncryptionException("empty", bpe);
}
}
/*
* Expose the interface for creating XML Encryption objects
*/
/**
* Creates an EncryptedData
Element
.
*
* The newEncryptedData and newEncryptedKey methods create fairly complete
* elements that are immediately useable. All the other create* methods
* return bare elements that still need to be built upon.
*
* An EncryptionMethod will still need to be added however
*
* @param type Either REFERENCE_TYPE or VALUE_TYPE - defines what kind of
* CipherData this EncryptedData will contain.
* @param value the Base 64 encoded, encrypted text to wrap in the
* EncryptedData
or the URI to set in the CipherReference
* (usage will depend on the type
* @return the EncryptedData
Element
.
*
*
* @throws XMLEncryptionException
*/
public EncryptedData createEncryptedData(int type, String value) throws XMLEncryptionException {
EncryptedData result = null;
CipherData data = null;
switch (type) {
case CipherData.REFERENCE_TYPE:
CipherReference cipherReference = factory.newCipherReference(value);
data = factory.newCipherData(type);
data.setCipherReference(cipherReference);
result = factory.newEncryptedData(data);
break;
case CipherData.VALUE_TYPE:
CipherValue cipherValue = factory.newCipherValue(value);
data = factory.newCipherData(type);
data.setCipherValue(cipherValue);
result = factory.newEncryptedData(data);
}
return result;
}
/**
* Creates an EncryptedKey
Element
.
*
* The newEncryptedData and newEncryptedKey methods create fairly complete
* elements that are immediately useable. All the other create* methods
* return bare elements that still need to be built upon.
*
* An EncryptionMethod will still need to be added however
*
* @param type Either REFERENCE_TYPE or VALUE_TYPE - defines what kind of
* CipherData this EncryptedData will contain.
* @param value the Base 64 encoded, encrypted text to wrap in the
* EncryptedKey
or the URI to set in the CipherReference
* (usage will depend on the type
* @return the EncryptedKey
Element
.
*
*
* @throws XMLEncryptionException
*/
public EncryptedKey createEncryptedKey(int type, String value) throws XMLEncryptionException {
EncryptedKey result = null;
CipherData data = null;
switch (type) {
case CipherData.REFERENCE_TYPE:
CipherReference cipherReference = factory.newCipherReference(value);
data = factory.newCipherData(type);
data.setCipherReference(cipherReference);
result = factory.newEncryptedKey(data);
break;
case CipherData.VALUE_TYPE:
CipherValue cipherValue = factory.newCipherValue(value);
data = factory.newCipherData(type);
data.setCipherValue(cipherValue);
result = factory.newEncryptedKey(data);
}
return result;
}
/**
* Create an AgreementMethod object
*
* @param algorithm Algorithm of the agreement method
* @return a new AgreementMethod
*/
public AgreementMethod createAgreementMethod(String algorithm) {
return factory.newAgreementMethod(algorithm);
}
/**
* Create a CipherData object
*
* @param type Type of this CipherData (either VALUE_TUPE or
* REFERENCE_TYPE)
* @return a new CipherData
*/
public CipherData createCipherData(int type) {
return factory.newCipherData(type);
}
/**
* Create a CipherReference object
*
* @param uri The URI that the reference will refer
* @return a new CipherReference
*/
public CipherReference createCipherReference(String uri) {
return factory.newCipherReference(uri);
}
/**
* Create a CipherValue element
*
* @param value The value to set the ciphertext to
* @return a new CipherValue
*/
public CipherValue createCipherValue(String value) {
return factory.newCipherValue(value);
}
/**
* Create an EncryptionMethod object
*
* @param algorithm Algorithm for the encryption
* @return a new EncryptionMethod
*/
public EncryptionMethod createEncryptionMethod(String algorithm) {
return factory.newEncryptionMethod(algorithm);
}
/**
* Create an EncryptionProperties element
* @return a new EncryptionProperties
*/
public EncryptionProperties createEncryptionProperties() {
return factory.newEncryptionProperties();
}
/**
* Create a new EncryptionProperty element
* @return a new EncryptionProperty
*/
public EncryptionProperty createEncryptionProperty() {
return factory.newEncryptionProperty();
}
/**
* Create a new ReferenceList object
* @param type ReferenceList.DATA_REFERENCE or ReferenceList.KEY_REFERENCE
* @return a new ReferenceList
*/
public ReferenceList createReferenceList(int type) {
return factory.newReferenceList(type);
}
/**
* Create a new Transforms object
*
* Note: A context document must have been set
* elsewhere (possibly via a call to doFinal). If not, use the
* createTransforms(Document) method.
* @return a new Transforms
*/
public Transforms createTransforms() {
return factory.newTransforms();
}
/**
* Create a new Transforms object
*
* Because the handling of Transforms is currently done in the signature
* code, the creation of a Transforms object requires a
* context document.
*
* @param doc Document that will own the created Transforms node
* @return a new Transforms
*/
public Transforms createTransforms(Document doc) {
return factory.newTransforms(doc);
}
/**
*
* @author Axl Mattheus
*/
private class Factory {
/**
* @param algorithm
* @return a new AgreementMethod
*/
AgreementMethod newAgreementMethod(String algorithm) {
return new AgreementMethodImpl(algorithm);
}
/**
* @param type
* @return a new CipherData
*
*/
CipherData newCipherData(int type) {
return new CipherDataImpl(type);
}
/**
* @param uri
* @return a new CipherReference
*/
CipherReference newCipherReference(String uri) {
return new CipherReferenceImpl(uri);
}
/**
* @param value
* @return a new CipherValue
*/
CipherValue newCipherValue(String value) {
return new CipherValueImpl(value);
}
/*
CipherValue newCipherValue(byte[] value) {
return new CipherValueImpl(value);
}
*/
/**
* @param data
* @return a new EncryptedData
*/
EncryptedData newEncryptedData(CipherData data) {
return new EncryptedDataImpl(data);
}
/**
* @param data
* @return a new EncryptedKey
*/
EncryptedKey newEncryptedKey(CipherData data) {
return new EncryptedKeyImpl(data);
}
/**
* @param algorithm
* @return a new EncryptionMethod
*/
EncryptionMethod newEncryptionMethod(String algorithm) {
return new EncryptionMethodImpl(algorithm);
}
/**
* @return a new EncryptionProperties
*/
EncryptionProperties newEncryptionProperties() {
return new EncryptionPropertiesImpl();
}
/**
* @return a new EncryptionProperty
*/
EncryptionProperty newEncryptionProperty() {
return new EncryptionPropertyImpl();
}
/**
* @param type ReferenceList.DATA_REFERENCE or ReferenceList.KEY_REFERENCE
* @return a new ReferenceList
*/
ReferenceList newReferenceList(int type) {
return new ReferenceListImpl(type);
}
/**
* @return a new Transforms
*/
Transforms newTransforms() {
return new TransformsImpl();
}
/**
* @param doc
* @return a new Transforms
*/
Transforms newTransforms(Document doc) {
return new TransformsImpl(doc);
}
/**
* @param element
* @return a new CipherData
* @throws XMLEncryptionException
*/
CipherData newCipherData(Element element) throws XMLEncryptionException {
if (null == element) {
throw new NullPointerException("element is null");
}
int type = 0;
Element e = null;
if (element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_CIPHERVALUE).getLength() > 0
) {
type = CipherData.VALUE_TYPE;
e = (Element) element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_CIPHERVALUE).item(0);
} else if (element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_CIPHERREFERENCE).getLength() > 0) {
type = CipherData.REFERENCE_TYPE;
e = (Element) element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_CIPHERREFERENCE).item(0);
}
CipherData result = newCipherData(type);
if (type == CipherData.VALUE_TYPE) {
result.setCipherValue(newCipherValue(e));
} else if (type == CipherData.REFERENCE_TYPE) {
result.setCipherReference(newCipherReference(e));
}
return result;
}
/**
* @param element
* @return a new CipherReference
* @throws XMLEncryptionException
*
*/
CipherReference newCipherReference(Element element) throws XMLEncryptionException {
Attr uriAttr =
element.getAttributeNodeNS(null, EncryptionConstants._ATT_URI);
CipherReference result = new CipherReferenceImpl(uriAttr);
// Find any Transforms
NodeList transformsElements =
element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS, EncryptionConstants._TAG_TRANSFORMS);
Element transformsElement = (Element) transformsElements.item(0);
if (transformsElement != null) {
if (log.isDebugEnabled()) {
log.debug("Creating a DSIG based Transforms element");
}
try {
result.setTransforms(new TransformsImpl(transformsElement));
} catch (XMLSignatureException xse) {
throw new XMLEncryptionException("empty", xse);
} catch (InvalidTransformException ite) {
throw new XMLEncryptionException("empty", ite);
} catch (XMLSecurityException xse) {
throw new XMLEncryptionException("empty", xse);
}
}
return result;
}
/**
* @param element
* @return a new CipherValue
*/
CipherValue newCipherValue(Element element) {
String value = XMLUtils.getFullTextChildrenFromElement(element);
return newCipherValue(value);
}
/**
* @param element
* @return a new EncryptedData
* @throws XMLEncryptionException
*
*/
EncryptedData newEncryptedData(Element element) throws XMLEncryptionException {
EncryptedData result = null;
NodeList dataElements =
element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS, EncryptionConstants._TAG_CIPHERDATA);
// Need to get the last CipherData found, as earlier ones will
// be for elements in the KeyInfo lists
Element dataElement =
(Element) dataElements.item(dataElements.getLength() - 1);
CipherData data = newCipherData(dataElement);
result = newEncryptedData(data);
result.setId(element.getAttributeNS(null, EncryptionConstants._ATT_ID));
result.setType(element.getAttributeNS(null, EncryptionConstants._ATT_TYPE));
result.setMimeType(element.getAttributeNS(null, EncryptionConstants._ATT_MIMETYPE));
result.setEncoding( element.getAttributeNS(null, Constants._ATT_ENCODING));
Element encryptionMethodElement =
(Element) element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_ENCRYPTIONMETHOD).item(0);
if (null != encryptionMethodElement) {
result.setEncryptionMethod(newEncryptionMethod(encryptionMethodElement));
}
// BFL 16/7/03 - simple implementation
// TODO: Work out how to handle relative URI
Element keyInfoElement =
(Element) element.getElementsByTagNameNS(
Constants.SignatureSpecNS, Constants._TAG_KEYINFO).item(0);
if (null != keyInfoElement) {
KeyInfo ki = newKeyInfo(keyInfoElement);
result.setKeyInfo(ki);
}
// TODO: Implement
Element encryptionPropertiesElement =
(Element) element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_ENCRYPTIONPROPERTIES).item(0);
if (null != encryptionPropertiesElement) {
result.setEncryptionProperties(
newEncryptionProperties(encryptionPropertiesElement)
);
}
return result;
}
/**
* @param element
* @return a new EncryptedKey
* @throws XMLEncryptionException
*/
EncryptedKey newEncryptedKey(Element element) throws XMLEncryptionException {
EncryptedKey result = null;
NodeList dataElements =
element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS, EncryptionConstants._TAG_CIPHERDATA);
Element dataElement =
(Element) dataElements.item(dataElements.getLength() - 1);
CipherData data = newCipherData(dataElement);
result = newEncryptedKey(data);
result.setId(element.getAttributeNS(null, EncryptionConstants._ATT_ID));
result.setType(element.getAttributeNS(null, EncryptionConstants._ATT_TYPE));
result.setMimeType(element.getAttributeNS(null, EncryptionConstants._ATT_MIMETYPE));
result.setEncoding(element.getAttributeNS(null, Constants._ATT_ENCODING));
result.setRecipient(element.getAttributeNS(null, EncryptionConstants._ATT_RECIPIENT));
Element encryptionMethodElement =
(Element) element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_ENCRYPTIONMETHOD).item(0);
if (null != encryptionMethodElement) {
result.setEncryptionMethod(newEncryptionMethod(encryptionMethodElement));
}
Element keyInfoElement =
(Element) element.getElementsByTagNameNS(
Constants.SignatureSpecNS, Constants._TAG_KEYINFO).item(0);
if (null != keyInfoElement) {
KeyInfo ki = newKeyInfo(keyInfoElement);
result.setKeyInfo(ki);
}
// TODO: Implement
Element encryptionPropertiesElement =
(Element) element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_ENCRYPTIONPROPERTIES).item(0);
if (null != encryptionPropertiesElement) {
result.setEncryptionProperties(
newEncryptionProperties(encryptionPropertiesElement)
);
}
Element referenceListElement =
(Element) element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_REFERENCELIST).item(0);
if (null != referenceListElement) {
result.setReferenceList(newReferenceList(referenceListElement));
}
Element carriedNameElement =
(Element) element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_CARRIEDKEYNAME).item(0);
if (null != carriedNameElement) {
result.setCarriedName(carriedNameElement.getFirstChild().getNodeValue());
}
return result;
}
/**
* @param element
* @return a new KeyInfo
* @throws XMLEncryptionException
*/
KeyInfo newKeyInfo(Element element) throws XMLEncryptionException {
try {
KeyInfo ki = new KeyInfo(element, null);
ki.setSecureValidation(secureValidation);
if (internalKeyResolvers != null) {
int size = internalKeyResolvers.size();
for (int i = 0; i < size; i++) {
ki.registerInternalKeyResolver(internalKeyResolvers.get(i));
}
}
return ki;
} catch (XMLSecurityException xse) {
throw new XMLEncryptionException("Error loading Key Info", xse);
}
}
/**
* @param element
* @return a new EncryptionMethod
*/
EncryptionMethod newEncryptionMethod(Element element) {
String encAlgorithm = element.getAttributeNS(null, EncryptionConstants._ATT_ALGORITHM);
EncryptionMethod result = newEncryptionMethod(encAlgorithm);
Element keySizeElement =
(Element) element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_KEYSIZE).item(0);
if (null != keySizeElement) {
result.setKeySize(
Integer.valueOf(
keySizeElement.getFirstChild().getNodeValue()).intValue());
}
Element oaepParamsElement =
(Element) element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_OAEPPARAMS).item(0);
if (null != oaepParamsElement) {
try {
String oaepParams = oaepParamsElement.getFirstChild().getNodeValue();
result.setOAEPparams(Base64.decode(oaepParams.getBytes("UTF-8")));
} catch(UnsupportedEncodingException e) {
throw new RuntimeException("UTF-8 not supported", e);
} catch (Base64DecodingException e) {
throw new RuntimeException("BASE-64 decoding error", e);
}
}
Element digestElement =
(Element) element.getElementsByTagNameNS(
Constants.SignatureSpecNS, Constants._TAG_DIGESTMETHOD).item(0);
if (digestElement != null) {
String digestAlgorithm = digestElement.getAttributeNS(null, "Algorithm");
result.setDigestAlgorithm(digestAlgorithm);
}
Element mgfElement =
(Element) element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpec11NS, EncryptionConstants._TAG_MGF).item(0);
if (mgfElement != null && !XMLCipher.RSA_OAEP.equals(algorithm)) {
String mgfAlgorithm = mgfElement.getAttributeNS(null, "Algorithm");
result.setMGFAlgorithm(mgfAlgorithm);
}
// TODO: Make this mess work
//
return result;
}
/**
* @param element
* @return a new EncryptionProperties
*/
EncryptionProperties newEncryptionProperties(Element element) {
EncryptionProperties result = newEncryptionProperties();
result.setId(element.getAttributeNS(null, EncryptionConstants._ATT_ID));
NodeList encryptionPropertyList =
element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_ENCRYPTIONPROPERTY);
for (int i = 0; i < encryptionPropertyList.getLength(); i++) {
Node n = encryptionPropertyList.item(i);
if (null != n) {
result.addEncryptionProperty(newEncryptionProperty((Element) n));
}
}
return result;
}
/**
* @param element
* @return a new EncryptionProperty
*/
EncryptionProperty newEncryptionProperty(Element element) {
EncryptionProperty result = newEncryptionProperty();
result.setTarget(element.getAttributeNS(null, EncryptionConstants._ATT_TARGET));
result.setId(element.getAttributeNS(null, EncryptionConstants._ATT_ID));
// TODO: Make this lot work...
//
// TODO: Make this work...
//
return result;
}
/**
* @param element
* @return a new ReferenceList
*/
ReferenceList newReferenceList(Element element) {
int type = 0;
if (null != element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_DATAREFERENCE).item(0)) {
type = ReferenceList.DATA_REFERENCE;
} else if (null != element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_KEYREFERENCE).item(0)) {
type = ReferenceList.KEY_REFERENCE;
}
ReferenceList result = new ReferenceListImpl(type);
NodeList list = null;
switch (type) {
case ReferenceList.DATA_REFERENCE:
list =
element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_DATAREFERENCE);
for (int i = 0; i < list.getLength() ; i++) {
String uri = ((Element) list.item(i)).getAttributeNS(null, "URI");
result.add(result.newDataReference(uri));
}
break;
case ReferenceList.KEY_REFERENCE:
list =
element.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_KEYREFERENCE);
for (int i = 0; i < list.getLength() ; i++) {
String uri = ((Element) list.item(i)).getAttributeNS(null, "URI");
result.add(result.newKeyReference(uri));
}
}
return result;
}
/**
* @param encryptedData
* @return the XML Element form of that EncryptedData
*/
Element toElement(EncryptedData encryptedData) {
return ((EncryptedDataImpl) encryptedData).toElement();
}
/**
* @param encryptedKey
* @return the XML Element form of that EncryptedKey
*/
Element toElement(EncryptedKey encryptedKey) {
return ((EncryptedKeyImpl) encryptedKey).toElement();
}
/**
* @param referenceList
* @return the XML Element form of that ReferenceList
*/
Element toElement(ReferenceList referenceList) {
return ((ReferenceListImpl) referenceList).toElement();
}
private class AgreementMethodImpl implements AgreementMethod {
private byte[] kaNonce = null;
private List agreementMethodInformation = null;
private KeyInfo originatorKeyInfo = null;
private KeyInfo recipientKeyInfo = null;
private String algorithmURI = null;
/**
* @param algorithm
*/
public AgreementMethodImpl(String algorithm) {
agreementMethodInformation = new LinkedList();
URI tmpAlgorithm = null;
try {
tmpAlgorithm = new URI(algorithm);
} catch (URISyntaxException ex) {
throw (IllegalArgumentException)
new IllegalArgumentException().initCause(ex);
}
algorithmURI = tmpAlgorithm.toString();
}
/** @inheritDoc */
public byte[] getKANonce() {
return kaNonce;
}
/** @inheritDoc */
public void setKANonce(byte[] kanonce) {
kaNonce = kanonce;
}
/** @inheritDoc */
public Iterator getAgreementMethodInformation() {
return agreementMethodInformation.iterator();
}
/** @inheritDoc */
public void addAgreementMethodInformation(Element info) {
agreementMethodInformation.add(info);
}
/** @inheritDoc */
public void revoveAgreementMethodInformation(Element info) {
agreementMethodInformation.remove(info);
}
/** @inheritDoc */
public KeyInfo getOriginatorKeyInfo() {
return originatorKeyInfo;
}
/** @inheritDoc */
public void setOriginatorKeyInfo(KeyInfo keyInfo) {
originatorKeyInfo = keyInfo;
}
/** @inheritDoc */
public KeyInfo getRecipientKeyInfo() {
return recipientKeyInfo;
}
/** @inheritDoc */
public void setRecipientKeyInfo(KeyInfo keyInfo) {
recipientKeyInfo = keyInfo;
}
/** @inheritDoc */
public String getAlgorithm() {
return algorithmURI;
}
}
private class CipherDataImpl implements CipherData {
private static final String valueMessage =
"Data type is reference type.";
private static final String referenceMessage =
"Data type is value type.";
private CipherValue cipherValue = null;
private CipherReference cipherReference = null;
private int cipherType = Integer.MIN_VALUE;
/**
* @param type
*/
public CipherDataImpl(int type) {
cipherType = type;
}
/** @inheritDoc */
public CipherValue getCipherValue() {
return cipherValue;
}
/** @inheritDoc */
public void setCipherValue(CipherValue value) throws XMLEncryptionException {
if (cipherType == REFERENCE_TYPE) {
throw new XMLEncryptionException(
"empty", new UnsupportedOperationException(valueMessage)
);
}
cipherValue = value;
}
/** @inheritDoc */
public CipherReference getCipherReference() {
return cipherReference;
}
/** @inheritDoc */
public void setCipherReference(CipherReference reference) throws
XMLEncryptionException {
if (cipherType == VALUE_TYPE) {
throw new XMLEncryptionException(
"empty", new UnsupportedOperationException(referenceMessage)
);
}
cipherReference = reference;
}
/** @inheritDoc */
public int getDataType() {
return cipherType;
}
Element toElement() {
Element result =
XMLUtils.createElementInEncryptionSpace(
contextDocument, EncryptionConstants._TAG_CIPHERDATA
);
if (cipherType == VALUE_TYPE) {
result.appendChild(((CipherValueImpl) cipherValue).toElement());
} else if (cipherType == REFERENCE_TYPE) {
result.appendChild(((CipherReferenceImpl) cipherReference).toElement());
}
return result;
}
}
private class CipherReferenceImpl implements CipherReference {
private String referenceURI = null;
private Transforms referenceTransforms = null;
private Attr referenceNode = null;
/**
* @param uri
*/
public CipherReferenceImpl(String uri) {
/* Don't check validity of URI as may be "" */
referenceURI = uri;
referenceNode = null;
}
/**
* @param uri
*/
public CipherReferenceImpl(Attr uri) {
referenceURI = uri.getNodeValue();
referenceNode = uri;
}
/** @inheritDoc */
public String getURI() {
return referenceURI;
}
/** @inheritDoc */
public Attr getURIAsAttr() {
return referenceNode;
}
/** @inheritDoc */
public Transforms getTransforms() {
return referenceTransforms;
}
/** @inheritDoc */
public void setTransforms(Transforms transforms) {
referenceTransforms = transforms;
}
Element toElement() {
Element result =
XMLUtils.createElementInEncryptionSpace(
contextDocument, EncryptionConstants._TAG_CIPHERREFERENCE
);
result.setAttributeNS(null, EncryptionConstants._ATT_URI, referenceURI);
if (null != referenceTransforms) {
result.appendChild(((TransformsImpl) referenceTransforms).toElement());
}
return result;
}
}
private class CipherValueImpl implements CipherValue {
private String cipherValue = null;
/**
* @param value
*/
public CipherValueImpl(String value) {
cipherValue = value;
}
/** @inheritDoc */
public String getValue() {
return cipherValue;
}
/** @inheritDoc */
public void setValue(String value) {
cipherValue = value;
}
Element toElement() {
Element result =
XMLUtils.createElementInEncryptionSpace(
contextDocument, EncryptionConstants._TAG_CIPHERVALUE
);
result.appendChild(contextDocument.createTextNode(cipherValue));
return result;
}
}
private class EncryptedDataImpl extends EncryptedTypeImpl implements EncryptedData {
/**
* @param data
*/
public EncryptedDataImpl(CipherData data) {
super(data);
}
Element toElement() {
Element result =
ElementProxy.createElementForFamily(
contextDocument, EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_ENCRYPTEDDATA
);
if (null != super.getId()) {
result.setAttributeNS(null, EncryptionConstants._ATT_ID, super.getId());
}
if (null != super.getType()) {
result.setAttributeNS(null, EncryptionConstants._ATT_TYPE, super.getType());
}
if (null != super.getMimeType()) {
result.setAttributeNS(
null, EncryptionConstants._ATT_MIMETYPE, super.getMimeType()
);
}
if (null != super.getEncoding()) {
result.setAttributeNS(
null, EncryptionConstants._ATT_ENCODING, super.getEncoding()
);
}
if (null != super.getEncryptionMethod()) {
result.appendChild(
((EncryptionMethodImpl)super.getEncryptionMethod()).toElement()
);
}
if (null != super.getKeyInfo()) {
result.appendChild(super.getKeyInfo().getElement().cloneNode(true));
}
result.appendChild(((CipherDataImpl) super.getCipherData()).toElement());
if (null != super.getEncryptionProperties()) {
result.appendChild(((EncryptionPropertiesImpl)
super.getEncryptionProperties()).toElement());
}
return result;
}
}
private class EncryptedKeyImpl extends EncryptedTypeImpl implements EncryptedKey {
private String keyRecipient = null;
private ReferenceList referenceList = null;
private String carriedName = null;
/**
* @param data
*/
public EncryptedKeyImpl(CipherData data) {
super(data);
}
/** @inheritDoc */
public String getRecipient() {
return keyRecipient;
}
/** @inheritDoc */
public void setRecipient(String recipient) {
keyRecipient = recipient;
}
/** @inheritDoc */
public ReferenceList getReferenceList() {
return referenceList;
}
/** @inheritDoc */
public void setReferenceList(ReferenceList list) {
referenceList = list;
}
/** @inheritDoc */
public String getCarriedName() {
return carriedName;
}
/** @inheritDoc */
public void setCarriedName(String name) {
carriedName = name;
}
Element toElement() {
Element result =
ElementProxy.createElementForFamily(
contextDocument, EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_ENCRYPTEDKEY
);
if (null != super.getId()) {
result.setAttributeNS(null, EncryptionConstants._ATT_ID, super.getId());
}
if (null != super.getType()) {
result.setAttributeNS(null, EncryptionConstants._ATT_TYPE, super.getType());
}
if (null != super.getMimeType()) {
result.setAttributeNS(
null, EncryptionConstants._ATT_MIMETYPE, super.getMimeType()
);
}
if (null != super.getEncoding()) {
result.setAttributeNS(null, Constants._ATT_ENCODING, super.getEncoding());
}
if (null != getRecipient()) {
result.setAttributeNS(
null, EncryptionConstants._ATT_RECIPIENT, getRecipient()
);
}
if (null != super.getEncryptionMethod()) {
result.appendChild(((EncryptionMethodImpl)
super.getEncryptionMethod()).toElement());
}
if (null != super.getKeyInfo()) {
result.appendChild(super.getKeyInfo().getElement().cloneNode(true));
}
result.appendChild(((CipherDataImpl) super.getCipherData()).toElement());
if (null != super.getEncryptionProperties()) {
result.appendChild(((EncryptionPropertiesImpl)
super.getEncryptionProperties()).toElement());
}
if (referenceList != null && !referenceList.isEmpty()) {
result.appendChild(((ReferenceListImpl)getReferenceList()).toElement());
}
if (null != carriedName) {
Element element =
ElementProxy.createElementForFamily(
contextDocument,
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_CARRIEDKEYNAME
);
Node node = contextDocument.createTextNode(carriedName);
element.appendChild(node);
result.appendChild(element);
}
return result;
}
}
private abstract class EncryptedTypeImpl {
private String id = null;
private String type = null;
private String mimeType = null;
private String encoding = null;
private EncryptionMethod encryptionMethod = null;
private KeyInfo keyInfo = null;
private CipherData cipherData = null;
private EncryptionProperties encryptionProperties = null;
/**
* Constructor.
* @param data
*/
protected EncryptedTypeImpl(CipherData data) {
cipherData = data;
}
/**
*
* @return the Id
*/
public String getId() {
return id;
}
/**
*
* @param id
*/
public void setId(String id) {
this.id = id;
}
/**
*
* @return the type
*/
public String getType() {
return type;
}
/**
*
* @param type
*/
public void setType(String type) {
if (type == null || type.length() == 0) {
this.type = null;
} else {
URI tmpType = null;
try {
tmpType = new URI(type);
} catch (URISyntaxException ex) {
throw (IllegalArgumentException)
new IllegalArgumentException().initCause(ex);
}
this.type = tmpType.toString();
}
}
/**
*
* @return the MimeType
*/
public String getMimeType() {
return mimeType;
}
/**
*
* @param type
*/
public void setMimeType(String type) {
mimeType = type;
}
/**
*
* @return the encoding
*/
public String getEncoding() {
return encoding;
}
/**
*
* @param encoding
*/
public void setEncoding(String encoding) {
if (encoding == null || encoding.length() == 0) {
this.encoding = null;
} else {
URI tmpEncoding = null;
try {
tmpEncoding = new URI(encoding);
} catch (URISyntaxException ex) {
throw (IllegalArgumentException)
new IllegalArgumentException().initCause(ex);
}
this.encoding = tmpEncoding.toString();
}
}
/**
*
* @return the EncryptionMethod
*/
public EncryptionMethod getEncryptionMethod() {
return encryptionMethod;
}
/**
*
* @param method
*/
public void setEncryptionMethod(EncryptionMethod method) {
encryptionMethod = method;
}
/**
*
* @return the KeyInfo
*/
public KeyInfo getKeyInfo() {
return keyInfo;
}
/**
*
* @param info
*/
public void setKeyInfo(KeyInfo info) {
keyInfo = info;
}
/**
*
* @return the CipherData
*/
public CipherData getCipherData() {
return cipherData;
}
/**
*
* @return the EncryptionProperties
*/
public EncryptionProperties getEncryptionProperties() {
return encryptionProperties;
}
/**
*
* @param properties
*/
public void setEncryptionProperties(EncryptionProperties properties) {
encryptionProperties = properties;
}
}
private class EncryptionMethodImpl implements EncryptionMethod {
private String algorithm = null;
private int keySize = Integer.MIN_VALUE;
private byte[] oaepParams = null;
private List encryptionMethodInformation = null;
private String digestAlgorithm = null;
private String mgfAlgorithm = null;
/**
* Constructor.
* @param algorithm
*/
public EncryptionMethodImpl(String algorithm) {
URI tmpAlgorithm = null;
try {
tmpAlgorithm = new URI(algorithm);
} catch (URISyntaxException ex) {
throw (IllegalArgumentException)
new IllegalArgumentException().initCause(ex);
}
this.algorithm = tmpAlgorithm.toString();
encryptionMethodInformation = new LinkedList();
}
/** @inheritDoc */
public String getAlgorithm() {
return algorithm;
}
/** @inheritDoc */
public int getKeySize() {
return keySize;
}
/** @inheritDoc */
public void setKeySize(int size) {
keySize = size;
}
/** @inheritDoc */
public byte[] getOAEPparams() {
return oaepParams;
}
/** @inheritDoc */
public void setOAEPparams(byte[] params) {
oaepParams = params;
}
/** @inheritDoc */
public void setDigestAlgorithm(String digestAlgorithm) {
this.digestAlgorithm = digestAlgorithm;
}
/** @inheritDoc */
public String getDigestAlgorithm() {
return digestAlgorithm;
}
/** @inheritDoc */
public void setMGFAlgorithm(String mgfAlgorithm) {
this.mgfAlgorithm = mgfAlgorithm;
}
/** @inheritDoc */
public String getMGFAlgorithm() {
return mgfAlgorithm;
}
/** @inheritDoc */
public Iterator getEncryptionMethodInformation() {
return encryptionMethodInformation.iterator();
}
/** @inheritDoc */
public void addEncryptionMethodInformation(Element info) {
encryptionMethodInformation.add(info);
}
/** @inheritDoc */
public void removeEncryptionMethodInformation(Element info) {
encryptionMethodInformation.remove(info);
}
Element toElement() {
Element result =
XMLUtils.createElementInEncryptionSpace(
contextDocument, EncryptionConstants._TAG_ENCRYPTIONMETHOD
);
result.setAttributeNS(null, EncryptionConstants._ATT_ALGORITHM, algorithm);
if (keySize > 0) {
result.appendChild(
XMLUtils.createElementInEncryptionSpace(
contextDocument, EncryptionConstants._TAG_KEYSIZE
).appendChild(contextDocument.createTextNode(String.valueOf(keySize))));
}
if (null != oaepParams) {
Element oaepElement =
XMLUtils.createElementInEncryptionSpace(
contextDocument, EncryptionConstants._TAG_OAEPPARAMS
);
oaepElement.appendChild(contextDocument.createTextNode(Base64.encode(oaepParams)));
result.appendChild(oaepElement);
}
if (digestAlgorithm != null) {
Element digestElement =
XMLUtils.createElementInSignatureSpace(contextDocument, Constants._TAG_DIGESTMETHOD);
digestElement.setAttributeNS(null, "Algorithm", digestAlgorithm);
result.appendChild(digestElement);
}
if (mgfAlgorithm != null) {
Element mgfElement =
XMLUtils.createElementInEncryption11Space(
contextDocument, EncryptionConstants._TAG_MGF
);
mgfElement.setAttributeNS(null, "Algorithm", mgfAlgorithm);
mgfElement.setAttributeNS(
Constants.NamespaceSpecNS,
"xmlns:" + ElementProxy.getDefaultPrefix(EncryptionConstants.EncryptionSpec11NS),
EncryptionConstants.EncryptionSpec11NS
);
result.appendChild(mgfElement);
}
Iterator itr = encryptionMethodInformation.iterator();
while (itr.hasNext()) {
result.appendChild(itr.next());
}
return result;
}
}
private class EncryptionPropertiesImpl implements EncryptionProperties {
private String id = null;
private List encryptionProperties = null;
/**
* Constructor.
*/
public EncryptionPropertiesImpl() {
encryptionProperties = new LinkedList();
}
/** @inheritDoc */
public String getId() {
return id;
}
/** @inheritDoc */
public void setId(String id) {
this.id = id;
}
/** @inheritDoc */
public Iterator getEncryptionProperties() {
return encryptionProperties.iterator();
}
/** @inheritDoc */
public void addEncryptionProperty(EncryptionProperty property) {
encryptionProperties.add(property);
}
/** @inheritDoc */
public void removeEncryptionProperty(EncryptionProperty property) {
encryptionProperties.remove(property);
}
Element toElement() {
Element result =
XMLUtils.createElementInEncryptionSpace(
contextDocument, EncryptionConstants._TAG_ENCRYPTIONPROPERTIES
);
if (null != id) {
result.setAttributeNS(null, EncryptionConstants._ATT_ID, id);
}
Iterator itr = getEncryptionProperties();
while (itr.hasNext()) {
result.appendChild(((EncryptionPropertyImpl)itr.next()).toElement());
}
return result;
}
}
private class EncryptionPropertyImpl implements EncryptionProperty {
private String target = null;
private String id = null;
private Map attributeMap = new HashMap();
private List encryptionInformation = null;
/**
* Constructor.
*/
public EncryptionPropertyImpl() {
encryptionInformation = new LinkedList();
}
/** @inheritDoc */
public String getTarget() {
return target;
}
/** @inheritDoc */
public void setTarget(String target) {
if (target == null || target.length() == 0) {
this.target = null;
} else if (target.startsWith("#")) {
/*
* This is a same document URI reference. Do not parse,
* because it has no scheme.
*/
this.target = target;
} else {
URI tmpTarget = null;
try {
tmpTarget = new URI(target);
} catch (URISyntaxException ex) {
throw (IllegalArgumentException)
new IllegalArgumentException().initCause(ex);
}
this.target = tmpTarget.toString();
}
}
/** @inheritDoc */
public String getId() {
return id;
}
/** @inheritDoc */
public void setId(String id) {
this.id = id;
}
/** @inheritDoc */
public String getAttribute(String attribute) {
return attributeMap.get(attribute);
}
/** @inheritDoc */
public void setAttribute(String attribute, String value) {
attributeMap.put(attribute, value);
}
/** @inheritDoc */
public Iterator getEncryptionInformation() {
return encryptionInformation.iterator();
}
/** @inheritDoc */
public void addEncryptionInformation(Element info) {
encryptionInformation.add(info);
}
/** @inheritDoc */
public void removeEncryptionInformation(Element info) {
encryptionInformation.remove(info);
}
Element toElement() {
Element result =
XMLUtils.createElementInEncryptionSpace(
contextDocument, EncryptionConstants._TAG_ENCRYPTIONPROPERTY
);
if (null != target) {
result.setAttributeNS(null, EncryptionConstants._ATT_TARGET, target);
}
if (null != id) {
result.setAttributeNS(null, EncryptionConstants._ATT_ID, id);
}
// TODO: figure out the anyAttribyte stuff...
// TODO: figure out the any stuff...
return result;
}
}
private class TransformsImpl extends org.apache.xml.security.transforms.Transforms
implements Transforms {
/**
* Construct Transforms
*/
public TransformsImpl() {
super(contextDocument);
}
/**
*
* @param doc
*/
public TransformsImpl(Document doc) {
if (doc == null) {
throw new RuntimeException("Document is null");
}
this.doc = doc;
this.constructionElement =
createElementForFamilyLocal(
this.doc, this.getBaseNamespace(), this.getBaseLocalName()
);
}
/**
*
* @param element
* @throws XMLSignatureException
* @throws InvalidTransformException
* @throws XMLSecurityException
* @throws TransformationException
*/
public TransformsImpl(Element element)
throws XMLSignatureException, InvalidTransformException,
XMLSecurityException, TransformationException {
super(element, "");
}
/**
*
* @return the XML Element form of that Transforms
*/
public Element toElement() {
if (doc == null) {
doc = contextDocument;
}
return getElement();
}
/** @inheritDoc */
public org.apache.xml.security.transforms.Transforms getDSTransforms() {
return this;
}
// Over-ride the namespace
/** @inheritDoc */
public String getBaseNamespace() {
return EncryptionConstants.EncryptionSpecNS;
}
}
private class ReferenceListImpl implements ReferenceList {
private Class> sentry;
private List references;
/**
* Constructor.
* @param type
*/
public ReferenceListImpl(int type) {
if (type == ReferenceList.DATA_REFERENCE) {
sentry = DataReference.class;
} else if (type == ReferenceList.KEY_REFERENCE) {
sentry = KeyReference.class;
} else {
throw new IllegalArgumentException();
}
references = new LinkedList();
}
/** @inheritDoc */
public void add(Reference reference) {
if (!reference.getClass().equals(sentry)) {
throw new IllegalArgumentException();
}
references.add(reference);
}
/** @inheritDoc */
public void remove(Reference reference) {
if (!reference.getClass().equals(sentry)) {
throw new IllegalArgumentException();
}
references.remove(reference);
}
/** @inheritDoc */
public int size() {
return references.size();
}
/** @inheritDoc */
public boolean isEmpty() {
return references.isEmpty();
}
/** @inheritDoc */
public Iterator getReferences() {
return references.iterator();
}
Element toElement() {
Element result =
ElementProxy.createElementForFamily(
contextDocument,
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_REFERENCELIST
);
Iterator eachReference = references.iterator();
while (eachReference.hasNext()) {
Reference reference = eachReference.next();
result.appendChild(((ReferenceImpl) reference).toElement());
}
return result;
}
/** @inheritDoc */
public Reference newDataReference(String uri) {
return new DataReference(uri);
}
/** @inheritDoc */
public Reference newKeyReference(String uri) {
return new KeyReference(uri);
}
/**
* ReferenceImpl
is an implementation of
* Reference
.
*
* @see Reference
*/
private abstract class ReferenceImpl implements Reference {
private String uri;
private List referenceInformation;
ReferenceImpl(String uri) {
this.uri = uri;
referenceInformation = new LinkedList();
}
/** @inheritDoc */
public abstract String getType();
/** @inheritDoc */
public String getURI() {
return uri;
}
/** @inheritDoc */
public Iterator getElementRetrievalInformation() {
return referenceInformation.iterator();
}
/** @inheritDoc */
public void setURI(String uri) {
this.uri = uri;
}
/** @inheritDoc */
public void removeElementRetrievalInformation(Element node) {
referenceInformation.remove(node);
}
/** @inheritDoc */
public void addElementRetrievalInformation(Element node) {
referenceInformation.add(node);
}
/**
* @return the XML Element form of that Reference
*/
public Element toElement() {
String tagName = getType();
Element result =
ElementProxy.createElementForFamily(
contextDocument,
EncryptionConstants.EncryptionSpecNS,
tagName
);
result.setAttributeNS(null, EncryptionConstants._ATT_URI, uri);
// TODO: Need to martial referenceInformation
// Figure out how to make this work..
//
return result;
}
}
private class DataReference extends ReferenceImpl {
DataReference(String uri) {
super(uri);
}
/** @inheritDoc */
public String getType() {
return EncryptionConstants._TAG_DATAREFERENCE;
}
}
private class KeyReference extends ReferenceImpl {
KeyReference(String uri) {
super(uri);
}
/** @inheritDoc */
public String getType() {
return EncryptionConstants._TAG_KEYREFERENCE;
}
}
}
}
}