package org.apereo.cas.util.cipher;
import com.google.common.base.Throwables;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.crypto.CipherService;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jwk.OctJwkGenerator;
import org.jose4j.jwk.OctetSequenceJsonWebKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Map;
/**
* A implementation that is based on algorithms
* provided by the default platform's JCE. By default AES encryption is
* used.
*
* @author Misagh Moayyed
* @since 4.2
*/
public abstract class BaseBinaryCipherExecutor extends AbstractCipherExecutor<byte[], byte[]> {
private static final Logger LOGGER = LoggerFactory.getLogger(BaseBinaryCipherExecutor.class);
/**
* Secret key IV algorithm. Default is {@code AES}.
*/
private String secretKeyAlgorithm = "AES";
private String encryptionSecretKey;
/**
* Instantiates a new cryptic ticket cipher executor.
*
* @param encryptionSecretKey the encryption secret key
* @param signingSecretKey the signing key
* @param signingKeySize the signing key size
* @param encryptionKeySize the encryption key size
*/
public BaseBinaryCipherExecutor(final String encryptionSecretKey,
final String signingSecretKey,
final int signingKeySize,
final int encryptionKeySize) {
String signingKeyToUse = signingSecretKey;
if (StringUtils.isBlank(signingKeyToUse)) {
LOGGER.warn("Secret key for signing is not defined. CAS will attempt to auto-generate the signing key");
signingKeyToUse = generateOctetJsonWebKeyOfSize(signingKeySize);
LOGGER.warn("Generated signing key [{}] of size [{}]. The generated key MUST be added to CAS settings.",
signingKeyToUse, signingKeySize);
}
setSigningKey(signingKeyToUse);
if (StringUtils.isBlank(encryptionSecretKey)) {
LOGGER.warn("No encryption key is defined. CAS will attempt to auto-generate keys");
this.encryptionSecretKey = RandomStringUtils.randomAlphabetic(encryptionKeySize);
LOGGER.warn("Generated encryption key [{}] of size [{}]. The generated key MUST be added to CAS settings.",
this.encryptionSecretKey, encryptionKeySize);
} else {
this.encryptionSecretKey = encryptionSecretKey;
}
}
public void setSecretKeyAlgorithm(final String secretKeyAlgorithm) {
this.secretKeyAlgorithm = secretKeyAlgorithm;
}
@Override
public byte[] encode(final byte[] value) {
try {
final Key key = new SecretKeySpec(this.encryptionSecretKey.getBytes(StandardCharsets.UTF_8),
this.secretKeyAlgorithm);
final CipherService cipher = new AesCipherService();
final byte[] result = cipher.encrypt(value, key.getEncoded()).getBytes();
return sign(result);
} catch (final Exception e) {
LOGGER.error(e.getMessage(), e);
throw Throwables.propagate(e);
}
}
@Override
public byte[] decode(final byte[] value) {
try {
final byte[] verifiedValue = verifySignature(value);
final Key key = new SecretKeySpec(this.encryptionSecretKey.getBytes(StandardCharsets.UTF_8),
this.secretKeyAlgorithm);
final CipherService cipher = new AesCipherService();
final byte[] result = cipher.decrypt(verifiedValue, key.getEncoded()).getBytes();
return result;
} catch (final Exception e) {
throw Throwables.propagate(e);
}
}
private static String generateOctetJsonWebKeyOfSize(final int size) {
try {
final OctetSequenceJsonWebKey octetKey = OctJwkGenerator.generateJwk(size);
final Map<String, Object> params = octetKey.toParams(JsonWebKey.OutputControlLevel.INCLUDE_SYMMETRIC);
return params.get("k").toString();
} catch (final Exception e) {
LOGGER.error(e.getMessage(), e);
throw Throwables.propagate(e);
}
}
@Override
public String getName() {
return null;
}
}