CLOUDSTACK-2039: Improve console access security with 128-bit AES encryption and securely-randomized key generation

This commit is contained in:
Kelven Yang 2013-04-15 16:51:50 -07:00
parent 8d33353b40
commit 34f8f795e1
9 changed files with 358 additions and 157 deletions

View File

@ -305,6 +305,8 @@ public enum Config {
SSOAuthTolerance("Advanced", ManagementServer.class, Long.class, "security.singlesignon.tolerance.millis", "300000", "The allowable clock difference in milliseconds between when an SSO login request is made and when it is received.", null),
//NetworkType("Hidden", ManagementServer.class, String.class, "network.type", "vlan", "The type of network that this deployment will use.", "vlan,direct"),
HashKey("Hidden", ManagementServer.class, String.class, "security.hash.key", null, "for generic key-ed hash", null),
EncryptionKey("Hidden", ManagementServer.class, String.class, "security.encryption.key", null, "base64 encoded key data", null),
EncryptionIV("Hidden", ManagementServer.class, String.class, "security.encryption.iv", null, "base64 encoded IV data", null),
RouterRamSize("Hidden", NetworkManager.class, Integer.class, "router.ram.size", "128", "Default RAM for router VM (in MB).", null),
VmOpWaitInterval("Advanced", ManagementServer.class, Integer.class, "vm.op.wait.interval", "120", "Time (in seconds) to wait before checking if a previous operation has succeeded", null),

View File

@ -33,6 +33,7 @@ import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.info.ConsoleProxyInfo;
import com.cloud.keystore.KeystoreManager;
import com.cloud.server.ManagementServer;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.component.ManagerBase;
import com.cloud.vm.ConsoleProxyVO;
@ -69,12 +70,13 @@ public class AgentBasedConsoleProxyManager extends ManagerBase implements Consol
protected KeystoreManager _ksMgr;
@Inject ConfigurationDao _configDao;
@Inject ManagementServer _ms;
public class AgentBasedAgentHook extends AgentHookBase {
public AgentBasedAgentHook(VMInstanceDao instanceDao, HostDao hostDao, ConfigurationDao cfgDao,
KeystoreManager ksMgr, AgentManager agentMgr) {
super(instanceDao, hostDao, cfgDao, ksMgr, agentMgr);
KeystoreManager ksMgr, AgentManager agentMgr, ManagementServer ms) {
super(instanceDao, hostDao, cfgDao, ksMgr, agentMgr, ms);
}
@Override
@ -120,7 +122,7 @@ public class AgentBasedConsoleProxyManager extends ManagerBase implements Consol
_consoleProxyUrlDomain = configs.get("consoleproxy.url.domain");
_listener =
new ConsoleProxyListener(new AgentBasedAgentHook(_instanceDao, _hostDao, _configDao, _ksMgr, _agentMgr));
new ConsoleProxyListener(new AgentBasedAgentHook(_instanceDao, _hostDao, _configDao, _ksMgr, _agentMgr, _ms));
_agentMgr.registerForHostEvents(_listener, true, true, false);
if (s_logger.isInfoEnabled()) {

View File

@ -42,10 +42,14 @@ import com.cloud.host.HostVO;
import com.cloud.host.Status;
import com.cloud.host.dao.HostDao;
import com.cloud.keystore.KeystoreManager;
import com.cloud.server.ManagementServer;
import com.cloud.servlet.ConsoleProxyPasswordBasedEncryptor;
import com.cloud.servlet.ConsoleProxyServlet;
import com.cloud.utils.Ternary;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.dao.VMInstanceDao;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
* Utility class to manage interactions with agent-based console access
@ -60,28 +64,19 @@ public abstract class AgentHookBase implements AgentHook {
ConfigurationDao _configDao;
AgentManager _agentMgr;
KeystoreManager _ksMgr;
ManagementServer _ms;
final Random _random = new Random(System.currentTimeMillis());
private String _hashKey;
public AgentHookBase(VMInstanceDao instanceDao, HostDao hostDao, ConfigurationDao cfgDao, KeystoreManager ksMgr,
AgentManager agentMgr) {
AgentManager agentMgr, ManagementServer ms) {
this._instanceDao = instanceDao;
this._hostDao = hostDao;
this._agentMgr = agentMgr;
this._configDao = cfgDao;
this._ksMgr = ksMgr;
}
public String getHashKey() {
// although we may have race condition here, database transaction
// serialization should give us the same key
if (_hashKey == null) {
_hashKey =
_configDao.getValueAndInitIfNotExist(Config.HashKey.key(), Config.HashKey.getCategory(), UUID
.randomUUID().toString());
}
return _hashKey;
this._ms = ms;
}
public AgentControlAnswer onConsoleAccessAuthentication(ConsoleAccessAuthenticationCommand cmd) {
@ -212,10 +207,10 @@ public abstract class AgentHookBase implements AgentHook {
s_logger.error("Could not find and construct a valid SSL certificate");
}
cmd = new StartConsoleProxyAgentHttpHandlerCommand(ksBits, storePassword);
cmd.setEncryptorPassword(getHashKey());
cmd.setEncryptorPassword(getEncryptorPassword());
} else {
cmd = new StartConsoleProxyAgentHttpHandlerCommand();
cmd.setEncryptorPassword(getHashKey());
cmd.setEncryptorPassword(getEncryptorPassword());
}
try {
@ -246,6 +241,33 @@ public abstract class AgentHookBase implements AgentHook {
+ startupCmd.getProxyVmId(), e);
}
}
private String getEncryptorPassword() {
String key;
String iv;
ConsoleProxyPasswordBasedEncryptor.KeyIVPair keyIvPair = null;
// if we failed after reset, something is definitely wrong
for(int i = 0; i < 2; i++) {
key = _ms.getEncryptionKey();
iv = _ms.getEncryptionIV();
keyIvPair = new ConsoleProxyPasswordBasedEncryptor.KeyIVPair(key, iv);
if(keyIvPair.getIvBytes() == null || keyIvPair.getIvBytes().length != 16 ||
keyIvPair.getKeyBytes() == null || keyIvPair.getKeyBytes().length != 16) {
s_logger.warn("Console access AES KeyIV sanity check failed, reset and regenerate");
_ms.resetEncryptionKeyIV();
} else {
break;
}
}
Gson gson = new GsonBuilder().create();
return gson.toJson(keyIvPair);
}
protected abstract HostVO findConsoleProxyHost(StartupProxyCommand cmd);

View File

@ -95,8 +95,10 @@ import com.cloud.resource.ResourceManager;
import com.cloud.resource.ResourceStateAdapter;
import com.cloud.resource.ServerResource;
import com.cloud.resource.UnableDeleteHostException;
import com.cloud.server.ManagementServer;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.servlet.ConsoleProxyPasswordBasedEncryptor;
import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePoolStatus;
import com.cloud.storage.VMTemplateHostVO;
@ -220,6 +222,8 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
TemplateManager templateMgr;
@Inject
IPAddressDao _ipAddressDao;
@Inject
ManagementServer _ms;
private ConsoleProxyListener _listener;
@ -254,7 +258,6 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
private Map<Long, ConsoleProxyLoadInfo> _zoneProxyCountMap; // map <zone id, info about proxy VMs count in zone>
private Map<Long, ConsoleProxyLoadInfo> _zoneVmCountMap; // map <zone id, info about running VMs count in zone>
private String _hashKey;
private String _staticPublicIp;
private int _staticPort;
@ -448,8 +451,8 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
public class VmBasedAgentHook extends AgentHookBase {
public VmBasedAgentHook(VMInstanceDao instanceDao, HostDao hostDao, ConfigurationDao cfgDao,
KeystoreManager ksMgr, AgentManager agentMgr) {
super(instanceDao, hostDao, cfgDao, ksMgr, agentMgr);
KeystoreManager ksMgr, AgentManager agentMgr, ManagementServer ms) {
super(instanceDao, hostDao, cfgDao, ksMgr, agentMgr, ms);
}
@Override
@ -1439,7 +1442,7 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
_listener =
new ConsoleProxyListener(new VmBasedAgentHook(_instanceDao, _hostDao, _configDao, _ksMgr,
_agentMgr));
_agentMgr, _ms));
_agentMgr.registerForHostEvents(_listener, true, true, false);
_itMgr.registerGuru(VirtualMachine.Type.ConsoleProxy, this);
@ -1884,15 +1887,6 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
return sc.find();
}
public String getHashKey() {
// although we may have race conditioning here, database transaction serialization should
// give us the same key
if (_hashKey == null) {
_hashKey = _configDao.getValueAndInitIfNotExist(Config.HashKey.key(), Config.HashKey.getCategory(), UUID.randomUUID().toString());
}
return _hashKey;
}
@Override
public boolean plugNic(Network network, NicTO nic, VirtualMachineTO vm,
ReservationContext context, DeployDestination dest) throws ConcurrentOperationException, ResourceUnavailableException,
@ -1912,4 +1906,5 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
@Override
public void prepareStop(VirtualMachineProfile<ConsoleProxyVO> profile) {
}
}

