package org.apereo.cas.configuration.support; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import java.security.Security; import java.util.HashMap; import java.util.Map; /** * This is {@link CasConfigurationJasyptDecryptor}. * * @author Misagh Moayyed * @since 5.1.0 */ public class CasConfigurationJasyptDecryptor { private static final Logger LOGGER = LoggerFactory.getLogger(CasConfigurationJasyptDecryptor.class); private static final String ENCRYPTED_VALUE_PREFIX = "{cipher}"; private enum JasyptEncryptionParameters { ALGORITHM("cas.standalone.config.security.alg", "PBEWithMD5AndTripleDES"), PROVIDER("cas.standalone.config.security.provider", null), ITERATIONS("cas.standalone.config.security.iteration", null), PASSWORD("cas.standalone.config.security.psw", null); private String name; private String defaultValue; JasyptEncryptionParameters(final String name, final String defaultValue) { this.name = name; this.defaultValue = defaultValue; } public String getName() { return name; } public String getDefaultValue() { return defaultValue; } } private final StandardPBEStringEncryptor decryptor; public CasConfigurationJasyptDecryptor(final Environment environment) { this.decryptor = new StandardPBEStringEncryptor(); final String alg = getJasyptParamFromEnv(environment, JasyptEncryptionParameters.ALGORITHM); if (StringUtils.isNotBlank(alg)) { LOGGER.debug("Configured decryptor algorithm [{}]", alg); decryptor.setAlgorithm(alg); } final String psw = getJasyptParamFromEnv(environment, JasyptEncryptionParameters.PASSWORD); if (StringUtils.isNotBlank(psw)) { LOGGER.debug("Configured decryptor password"); decryptor.setPassword(psw); } final String pName = getJasyptParamFromEnv(environment, JasyptEncryptionParameters.PROVIDER); if (StringUtils.isNotBlank(pName)) { LOGGER.debug("Configured decryptor provider"); if (StringUtils.equals(pName, BouncyCastleProvider.PROVIDER_NAME)) { Security.addProvider(new BouncyCastleProvider()); } this.decryptor.setProviderName(pName); } final String iter = getJasyptParamFromEnv(environment, JasyptEncryptionParameters.ITERATIONS); if (StringUtils.isNotBlank(iter) && NumberUtils.isCreatable(iter)) { LOGGER.debug("Configured decryptor iterations"); decryptor.setKeyObtentionIterations(Integer.valueOf(iter)); } } private static String getJasyptParamFromEnv(final Environment environment, final JasyptEncryptionParameters param) { return environment.getProperty(param.getName(), param.getDefaultValue()); } /** * Decrypt map. * * @param settings the settings * @return the map */ public Map<Object, Object> decrypt(final Map<Object, Object> settings) { final Map<Object, Object> decrypted = new HashMap<>(); settings.forEach((key, value1) -> { final String stringValue = getStringPropertyValue(value1); if (StringUtils.isNotBlank(stringValue) && stringValue.startsWith(ENCRYPTED_VALUE_PREFIX)) { try { if (!this.decryptor.isInitialized()) { LOGGER.debug("Initializing decryptor...", key); this.decryptor.initialize(); } final String encValue = stringValue.substring(ENCRYPTED_VALUE_PREFIX.length()); LOGGER.debug("Decrypting property [{}]...", key); final String value = this.decryptor.decrypt(encValue); LOGGER.debug("Decrypted property [{}] successfully.", key); decrypted.put(key, value); } catch (final Exception e) { LOGGER.error("Could not decrypt property [{}]. Setting will be ignored by CAS", key, e); } } else { decrypted.put(key, value1); } }); return decrypted; } /** * Retrieves the {@link String} of an {@link Object}. * * @param propertyValue The property value to cast * @return A {@link String} representing the property value or {@code null} if it is not a {@link String} */ private static String getStringPropertyValue(final Object propertyValue) { return propertyValue instanceof String ? propertyValue.toString() : null; } }