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

org.opensaml.saml.saml2.encryption.Encrypter Maven / Gradle / Ivy

The newest version!
/*
 * 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.saml.saml2.encryption;

import java.security.Key;
import java.util.ArrayList;
import java.util.List;

import javax.xml.namespace.QName;

import net.shibboleth.utilities.java.support.security.IdentifierGenerationStrategy;
import net.shibboleth.utilities.java.support.security.impl.RandomIdentifierGenerationStrategy;
import net.shibboleth.utilities.java.support.xml.SerializeSupport;

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.MarshallingException;
import org.opensaml.core.xml.util.XMLObjectSupport;
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.Attribute;
import org.opensaml.saml.saml2.core.BaseID;
import org.opensaml.saml.saml2.core.EncryptedAssertion;
import org.opensaml.saml.saml2.core.EncryptedAttribute;
import org.opensaml.saml.saml2.core.EncryptedElementType;
import org.opensaml.saml.saml2.core.EncryptedID;
import org.opensaml.saml.saml2.core.NameID;
import org.opensaml.saml.saml2.core.NewEncryptedID;
import org.opensaml.saml.saml2.core.NewID;
import org.opensaml.security.SecurityException;
import org.opensaml.security.credential.CredentialSupport;
import org.opensaml.xmlsec.encryption.CarriedKeyName;
import org.opensaml.xmlsec.encryption.DataReference;
import org.opensaml.xmlsec.encryption.EncryptedData;
import org.opensaml.xmlsec.encryption.EncryptedKey;
import org.opensaml.xmlsec.encryption.ReferenceList;
import org.opensaml.xmlsec.encryption.XMLEncryptionBuilder;
import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
import org.opensaml.xmlsec.encryption.support.EncryptionException;
import org.opensaml.xmlsec.encryption.support.DataEncryptionParameters;
import org.opensaml.xmlsec.encryption.support.KeyEncryptionParameters;
import org.opensaml.xmlsec.keyinfo.KeyInfoGenerator;
import org.opensaml.xmlsec.signature.KeyInfo;
import org.opensaml.xmlsec.signature.KeyName;
import org.opensaml.xmlsec.signature.RetrievalMethod;
import org.opensaml.xmlsec.signature.XMLSignatureBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.google.common.base.Strings;

/**
 * Encrypter for SAML 2 SAMLObjects which has specific options for generating instances of subtypes of
 * {@link EncryptedElementType}.
 * 
 * 

* Overloaded methods are provided for encrypting various SAML 2 elements to their corresponding encrypted element * variant of {@link EncryptedElementType}. *

* *

* Support is also provided for differing placement options for any associated EncryptedKeys that may be generated. The * options are: *

* *
    *
  • INLINE: EncryptedKeys will placed inside the KeyInfo element of the EncryptedData element
  • *
  • PEER: EncryptedKeys will be placed as peer elements of the EncryptedData inside the * EncryptedElementType element
  • *
* *

* The default placement is PEER. *

* *

* The EncryptedKey forward and back referencing behavior associated with these key placement options is intended to be * consistent with the guidelines detailed in SAML 2 Errata E43. See that document for further information. *

* *

* For information on other parameters and options, and general XML Encryption issues, see * {@link org.opensaml.xmlsec.encryption.support.Encrypter}. *

* */ public class Encrypter extends org.opensaml.xmlsec.encryption.support.Encrypter { /** * Options for where to place the resulting EncryptedKey elements with respect to the associated EncryptedData * element. */ public enum KeyPlacement { /** Place the EncryptedKey element(s) as a peer to the EncryptedData inside the EncryptedElementType. */ PEER, /** Place the EncryptedKey element(s) within the KeyInfo of the EncryptedData. */ INLINE } /** Factory for building XMLObject instances. */ private XMLObjectBuilderFactory builderFactory; /** Builder for KeyInfo objects. */ private XMLSignatureBuilder keyInfoBuilder; /** Builder for DataReference objects. */ private XMLEncryptionBuilder dataReferenceBuilder; /** Builder for ReferenceList objects. */ private XMLEncryptionBuilder referenceListBuilder; /** Builder for RetrievalMethod objects. */ private XMLSignatureBuilder retrievalMethodBuilder; /** Builder for KeyName objects. */ private XMLSignatureBuilder keyNameBuilder; /** Builder for CarriedKeyName objects. */ private XMLEncryptionBuilder carriedKeyNameBuilder; /** Generator for XML ID attribute values. */ private IdentifierGenerationStrategy idGenerator; /** The parameters to use for encrypting the data. */ private DataEncryptionParameters encParams; /** The parameters to use for encrypting (wrapping) the data encryption key. */ private List kekParamsList; /** The option for where to place the generated EncryptedKey elements. */ private KeyPlacement keyPlacement; /** Class logger. */ private final Logger log = LoggerFactory.getLogger(Encrypter.class); /** * Constructor. * * @param dataEncParams the data encryption parameters * @param keyEncParams the key encryption parameters */ public Encrypter(final DataEncryptionParameters dataEncParams, final List keyEncParams) { super(); this.encParams = dataEncParams; this.kekParamsList = keyEncParams; init(); } /** * Constructor. * * @param dataEncParams the data encryption parameters * @param keyEncParam the key encryption parameter */ public Encrypter(final DataEncryptionParameters dataEncParams, final KeyEncryptionParameters keyEncParam) { super(); final List keks = new ArrayList<>(); keks.add(keyEncParam); this.encParams = dataEncParams; this.kekParamsList = keks; init(); } /** * Constructor. * * @param dataEncParams the data encryption parameters */ public Encrypter(final DataEncryptionParameters dataEncParams) { super(); final List keks = new ArrayList<>(); this.encParams = dataEncParams; this.kekParamsList = keks; init(); } /** * Helper method for constructors. */ private void init() { builderFactory = XMLObjectProviderRegistrySupport.getBuilderFactory(); keyInfoBuilder = (XMLSignatureBuilder) builderFactory.getBuilderOrThrow(KeyInfo.DEFAULT_ELEMENT_NAME); dataReferenceBuilder = (XMLEncryptionBuilder) builderFactory.getBuilderOrThrow( DataReference.DEFAULT_ELEMENT_NAME); referenceListBuilder = (XMLEncryptionBuilder) builderFactory.getBuilderOrThrow( ReferenceList.DEFAULT_ELEMENT_NAME); retrievalMethodBuilder = (XMLSignatureBuilder) builderFactory.getBuilderOrThrow( RetrievalMethod.DEFAULT_ELEMENT_NAME); keyNameBuilder = (XMLSignatureBuilder) builderFactory.getBuilderOrThrow(KeyName.DEFAULT_ELEMENT_NAME); carriedKeyNameBuilder = (XMLEncryptionBuilder) builderFactory.getBuilderOrThrow( CarriedKeyName.DEFAULT_ELEMENT_NAME); idGenerator = new RandomIdentifierGenerationStrategy(); keyPlacement = KeyPlacement.PEER; } /** * Set the generator to use when creating XML ID attribute values. * * @param newIDGenerator the new IdentifierGenerator to use */ public void setIDGenerator(final IdentifierGenerationStrategy newIDGenerator) { this.idGenerator = newIDGenerator; } /** * Get the current key placement option. * * @return returns the key placement option. */ public KeyPlacement getKeyPlacement() { return this.keyPlacement; } /** * Set the key placement option. * * @param newKeyPlacement The new key placement option to set */ public void setKeyPlacement(final KeyPlacement newKeyPlacement) { this.keyPlacement = newKeyPlacement; } /** * Encrypt the specified Assertion. * * @param assertion the Assertion to encrypt * @return an EncryptedAssertion * @throws EncryptionException thrown when encryption generates an error */ public EncryptedAssertion encrypt(final Assertion assertion) throws EncryptionException { logPreEncryption(assertion, "Assertion"); return (EncryptedAssertion) encrypt(assertion, EncryptedAssertion.DEFAULT_ELEMENT_NAME); } /** * Encrypt the specified Assertion, treating as an identifier and returning an EncryptedID. * * @param assertion the Assertion to encrypt * @return an EncryptedID * @throws EncryptionException thrown when encryption generates an error */ public EncryptedID encryptAsID(final Assertion assertion) throws EncryptionException { logPreEncryption(assertion, "Assertion (as EncryptedID)"); return (EncryptedID) encrypt(assertion, EncryptedID.DEFAULT_ELEMENT_NAME); } /** * Encrypt the specified Attribute. * * @param attribute the Attribute to encrypt * @return an EncryptedAttribute * @throws EncryptionException thrown when encryption generates an error */ public EncryptedAttribute encrypt(final Attribute attribute) throws EncryptionException { logPreEncryption(attribute, "Attribute"); return (EncryptedAttribute) encrypt(attribute, EncryptedAttribute.DEFAULT_ELEMENT_NAME); } /** * Encrypt the specified NameID. * * @param nameID the NameID to encrypt * @return an EncryptedID * @throws EncryptionException thrown when encryption generates an error */ public EncryptedID encrypt(final NameID nameID) throws EncryptionException { logPreEncryption(nameID, "NameID"); return (EncryptedID) encrypt(nameID, EncryptedID.DEFAULT_ELEMENT_NAME); } /** * Encrypt the specified BaseID. * * @param baseID the BaseID to encrypt * @return an EncryptedID * @throws EncryptionException thrown when encryption generates an error */ public EncryptedID encrypt(final BaseID baseID) throws EncryptionException { logPreEncryption(baseID, "BaseID"); return (EncryptedID) encrypt(baseID, EncryptedID.DEFAULT_ELEMENT_NAME); } /** * Encrypt the specified NewID. * * @param newID the NewID to encrypt * @return a NewEncryptedID * @throws EncryptionException thrown when encryption generates an error */ public NewEncryptedID encrypt(final NewID newID) throws EncryptionException { logPreEncryption(newID, "NewID"); return (NewEncryptedID) encrypt(newID, NewEncryptedID.DEFAULT_ELEMENT_NAME); } /** * Log the target object prior to encryption. * * @param xmlObject the XMLObject to encrypt * @param objectType String description of the type of object to encrypt */ private void logPreEncryption(final XMLObject xmlObject, final String objectType) { if (log.isDebugEnabled()) { try { final Element dom = XMLObjectSupport.marshall(xmlObject); log.debug("{} before encryption:\n{}", objectType, SerializeSupport.prettyPrintXML(dom)); } catch (final MarshallingException e) { log.error("Unable to marshall {} for logging purposes", objectType, e); } } } /** * Encrypt the specified XMLObject, and return it as an instance of the specified QName, which should be one of the * types derived from {@link org.opensaml.saml.saml2.core.EncryptedElementType}. * * @param xmlObject the XMLObject to encrypt * @param encElementName the QName of the specialization of EncryptedElementType to return * @return a specialization of {@link org.opensaml.saml.saml2.core.EncryptedElementType} * @throws EncryptionException thrown when encryption generates an error */ private EncryptedElementType encrypt(final XMLObject xmlObject, final QName encElementName) throws EncryptionException { checkParams(encParams, kekParamsList); final EncryptedElementType encElement = (EncryptedElementType) builderFactory.getBuilder(encElementName).buildObject(encElementName); // Marshall the containing element, we will need its Document context to pass // to the key encryption method checkAndMarshall(encElement); final Document ownerDocument = encElement.getDOM().getOwnerDocument(); final String encryptionAlgorithmURI = encParams.getAlgorithm(); Key encryptionKey = CredentialSupport.extractEncryptionKey(encParams.getEncryptionCredential()); if (encryptionKey == null) { encryptionKey = generateEncryptionKey(encryptionAlgorithmURI); } final EncryptedData encryptedData = encryptElement(xmlObject, encryptionKey, encryptionAlgorithmURI, false); if (encParams.getKeyInfoGenerator() != null) { final 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 (final SecurityException e) { throw new EncryptionException("Error generating EncryptedData KeyInfo", e); } } final List encryptedKeys = new ArrayList<>(); if (kekParamsList != null && !kekParamsList.isEmpty()) { encryptedKeys.addAll(encryptKey(encryptionKey, kekParamsList, ownerDocument)); } return processElements(encElement, encryptedData, encryptedKeys); } /** * Handle post-processing of generated EncryptedData and EncryptedKey(s) and storage in the appropriate * EncryptedElementType instance. * * @param encElement the EncryptedElementType instance which will hold the encrypted data and keys * @param encData the EncryptedData object * @param encKeys the list of EncryptedKey objects * @return the processed EncryptedElementType instance * * @throws EncryptionException thrown when processing encounters an error */ protected EncryptedElementType processElements(final EncryptedElementType encElement, final EncryptedData encData, final List encKeys) throws EncryptionException { // First ensure certain elements/attributes are non-null, common to all cases. if (encData.getID() == null) { encData.setID(idGenerator.generateIdentifier()); } // If not doing key wrapping, just return the encrypted element if (encKeys.isEmpty()) { encElement.setEncryptedData(encData); return encElement; } if (encData.getKeyInfo() == null) { encData.setKeyInfo(keyInfoBuilder.buildObject()); } for (final EncryptedKey encKey : encKeys) { if (encKey.getID() == null) { encKey.setID(idGenerator.generateIdentifier()); } } switch (keyPlacement) { case INLINE: return placeKeysInline(encElement, encData, encKeys); case PEER: return placeKeysAsPeers(encElement, encData, encKeys); default: // Shouldn't be able to get here, but just in case... throw new EncryptionException("Unsupported key placement option was specified: " + keyPlacement); } } /** * Place the EncryptedKey elements inside the KeyInfo element within the EncryptedData element. * * Although operationally trivial, this method is provided so that subclasses may override or augment as desired. * * @param encElement the EncryptedElementType instance which will hold the encrypted data and keys * @param encData the EncryptedData object * @param encKeys the list of EncryptedKey objects * @return the processed EncryptedElementType instance */ protected EncryptedElementType placeKeysInline(final EncryptedElementType encElement, final EncryptedData encData, final List encKeys) { log.debug("Placing EncryptedKey elements inline inside EncryptedData"); encData.getKeyInfo().getEncryptedKeys().addAll(encKeys); encElement.setEncryptedData(encData); return encElement; } /** * Store the specified EncryptedData and EncryptedKey(s) in the specified instance of EncryptedElementType as peer * elements, following SAML 2 Errata E43 guidelines for forward and back referencing between the EncryptedData and * EncryptedKey(s). * * @param encElement a specialization of EncryptedElementType to store the encrypted data and keys * @param encData the EncryptedData to store * @param encKeys the EncryptedKey(s) to store * @return the resulting specialization of EncryptedElementType */ protected EncryptedElementType placeKeysAsPeers(final EncryptedElementType encElement, final EncryptedData encData, final List encKeys) { log.debug("Placing EncryptedKey elements as peers of EncryptedData in EncryptedElementType"); for (final EncryptedKey encKey : encKeys) { if (encKey.getReferenceList() == null) { encKey.setReferenceList(referenceListBuilder.buildObject()); } } // If there is only 1 EncryptedKey we have a simple forward reference (RetrievalMethod) // and back reference (ReferenceList/DataReference) requirement. // Multiple "multicast" keys use back reference + CarriedKeyName if (encKeys.size() == 1) { linkSinglePeerKey(encData, encKeys.get(0)); } else if (encKeys.size() > 1) { linkMultiplePeerKeys(encData, encKeys); } encElement.setEncryptedData(encData); encElement.getEncryptedKeys().addAll(encKeys); return encElement; } /** * Link a single EncryptedKey to the EncryptedData according to guidelines in SAML Errata E43. * * @param encData the EncryptedData * @param encKey the EncryptedKey */ protected void linkSinglePeerKey(final EncryptedData encData, final EncryptedKey encKey) { log.debug("Linking single peer EncryptedKey with RetrievalMethod and DataReference"); // Forward reference from EncryptedData to the EncryptedKey final RetrievalMethod rm = retrievalMethodBuilder.buildObject(); rm.setURI("#" + encKey.getID()); rm.setType(EncryptionConstants.TYPE_ENCRYPTED_KEY); encData.getKeyInfo().getRetrievalMethods().add(rm); // Back reference from the EncryptedKey to the EncryptedData final DataReference dr = dataReferenceBuilder.buildObject(); dr.setURI("#" + encData.getID()); encKey.getReferenceList().getDataReferences().add(dr); } /** * Link multiple "multicast" EncryptedKeys to the EncryptedData according to guidelines in SAML Errata E43. * * @param encData the EncryptedData * @param encKeys the list of EncryptedKeys */ protected void linkMultiplePeerKeys(final EncryptedData encData, final List encKeys) { log.debug("Linking multiple peer EncryptedKeys with CarriedKeyName and DataReference"); // Get the name of the data encryption key final List dataEncKeyNames = encData.getKeyInfo().getKeyNames(); final String carriedKeyNameValue; if (dataEncKeyNames.size() == 0 || Strings.isNullOrEmpty(dataEncKeyNames.get(0).getValue())) { // If there isn't one, autogenerate a random key name. final String keyNameValue = idGenerator.generateIdentifier(); log.debug("EncryptedData encryption key had no KeyName, generated one for use in CarriedKeyName: {}", keyNameValue); KeyName keyName = dataEncKeyNames.get(0); if (keyName == null) { keyName = keyNameBuilder.buildObject(); dataEncKeyNames.add(keyName); } keyName.setValue(keyNameValue); carriedKeyNameValue = keyNameValue; } else { carriedKeyNameValue = dataEncKeyNames.get(0).getValue(); } // Set carried key name of the multicast key in each EncryptedKey for (final EncryptedKey encKey : encKeys) { if (encKey.getCarriedKeyName() == null) { encKey.setCarriedKeyName(carriedKeyNameBuilder.buildObject()); } encKey.getCarriedKeyName().setValue(carriedKeyNameValue); // Back reference from the EncryptedKeys to the EncryptedData final DataReference dr = dataReferenceBuilder.buildObject(); dr.setURI("#" + encData.getID()); encKey.getReferenceList().getDataReferences().add(dr); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy