package org.apereo.cas.support.saml.web.idp.profile.builders.enc;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.configuration.model.support.saml.idp.SamlIdPProperties;
import org.apereo.cas.support.saml.SamlException;
import org.apereo.cas.support.saml.services.SamlRegisteredService;
import org.apereo.cas.support.saml.services.idp.metadata.SamlRegisteredServiceServiceProviderMetadataFacade;
import org.apereo.cas.util.EncodingUtils;
import org.opensaml.core.criterion.EntityIdCriterion;
import org.opensaml.saml.criterion.EntityRoleCriterion;
import org.opensaml.saml.metadata.resolver.impl.PredicateRoleDescriptorResolver;
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.EncryptedAssertion;
import org.opensaml.saml.saml2.encryption.Encrypter;
import org.opensaml.saml.saml2.metadata.SPSSODescriptor;
import org.opensaml.saml.security.impl.MetadataCredentialResolver;
import org.opensaml.security.credential.Credential;
import org.opensaml.security.credential.UsageType;
import org.opensaml.security.criteria.UsageCriterion;
import org.opensaml.xmlsec.config.DefaultSecurityConfigurationBootstrap;
import org.opensaml.xmlsec.criterion.EncryptionConfigurationCriterion;
import org.opensaml.xmlsec.encryption.support.DataEncryptionParameters;
import org.opensaml.xmlsec.encryption.support.EncryptionConstants;
import org.opensaml.xmlsec.encryption.support.KeyEncryptionParameters;
import org.opensaml.xmlsec.impl.BasicEncryptionConfiguration;
import org.opensaml.xmlsec.keyinfo.impl.BasicProviderKeyInfoCredentialResolver;
import org.opensaml.xmlsec.keyinfo.impl.KeyInfoProvider;
import org.opensaml.xmlsec.keyinfo.impl.provider.DEREncodedKeyValueProvider;
import org.opensaml.xmlsec.keyinfo.impl.provider.DSAKeyValueProvider;
import org.opensaml.xmlsec.keyinfo.impl.provider.InlineX509DataProvider;
import org.opensaml.xmlsec.keyinfo.impl.provider.KeyInfoReferenceProvider;
import org.opensaml.xmlsec.keyinfo.impl.provider.RSAKeyValueProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
/**
* This is {@link SamlObjectEncrypter}.
*
* @author Misagh Moayyed
* @since 5.0.0
*/
public class SamlObjectEncrypter {
private static final Logger LOGGER = LoggerFactory.getLogger(SamlObjectEncrypter.class);
/**
* The Override data encryption algorithms.
*/
protected List overrideDataEncryptionAlgorithms;
/**
* The Override key encryption algorithms.
*/
protected List overrideKeyEncryptionAlgorithms;
/**
* The Override black listed encryption signing algorithms.
*/
protected List overrideBlackListedEncryptionAlgorithms;
/**
* The Override white listed encryption signing algorithms.
*/
protected List overrideWhiteListedAlgorithms;
@Autowired
private CasConfigurationProperties casProperties;
public SamlObjectEncrypter(final List overrideDataEncryptionAlgorithms, final List overrideKeyEncryptionAlgorithms,
final List overrideBlackListedEncryptionAlgorithms, final List overrideWhiteListedAlgorithms) {
this.overrideDataEncryptionAlgorithms = overrideDataEncryptionAlgorithms;
this.overrideKeyEncryptionAlgorithms = overrideKeyEncryptionAlgorithms;
this.overrideBlackListedEncryptionAlgorithms = overrideBlackListedEncryptionAlgorithms;
this.overrideWhiteListedAlgorithms = overrideWhiteListedAlgorithms;
}
/**
* Encode a given saml object by invoking a number of outbound security handlers on the context.
*
* @param samlObject the saml object
* @param service the service
* @param adaptor the adaptor
* @param response the response
* @param request the request
* @return the t
* @throws SamlException the saml exception
*/
public EncryptedAssertion encode(final Assertion samlObject,
final SamlRegisteredService service,
final SamlRegisteredServiceServiceProviderMetadataFacade adaptor,
final HttpServletResponse response,
final HttpServletRequest request) throws SamlException {
try {
LOGGER.debug("Attempting to encrypt [{}] for [{}]", samlObject.getClass().getName(), adaptor.getEntityId());
final Credential credential = getKeyEncryptionCredential(adaptor.getEntityId(), adaptor, service);
LOGGER.info("Found encryption public key: [{}]", EncodingUtils.encodeBase64(credential.getPublicKey().getEncoded()));
final KeyEncryptionParameters keyEncParams = getKeyEncryptionParameters(samlObject, service, adaptor, credential);
LOGGER.debug("Key encryption algorithm for [{}] is [{}]", keyEncParams.getRecipient(), keyEncParams.getAlgorithm());
final DataEncryptionParameters dataEncParams = getDataEncryptionParameters(samlObject, service, adaptor);
LOGGER.debug("Data encryption algorithm for [{}] is [{}]", adaptor.getEntityId(), dataEncParams.getAlgorithm());
final Encrypter encrypter = getEncrypter(samlObject, service, adaptor, keyEncParams, dataEncParams);
LOGGER.debug("Attempting to encrypt [{}] for [{}] with key placement of [{}]",
samlObject.getClass().getName(), adaptor.getEntityId(), encrypter.getKeyPlacement());
return encrypter.encrypt(samlObject);
} catch (final Exception e) {
throw new SamlException(e.getMessage(), e);
}
}
/**
* Gets encrypter.
*
* @param samlObject the saml object
* @param service the service
* @param adaptor the adaptor
* @param keyEncParams the key enc params
* @param dataEncParams the data enc params
* @return the encrypter
*/
protected Encrypter getEncrypter(final Assertion samlObject, final SamlRegisteredService service,
final SamlRegisteredServiceServiceProviderMetadataFacade adaptor,
final KeyEncryptionParameters keyEncParams, final
DataEncryptionParameters dataEncParams) {
final Encrypter encrypter = new Encrypter(dataEncParams, keyEncParams);
encrypter.setKeyPlacement(Encrypter.KeyPlacement.PEER);
return encrypter;
}
/**
* Gets data encryption parameters.
*
* @param samlObject the saml object
* @param service the service
* @param adaptor the adaptor
* @return the data encryption parameters
*/
protected DataEncryptionParameters getDataEncryptionParameters(final Assertion samlObject, final SamlRegisteredService service,
final SamlRegisteredServiceServiceProviderMetadataFacade adaptor) {
final DataEncryptionParameters dataEncParams = new DataEncryptionParameters();
dataEncParams.setAlgorithm(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128);
return dataEncParams;
}
/**
* Gets key encryption parameters.
*
* @param samlObject the saml object
* @param service the service
* @param adaptor the adaptor
* @param credential the credential
* @return the key encryption parameters
*/
protected KeyEncryptionParameters getKeyEncryptionParameters(final Assertion samlObject, final SamlRegisteredService service,
final SamlRegisteredServiceServiceProviderMetadataFacade adaptor,
final Credential credential) {
final KeyEncryptionParameters keyEncParams = new KeyEncryptionParameters();
keyEncParams.setRecipient(adaptor.getEntityId());
keyEncParams.setEncryptionCredential(credential);
keyEncParams.setAlgorithm(EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP);
return keyEncParams;
}
/**
* Gets key encryption credential.
*
* @param peerEntityId the peer entity id
* @param adaptor the adaptor
* @param service the service
* @return the key encryption credential
* @throws Exception the exception
*/
protected Credential getKeyEncryptionCredential(final String peerEntityId,
final SamlRegisteredServiceServiceProviderMetadataFacade adaptor,
final SamlRegisteredService service) throws Exception {
final SamlIdPProperties idp = casProperties.getAuthn().getSamlIdp();
final BasicEncryptionConfiguration config =
DefaultSecurityConfigurationBootstrap.buildDefaultEncryptionConfiguration();
if (this.overrideBlackListedEncryptionAlgorithms != null && !this.overrideBlackListedEncryptionAlgorithms.isEmpty()) {
config.setBlacklistedAlgorithms(this.overrideBlackListedEncryptionAlgorithms);
}
if (this.overrideWhiteListedAlgorithms != null && !this.overrideWhiteListedAlgorithms.isEmpty()) {
config.setWhitelistedAlgorithms(this.overrideWhiteListedAlgorithms);
}
if (this.overrideDataEncryptionAlgorithms != null && !this.overrideDataEncryptionAlgorithms.isEmpty()) {
config.setDataEncryptionAlgorithms(this.overrideDataEncryptionAlgorithms);
}
if (this.overrideKeyEncryptionAlgorithms != null && !this.overrideKeyEncryptionAlgorithms.isEmpty()) {
config.setKeyTransportEncryptionAlgorithms(this.overrideKeyEncryptionAlgorithms);
}
LOGGER.debug("Encryption blacklisted algorithms: [{}]", config.getBlacklistedAlgorithms());
LOGGER.debug("Encryption key algorithms: [{}]", config.getKeyTransportEncryptionAlgorithms());
LOGGER.debug("Signature data algorithms: [{}]", config.getDataEncryptionAlgorithms());
LOGGER.debug("Encryption whitelisted algorithms: [{}]", config.getWhitelistedAlgorithms());
final MetadataCredentialResolver kekCredentialResolver = new MetadataCredentialResolver();
final List<KeyInfoProvider> providers = new ArrayList<>();
providers.add(new RSAKeyValueProvider());
providers.add(new DSAKeyValueProvider());
providers.add(new InlineX509DataProvider());
providers.add(new DEREncodedKeyValueProvider());
providers.add(new KeyInfoReferenceProvider());
final BasicProviderKeyInfoCredentialResolver keyInfoResolver = new BasicProviderKeyInfoCredentialResolver(providers);
kekCredentialResolver.setKeyInfoCredentialResolver(keyInfoResolver);
final PredicateRoleDescriptorResolver roleDescriptorResolver = new PredicateRoleDescriptorResolver(adaptor.getMetadataResolver());
roleDescriptorResolver.setSatisfyAnyPredicates(true);
roleDescriptorResolver.setUseDefaultPredicateRegistry(true);
roleDescriptorResolver.setRequireValidMetadata(idp.getMetadata().isRequireValidMetadata());
roleDescriptorResolver.initialize();
kekCredentialResolver.setRoleDescriptorResolver(roleDescriptorResolver);
kekCredentialResolver.initialize();
final CriteriaSet criteriaSet = new CriteriaSet();
criteriaSet.add(new EncryptionConfigurationCriterion(config));
criteriaSet.add(new EntityIdCriterion(peerEntityId));
criteriaSet.add(new EntityRoleCriterion(SPSSODescriptor.DEFAULT_ELEMENT_NAME));
criteriaSet.add(new UsageCriterion(UsageType.ENCRYPTION));
LOGGER.debug("Attempting to resolve the encryption key for entity id [{}]", peerEntityId);
return kekCredentialResolver.resolveSingle(criteriaSet);
}
public void setOverrideDataEncryptionAlgorithms(final List overrideDataEncryptionAlgorithms) {
this.overrideDataEncryptionAlgorithms = overrideDataEncryptionAlgorithms;
}
public void setOverrideKeyEncryptionAlgorithms(final List overrideKeyEncryptionAlgorithms) {
this.overrideKeyEncryptionAlgorithms = overrideKeyEncryptionAlgorithms;
}
public void setOverrideBlackListedEncryptionAlgorithms(final List overrideBlackListedEncryptionAlgorithms) {
this.overrideBlackListedEncryptionAlgorithms = overrideBlackListedEncryptionAlgorithms;
}
public void setOverrideWhiteListedAlgorithms(final List overrideWhiteListedAlgorithms) {
this.overrideWhiteListedAlgorithms = overrideWhiteListedAlgorithms;
}
}