package org.apereo.cas.util.services;
import com.google.common.base.Throwables;
import org.apereo.cas.services.RegisteredService;
import org.apereo.cas.util.EncodingUtils;
import org.apereo.cas.services.RegisteredServiceCipherExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
/**
* Default cipher implementation based on public keys.
*
* @author Misagh Moayyed
* @since 4.1
*/
public class DefaultRegisteredServiceCipherExecutor implements RegisteredServiceCipherExecutor {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultRegisteredServiceCipherExecutor.class);
/**
* Encrypt using the given cipher associated with the service,
* and encode the data in base 64.
*
* @param data the data
* @param service the registered service
* @return the encoded piece of data in base64
*/
@Override
public String encode(final String data, final RegisteredService service) {
try {
final PublicKey publicKey = createRegisteredServicePublicKey(service);
final byte[] result = encodeInternal(data, publicKey, service);
if (result != null) {
return EncodingUtils.encodeBase64(result);
}
} catch (final Exception e) {
LOGGER.warn(e.getMessage(), e);
}
return null;
}
/**
* Encode internally, meant to be called by extensions.
* Default behavior will encode the data based on the
* registered service public key's algorithm using {@link javax.crypto.Cipher}.
*
* @param data the data
* @param publicKey the public key
* @param registeredService the registered service
* @return a byte[] that contains the encrypted result
*/
protected static byte[] encodeInternal(final String data, final PublicKey publicKey,
final RegisteredService registeredService) {
try {
final Cipher cipher = initializeCipherBasedOnServicePublicKey(publicKey, registeredService);
if (cipher != null) {
return cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
}
} catch (final Exception e) {
throw Throwables.propagate(e);
}
return null;
}
/**
* Create registered service public key defined.
*
* @param registeredService the registered service
* @return the public key
* @throws Exception the exception, if key cant be created
*/
private static PublicKey createRegisteredServicePublicKey(final RegisteredService registeredService) throws Exception {
if (registeredService.getPublicKey() == null) {
LOGGER.debug("No public key is defined for service [{}]. No encoding will take place.", registeredService);
return null;
}
final PublicKey publicKey = registeredService.getPublicKey().createInstance();
if (publicKey == null) {
LOGGER.debug("No public key instance created for service [{}]. No encoding will take place.", registeredService);
return null;
}
return publicKey;
}
/**
* Initialize cipher based on service public key.
*
* @param publicKey the public key
* @param registeredService the registered service
* @return the false if no public key is found
* or if cipher cannot be initialized, etc.
*/
private static Cipher initializeCipherBasedOnServicePublicKey(final PublicKey publicKey,
final RegisteredService registeredService) {
try {
LOGGER.debug("Using public key [{}] to initialize the cipher", registeredService.getPublicKey());
final Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
LOGGER.debug("Initialized cipher in encrypt-mode via the public key algorithm [{}]", publicKey.getAlgorithm());
return cipher;
} catch (final Exception e) {
LOGGER.warn("Cipher could not be initialized for service [{}]. Error [{}]",
registeredService, e.getMessage());
}
return null;
}
}