package com.constellio.model.services.encrypt;
import java.io.IOException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.List;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import com.constellio.data.utils.ImpossibleRuntimeException;
import com.constellio.data.utils.dev.Toggle;
import com.constellio.model.services.encrypt.EncryptionServicesRuntimeException.EncryptionServicesRuntimeException_InvalidKey;
public class EncryptionServices {
private static final String DEFAULT_ENCRYPTION_ALGORITHM = "AES/CBC/PKCS5Padding";
byte[] key = new byte[16];
byte[] iv = new byte[16];
boolean initialized = false;
boolean lostPreviousKey;
public EncryptionServices(boolean lostPreviousKey) {
this.lostPreviousKey = lostPreviousKey;
}
public EncryptionServices withKey(Key key)
throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {
if (initialized) {
throw new RuntimeException("Already intialized");
}
System.arraycopy(key.getEncoded(), 0, this.iv, 0, 16);
System.arraycopy(key.getEncoded(), 16, this.key, 0, 16);
initialized = true;
return this;
}
public String encrypt(String toEncrypt, String algorithm) {
if (toEncrypt == null) {
return null;
}
try {
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
byte[] encrypted = cipher.doFinal(toEncrypt.getBytes());
return Base64.encodeBase64String(encrypted);
} catch (Exception e) {
throw new RuntimeException("Cannot encrypt '" + toEncrypt + "'", e);
}
}
public String encrypt(String toEncrypt) {
return encrypt(toEncrypt, DEFAULT_ENCRYPTION_ALGORITHM);
}
public String decrypt(String encryptedBase64, String algorithm) {
try {
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
byte[] decryptedText = cipher.doFinal(Base64.decodeBase64(encryptedBase64));
return new String(decryptedText);
} catch (Exception e) {
if (lostPreviousKey || Toggle.LOST_PRIVATE_KEY.isEnabled()) {
return encryptedBase64;
}
throw new RuntimeException("Cannot decrypt '" + encryptedBase64 + "'", e);
}
}
public Object decrypt(Object encryptedText) {
if (encryptedText instanceof String) {
return decrypt((String) encryptedText);
} else if (encryptedText instanceof List) {
List<Object> list = (List<Object>) encryptedText;
List<Object> decryptedValues = new ArrayList<>();
for (Object item : list) {
decryptedValues.add(decrypt(item));
}
return decryptedValues;
} else {
throw new IllegalArgumentException("Unsupported element of class '" + encryptedText.getClass().getName() + "'");
}
}
public Object encrypt(Object toEncrypt) {
if (toEncrypt instanceof String) {
return encrypt((String) toEncrypt);
} else if (toEncrypt instanceof List) {
List<Object> list = (List<Object>) toEncrypt;
List<Object> encryptedValues = new ArrayList<>();
for (Object item : list) {
encryptedValues.add(encrypt(item));
}
return encryptedValues;
} else {
throw new IllegalArgumentException("Unsupported element of class '" + toEncrypt.getClass().getName() + "'");
}
}
public String decrypt(String encryptedText) {
return decrypt(encryptedText, DEFAULT_ENCRYPTION_ALGORITHM);
}
public static EncryptionServices create(boolean lostPreviousKey, Key key) {
try {
return new EncryptionServices(lostPreviousKey).withKey(key);
} catch (InvalidKeySpecException e) {
throw new EncryptionServicesRuntimeException_InvalidKey(e);
} catch (NoSuchAlgorithmException | IOException e) {
throw new ImpossibleRuntimeException(e);
}
}
public boolean isInitialized() {
return initialized;
}
}