View File

@ -95,6 +95,9 @@ public interface ManagementServer extends ManagementService, PluggableService {
Pair<List<StoragePoolVO>, Integer> searchForStoragePools(Criteria c);
String getHashKey();
String getEncryptionKey();
String getEncryptionIV();
void resetEncryptionKeyIV();
public void enableAdminUser(String password);
}

View File

@ -22,6 +22,8 @@ import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
@ -400,7 +402,9 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
@Inject ClusterManager _clusterMgr;
private String _hashKey = null;
private String _encryptionKey = null;
private String _encryptionIV = null;
@Inject
protected AffinityGroupVMMapDao _affinityGroupVMMapDao;
@ -3000,15 +3004,66 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
@Override
public String getHashKey() {
// although we may have race conditioning here, database transaction
// serialization should
// although we may have race conditioning here, database transaction serialization should
// give us the same key
if (_hashKey == null) {
_hashKey = _configDao.getValueAndInitIfNotExist(Config.HashKey.key(), Config.HashKey.getCategory(), UUID.randomUUID().toString());
_hashKey = _configDao.getValueAndInitIfNotExist(Config.HashKey.key(), Config.HashKey.getCategory(),
getBase64EncodedRandomKey(128));
}
return _hashKey;
}
@Override
public String getEncryptionKey() {
if (_encryptionKey == null) {
_encryptionKey = _configDao.getValueAndInitIfNotExist(Config.EncryptionKey.key(),
Config.EncryptionKey.getCategory(),
getBase64EncodedRandomKey(128));
}
return _encryptionKey;
}
@Override
public String getEncryptionIV() {
if (_encryptionIV == null) {
_encryptionIV = _configDao.getValueAndInitIfNotExist(Config.EncryptionIV.key(),
Config.EncryptionIV.getCategory(),
getBase64EncodedRandomKey(128));
}
return _encryptionIV;
}
@Override
@DB
public void resetEncryptionKeyIV() {
SearchBuilder<ConfigurationVO> sb = _configDao.createSearchBuilder();
sb.and("name1", sb.entity().getName(), SearchCriteria.Op.EQ);
sb.or("name2", sb.entity().getName(), SearchCriteria.Op.EQ);
sb.done();
SearchCriteria<ConfigurationVO> sc = sb.create();
sc.setParameters("name1", Config.EncryptionKey.key());
sc.setParameters("name2", Config.EncryptionIV.key());
_configDao.expunge(sc);
_encryptionKey = null;
_encryptionIV = null;
}
private static String getBase64EncodedRandomKey(int nBits) {
SecureRandom random;
try {
random = SecureRandom.getInstance("SHA1PRNG");
byte[] keyBytes = new byte[nBits/8];
random.nextBytes(keyBytes);
return Base64.encodeBase64URLSafeString(keyBytes);
} catch (NoSuchAlgorithmException e) {
s_logger.error("Unhandled exception: ", e);
}
return null;
}
@Override
public SSHKeyPair createSSHKeyPair(CreateSSHKeyPairCmd cmd) {
Account caller = UserContext.current().getCaller();

View File

@ -16,13 +16,16 @@
// under the License.
package com.cloud.servlet;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
@ -35,26 +38,26 @@ import com.google.gson.GsonBuilder;
public class ConsoleProxyPasswordBasedEncryptor {
private static final Logger s_logger = Logger.getLogger(ConsoleProxyPasswordBasedEncryptor.class);
private String password;
private Gson gson;
// key/IV will be set in 128 bit strength
private KeyIVPair keyIvPair;
public ConsoleProxyPasswordBasedEncryptor(String password) {
this.password = password;
gson = new GsonBuilder().create();
keyIvPair = gson.fromJson(password, KeyIVPair.class);
}
public String encryptText(String text) {
if(text == null || text.isEmpty())
return text;
assert(password != null);
assert(!password.isEmpty());
try {
Cipher cipher = Cipher.getInstance("DES");
int maxKeySize = 8;
SecretKeySpec keySpec = new SecretKeySpec(normalizeKey(password.getBytes(), maxKeySize), "DES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(keyIvPair.getKeyBytes(), "AES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(keyIvPair.getIvBytes()));
byte[] encryptedBytes = cipher.doFinal(text.getBytes());
return Base64.encodeBase64URLSafeString(encryptedBytes);
} catch (NoSuchAlgorithmException e) {
@ -72,6 +75,9 @@ public class ConsoleProxyPasswordBasedEncryptor {
} catch (InvalidKeyException e) {
s_logger.error("Unexpected exception ", e);
return null;
} catch (InvalidAlgorithmParameterException e) {
s_logger.error("Unexpected exception ", e);
return null;
}
}
@ -79,14 +85,10 @@ public class ConsoleProxyPasswordBasedEncryptor {
if(encryptedText == null || encryptedText.isEmpty())
return encryptedText;
assert(password != null);
assert(!password.isEmpty());
try {
Cipher cipher = Cipher.getInstance("DES");
int maxKeySize = 8;
SecretKeySpec keySpec = new SecretKeySpec(normalizeKey(password.getBytes(), maxKeySize), "DES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(keyIvPair.getKeyBytes(), "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(keyIvPair.getIvBytes()));
byte[] encryptedBytes = Base64.decodeBase64(encryptedText);
return new String(cipher.doFinal(encryptedBytes));
@ -105,6 +107,9 @@ public class ConsoleProxyPasswordBasedEncryptor {
} catch (InvalidKeyException e) {
s_logger.error("Unexpected exception ", e);
return null;
} catch (InvalidAlgorithmParameterException e) {
s_logger.error("Unexpected exception ", e);
return null;
}
}
@ -125,13 +130,63 @@ public class ConsoleProxyPasswordBasedEncryptor {
return (T)gson.fromJson(json, clz);
}
private static byte[] normalizeKey(byte[] keyBytes, int keySize) {
assert(keySize > 0);
byte[] key = new byte[keySize];
public static class KeyIVPair {
String base64EncodedKeyBytes;
String base64EncodedIvBytes;
for(int i = 0; i < keyBytes.length; i++)
key[i%keySize] ^= keyBytes[i];
public KeyIVPair() {
}
return key;
public KeyIVPair(String base64EncodedKeyBytes, String base64EncodedIvBytes) {
this.base64EncodedKeyBytes = base64EncodedKeyBytes;
this.base64EncodedIvBytes = base64EncodedIvBytes;
}
public byte[] getKeyBytes() {
return Base64.decodeBase64(base64EncodedKeyBytes);
}
public void setKeyBytes(byte[] keyBytes) {
base64EncodedKeyBytes = Base64.encodeBase64URLSafeString(keyBytes);
}
public byte[] getIvBytes() {
return Base64.decodeBase64(base64EncodedIvBytes);
}
public void setIvBytes(byte[] ivBytes) {
base64EncodedIvBytes = Base64.encodeBase64URLSafeString(ivBytes);
}
}
public static void main(String[] args) {
SecureRandom random;
try {
random = SecureRandom.getInstance("SHA1PRNG");
byte[] keyBytes = new byte[16];
random.nextBytes(keyBytes);
byte[] ivBytes = new byte[16];
random.nextBytes(ivBytes);
KeyIVPair keyIvPair = new KeyIVPair("8x/xUBgX0Up+3UEo39dSeG277JhVj31+ElHkN5+EC0Q=", "Y2SUiIN6JXTdKNK/ZMDyVtLB7gAM9MCCiyrP1xd3bSQ=");
//keyIvPair.setKeyBytes(keyBytes);
//keyIvPair.setIvBytes(ivBytes);
Gson gson = new GsonBuilder().create();
ConsoleProxyPasswordBasedEncryptor encryptor = new ConsoleProxyPasswordBasedEncryptor(gson.toJson(keyIvPair));
String encrypted = encryptor.encryptText("Hello, world");
System.out.println("Encrypted result: " + encrypted);
String decrypted = encryptor.decryptText(encrypted);
System.out.println("Decrypted result: " + decrypted);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}

View File

@ -55,6 +55,8 @@ import com.cloud.utils.db.Transaction;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineManager;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
* Thumbnail access : /console?cmd=thumbnail&vm=xxx&w=xxx&h=xxx
@ -75,6 +77,8 @@ public class ConsoleProxyServlet extends HttpServlet {
static ManagementServer s_ms;
private Gson _gson = new GsonBuilder().create();
public ConsoleProxyServlet() {
}
@ -327,6 +331,14 @@ public class ConsoleProxyServlet extends HttpServlet {
return new Ternary<String, String, String>(host, tunnelUrl, tunnelSession);
}
private String getEncryptorPassword() {
String key = _ms.getEncryptionKey();
String iv = _ms.getEncryptionIV();
ConsoleProxyPasswordBasedEncryptor.KeyIVPair keyIvPair = new ConsoleProxyPasswordBasedEncryptor.KeyIVPair(key, iv);
return _gson.toJson(keyIvPair);
}
private String composeThumbnailUrl(String rootUrl, VMInstanceVO vm, HostVO hostVo, int w, int h) {
StringBuffer sb = new StringBuffer(rootUrl);
@ -340,7 +352,7 @@ public class ConsoleProxyServlet extends HttpServlet {
tag = _identityService.getIdentityUuid("vm_instance", tag);
String ticket = genAccessTicket(host, String.valueOf(portInfo.second()), sid, tag);
ConsoleProxyPasswordBasedEncryptor encryptor = new ConsoleProxyPasswordBasedEncryptor(_ms.getHashKey());
ConsoleProxyPasswordBasedEncryptor encryptor = new ConsoleProxyPasswordBasedEncryptor(getEncryptorPassword());
ConsoleProxyClientParam param = new ConsoleProxyClientParam();
param.setClientHostAddress(parsedHostInfo.first());
param.setClientHostPort(portInfo.second());
@ -376,7 +388,7 @@ public class ConsoleProxyServlet extends HttpServlet {
String tag = String.valueOf(vm.getId());
tag = _identityService.getIdentityUuid("vm_instance", tag);
String ticket = genAccessTicket(host, String.valueOf(portInfo.second()), sid, tag);
ConsoleProxyPasswordBasedEncryptor encryptor = new ConsoleProxyPasswordBasedEncryptor(_ms.getHashKey());
ConsoleProxyPasswordBasedEncryptor encryptor = new ConsoleProxyPasswordBasedEncryptor(getEncryptorPassword());
ConsoleProxyClientParam param = new ConsoleProxyClientParam();
param.setClientHostAddress(parsedHostInfo.first());
param.setClientHostPort(portInfo.second());

View File

@ -16,13 +16,16 @@
// under the License.
package com.cloud.consoleproxy;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
@ -33,110 +36,162 @@ import com.google.gson.GsonBuilder;
/**
*
* A simple password based encyrptor based on DES. It can serialize simple POJO object into URL safe string
* @author Kelven Yang
* A simple password based encyrptor based on AES/CBC. It can serialize simple POJO object into URL safe string
* and deserialize it back.
*
*/
public class ConsoleProxyPasswordBasedEncryptor {
private static final Logger s_logger = Logger.getLogger(ConsoleProxyPasswordBasedEncryptor.class);
private String password;
private Gson gson;
public ConsoleProxyPasswordBasedEncryptor(String password) {
this.password = password;
gson = new GsonBuilder().create();
}
public String encryptText(String text) {
if(text == null || text.isEmpty())
return text;
assert(password != null);
assert(!password.isEmpty());
try {
Cipher cipher = Cipher.getInstance("DES");
int maxKeySize = 8;
SecretKeySpec keySpec = new SecretKeySpec(normalizeKey(password.getBytes(), maxKeySize), "DES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] encryptedBytes = cipher.doFinal(text.getBytes());
return Base64.encodeBase64URLSafeString(encryptedBytes);
} catch (NoSuchAlgorithmException e) {
s_logger.error("Unexpected exception ", e);
return null;
} catch (NoSuchPaddingException e) {
s_logger.error("Unexpected exception ", e);
return null;
} catch (IllegalBlockSizeException e) {
s_logger.error("Unexpected exception ", e);
return null;
} catch (BadPaddingException e) {
s_logger.error("Unexpected exception ", e);
return null;
} catch (InvalidKeyException e) {
s_logger.error("Unexpected exception ", e);
return null;
}
}
private static final Logger s_logger = Logger.getLogger(ConsoleProxyPasswordBasedEncryptor.class);
private Gson gson;
// key/IV will be set in 128 bit strength
private KeyIVPair keyIvPair;
public ConsoleProxyPasswordBasedEncryptor(String password) {
gson = new GsonBuilder().create();
keyIvPair = gson.fromJson(password, KeyIVPair.class);
}
public String encryptText(String text) {
if(text == null || text.isEmpty())
return text;
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(keyIvPair.getKeyBytes(), "AES");
public String decryptText(String encryptedText) {
if(encryptedText == null || encryptedText.isEmpty())
return encryptedText;
cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(keyIvPair.getIvBytes()));
byte[] encryptedBytes = cipher.doFinal(text.getBytes());
return Base64.encodeBase64URLSafeString(encryptedBytes);
} catch (NoSuchAlgorithmException e) {
s_logger.error("Unexpected exception ", e);
return null;
} catch (NoSuchPaddingException e) {
s_logger.error("Unexpected exception ", e);
return null;
} catch (IllegalBlockSizeException e) {
s_logger.error("Unexpected exception ", e);
return null;
} catch (BadPaddingException e) {
s_logger.error("Unexpected exception ", e);
return null;
} catch (InvalidKeyException e) {
s_logger.error("Unexpected exception ", e);
return null;
} catch (InvalidAlgorithmParameterException e) {
s_logger.error("Unexpected exception ", e);
return null;
}
}
assert(password != null);
assert(!password.isEmpty());
public String decryptText(String encryptedText) {
if(encryptedText == null || encryptedText.isEmpty())
return encryptedText;
try {
Cipher cipher = Cipher.getInstance("DES");
int maxKeySize = 8;
SecretKeySpec keySpec = new SecretKeySpec(normalizeKey(password.getBytes(), maxKeySize), "DES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] encryptedBytes = Base64.decodeBase64(encryptedText);
return new String(cipher.doFinal(encryptedBytes));
} catch (NoSuchAlgorithmException e) {
s_logger.error("Unexpected exception ", e);
return null;
} catch (NoSuchPaddingException e) {
s_logger.error("Unexpected exception ", e);
return null;
} catch (IllegalBlockSizeException e) {
s_logger.error("Unexpected exception ", e);
return null;
} catch (BadPaddingException e) {
s_logger.error("Unexpected exception ", e);
return null;
} catch (InvalidKeyException e) {
s_logger.error("Unexpected exception ", e);
return null;
}
}
public <T> String encryptObject(Class<?> clz, T obj) {
if(obj == null)
return null;
String json = gson.toJson(obj);
return encryptText(json);
}
@SuppressWarnings("unchecked")
public <T> T decryptObject(Class<?> clz, String encrypted) {
if(encrypted == null || encrypted.isEmpty())
return null;
String json = decryptText(encrypted);
return (T)gson.fromJson(json, clz);
}
private static byte[] normalizeKey(byte[] keyBytes, int keySize) {
assert(keySize > 0);
byte[] key = new byte[keySize];
for(int i = 0; i < keyBytes.length; i++)
key[i%keySize] ^= keyBytes[i];
return key;
}
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(keyIvPair.getKeyBytes(), "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(keyIvPair.getIvBytes()));
byte[] encryptedBytes = Base64.decodeBase64(encryptedText);
return new String(cipher.doFinal(encryptedBytes));
} catch (NoSuchAlgorithmException e) {
s_logger.error("Unexpected exception ", e);
return null;
} catch (NoSuchPaddingException e) {
s_logger.error("Unexpected exception ", e);
return null;
} catch (IllegalBlockSizeException e) {
s_logger.error("Unexpected exception ", e);
return null;
} catch (BadPaddingException e) {
s_logger.error("Unexpected exception ", e);
return null;
} catch (InvalidKeyException e) {
s_logger.error("Unexpected exception ", e);
return null;
} catch (InvalidAlgorithmParameterException e) {
s_logger.error("Unexpected exception ", e);
return null;
}
}
public <T> String encryptObject(Class<?> clz, T obj) {
if(obj == null)
return null;
String json = gson.toJson(obj);
return encryptText(json);
}
@SuppressWarnings("unchecked")
public <T> T decryptObject(Class<?> clz, String encrypted) {
if(encrypted == null || encrypted.isEmpty())
return null;
String json = decryptText(encrypted);
return (T)gson.fromJson(json, clz);
}
public static class KeyIVPair {
String base64EncodedKeyBytes;
String base64EncodedIvBytes;
public KeyIVPair() {
}
public KeyIVPair(String base64EncodedKeyBytes, String base64EncodedIvBytes) {
this.base64EncodedKeyBytes = base64EncodedKeyBytes;
this.base64EncodedIvBytes = base64EncodedIvBytes;
}
public byte[] getKeyBytes() {
return Base64.decodeBase64(base64EncodedKeyBytes);
}
public void setKeyBytes(byte[] keyBytes) {
base64EncodedKeyBytes = Base64.encodeBase64URLSafeString(keyBytes);
}
public byte[] getIvBytes() {
return Base64.decodeBase64(base64EncodedIvBytes);
}
public void setIvBytes(byte[] ivBytes) {
base64EncodedIvBytes = Base64.encodeBase64URLSafeString(ivBytes);
}
}
public static void main(String[] args) {
SecureRandom random;
try {
random = SecureRandom.getInstance("SHA1PRNG");
byte[] keyBytes = new byte[16];
random.nextBytes(keyBytes);
byte[] ivBytes = new byte[16];
random.nextBytes(ivBytes);
KeyIVPair keyIvPair = new KeyIVPair("8x/xUBgX0Up+3UEo39dSeG277JhVj31+ElHkN5+EC0Q=", "Y2SUiIN6JXTdKNK/ZMDyVtLB7gAM9MCCiyrP1xd3bSQ=");
//keyIvPair.setKeyBytes(keyBytes);
//keyIvPair.setIvBytes(ivBytes);
Gson gson = new GsonBuilder().create();
ConsoleProxyPasswordBasedEncryptor encryptor = new ConsoleProxyPasswordBasedEncryptor(gson.toJson(keyIvPair));
String encrypted = encryptor.encryptText("Hello, world");
System.out.println("Encrypted result: " + encrypted);
String decrypted = encryptor.decryptText(encrypted);
System.out.println("Decrypted result: " + decrypted);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}