mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
CLOUDSTACK-2039: Improve console access security with 128-bit AES encryption and securely-randomized key generation
This commit is contained in:
parent
8d33353b40
commit
34f8f795e1
@ -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),
|
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"),
|
//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),
|
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),
|
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),
|
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),
|
||||||
|
|||||||
@ -33,6 +33,7 @@ import com.cloud.host.HostVO;
|
|||||||
import com.cloud.host.dao.HostDao;
|
import com.cloud.host.dao.HostDao;
|
||||||
import com.cloud.info.ConsoleProxyInfo;
|
import com.cloud.info.ConsoleProxyInfo;
|
||||||
import com.cloud.keystore.KeystoreManager;
|
import com.cloud.keystore.KeystoreManager;
|
||||||
|
import com.cloud.server.ManagementServer;
|
||||||
import com.cloud.utils.NumbersUtil;
|
import com.cloud.utils.NumbersUtil;
|
||||||
import com.cloud.utils.component.ManagerBase;
|
import com.cloud.utils.component.ManagerBase;
|
||||||
import com.cloud.vm.ConsoleProxyVO;
|
import com.cloud.vm.ConsoleProxyVO;
|
||||||
@ -69,12 +70,13 @@ public class AgentBasedConsoleProxyManager extends ManagerBase implements Consol
|
|||||||
protected KeystoreManager _ksMgr;
|
protected KeystoreManager _ksMgr;
|
||||||
|
|
||||||
@Inject ConfigurationDao _configDao;
|
@Inject ConfigurationDao _configDao;
|
||||||
|
@Inject ManagementServer _ms;
|
||||||
|
|
||||||
public class AgentBasedAgentHook extends AgentHookBase {
|
public class AgentBasedAgentHook extends AgentHookBase {
|
||||||
|
|
||||||
public AgentBasedAgentHook(VMInstanceDao instanceDao, HostDao hostDao, ConfigurationDao cfgDao,
|
public AgentBasedAgentHook(VMInstanceDao instanceDao, HostDao hostDao, ConfigurationDao cfgDao,
|
||||||
KeystoreManager ksMgr, AgentManager agentMgr) {
|
KeystoreManager ksMgr, AgentManager agentMgr, ManagementServer ms) {
|
||||||
super(instanceDao, hostDao, cfgDao, ksMgr, agentMgr);
|
super(instanceDao, hostDao, cfgDao, ksMgr, agentMgr, ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -120,7 +122,7 @@ public class AgentBasedConsoleProxyManager extends ManagerBase implements Consol
|
|||||||
_consoleProxyUrlDomain = configs.get("consoleproxy.url.domain");
|
_consoleProxyUrlDomain = configs.get("consoleproxy.url.domain");
|
||||||
|
|
||||||
_listener =
|
_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);
|
_agentMgr.registerForHostEvents(_listener, true, true, false);
|
||||||
|
|
||||||
if (s_logger.isInfoEnabled()) {
|
if (s_logger.isInfoEnabled()) {
|
||||||
|
|||||||
@ -42,10 +42,14 @@ import com.cloud.host.HostVO;
|
|||||||
import com.cloud.host.Status;
|
import com.cloud.host.Status;
|
||||||
import com.cloud.host.dao.HostDao;
|
import com.cloud.host.dao.HostDao;
|
||||||
import com.cloud.keystore.KeystoreManager;
|
import com.cloud.keystore.KeystoreManager;
|
||||||
|
import com.cloud.server.ManagementServer;
|
||||||
|
import com.cloud.servlet.ConsoleProxyPasswordBasedEncryptor;
|
||||||
import com.cloud.servlet.ConsoleProxyServlet;
|
import com.cloud.servlet.ConsoleProxyServlet;
|
||||||
import com.cloud.utils.Ternary;
|
import com.cloud.utils.Ternary;
|
||||||
import com.cloud.vm.VirtualMachine;
|
import com.cloud.vm.VirtualMachine;
|
||||||
import com.cloud.vm.dao.VMInstanceDao;
|
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
|
* Utility class to manage interactions with agent-based console access
|
||||||
@ -60,28 +64,19 @@ public abstract class AgentHookBase implements AgentHook {
|
|||||||
ConfigurationDao _configDao;
|
ConfigurationDao _configDao;
|
||||||
AgentManager _agentMgr;
|
AgentManager _agentMgr;
|
||||||
KeystoreManager _ksMgr;
|
KeystoreManager _ksMgr;
|
||||||
|
ManagementServer _ms;
|
||||||
final Random _random = new Random(System.currentTimeMillis());
|
final Random _random = new Random(System.currentTimeMillis());
|
||||||
private String _hashKey;
|
private String _hashKey;
|
||||||
|
|
||||||
|
|
||||||
public AgentHookBase(VMInstanceDao instanceDao, HostDao hostDao, ConfigurationDao cfgDao, KeystoreManager ksMgr,
|
public AgentHookBase(VMInstanceDao instanceDao, HostDao hostDao, ConfigurationDao cfgDao, KeystoreManager ksMgr,
|
||||||
AgentManager agentMgr) {
|
AgentManager agentMgr, ManagementServer ms) {
|
||||||
this._instanceDao = instanceDao;
|
this._instanceDao = instanceDao;
|
||||||
this._hostDao = hostDao;
|
this._hostDao = hostDao;
|
||||||
this._agentMgr = agentMgr;
|
this._agentMgr = agentMgr;
|
||||||
this._configDao = cfgDao;
|
this._configDao = cfgDao;
|
||||||
this._ksMgr = ksMgr;
|
this._ksMgr = ksMgr;
|
||||||
}
|
this._ms = ms;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AgentControlAnswer onConsoleAccessAuthentication(ConsoleAccessAuthenticationCommand cmd) {
|
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");
|
s_logger.error("Could not find and construct a valid SSL certificate");
|
||||||
}
|
}
|
||||||
cmd = new StartConsoleProxyAgentHttpHandlerCommand(ksBits, storePassword);
|
cmd = new StartConsoleProxyAgentHttpHandlerCommand(ksBits, storePassword);
|
||||||
cmd.setEncryptorPassword(getHashKey());
|
cmd.setEncryptorPassword(getEncryptorPassword());
|
||||||
} else {
|
} else {
|
||||||
cmd = new StartConsoleProxyAgentHttpHandlerCommand();
|
cmd = new StartConsoleProxyAgentHttpHandlerCommand();
|
||||||
cmd.setEncryptorPassword(getHashKey());
|
cmd.setEncryptorPassword(getEncryptorPassword());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -247,6 +242,33 @@ public abstract class AgentHookBase implements AgentHook {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
protected abstract HostVO findConsoleProxyHost(StartupProxyCommand cmd);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -95,8 +95,10 @@ import com.cloud.resource.ResourceManager;
|
|||||||
import com.cloud.resource.ResourceStateAdapter;
|
import com.cloud.resource.ResourceStateAdapter;
|
||||||
import com.cloud.resource.ServerResource;
|
import com.cloud.resource.ServerResource;
|
||||||
import com.cloud.resource.UnableDeleteHostException;
|
import com.cloud.resource.UnableDeleteHostException;
|
||||||
|
import com.cloud.server.ManagementServer;
|
||||||
import com.cloud.service.ServiceOfferingVO;
|
import com.cloud.service.ServiceOfferingVO;
|
||||||
import com.cloud.service.dao.ServiceOfferingDao;
|
import com.cloud.service.dao.ServiceOfferingDao;
|
||||||
|
import com.cloud.servlet.ConsoleProxyPasswordBasedEncryptor;
|
||||||
import com.cloud.storage.StorageManager;
|
import com.cloud.storage.StorageManager;
|
||||||
import com.cloud.storage.StoragePoolStatus;
|
import com.cloud.storage.StoragePoolStatus;
|
||||||
import com.cloud.storage.VMTemplateHostVO;
|
import com.cloud.storage.VMTemplateHostVO;
|
||||||
@ -220,6 +222,8 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
|
|||||||
TemplateManager templateMgr;
|
TemplateManager templateMgr;
|
||||||
@Inject
|
@Inject
|
||||||
IPAddressDao _ipAddressDao;
|
IPAddressDao _ipAddressDao;
|
||||||
|
@Inject
|
||||||
|
ManagementServer _ms;
|
||||||
|
|
||||||
private ConsoleProxyListener _listener;
|
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> _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 Map<Long, ConsoleProxyLoadInfo> _zoneVmCountMap; // map <zone id, info about running VMs count in zone>
|
||||||
|
|
||||||
private String _hashKey;
|
|
||||||
private String _staticPublicIp;
|
private String _staticPublicIp;
|
||||||
private int _staticPort;
|
private int _staticPort;
|
||||||
|
|
||||||
@ -448,8 +451,8 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
|
|||||||
public class VmBasedAgentHook extends AgentHookBase {
|
public class VmBasedAgentHook extends AgentHookBase {
|
||||||
|
|
||||||
public VmBasedAgentHook(VMInstanceDao instanceDao, HostDao hostDao, ConfigurationDao cfgDao,
|
public VmBasedAgentHook(VMInstanceDao instanceDao, HostDao hostDao, ConfigurationDao cfgDao,
|
||||||
KeystoreManager ksMgr, AgentManager agentMgr) {
|
KeystoreManager ksMgr, AgentManager agentMgr, ManagementServer ms) {
|
||||||
super(instanceDao, hostDao, cfgDao, ksMgr, agentMgr);
|
super(instanceDao, hostDao, cfgDao, ksMgr, agentMgr, ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1439,7 +1442,7 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
|
|||||||
|
|
||||||
_listener =
|
_listener =
|
||||||
new ConsoleProxyListener(new VmBasedAgentHook(_instanceDao, _hostDao, _configDao, _ksMgr,
|
new ConsoleProxyListener(new VmBasedAgentHook(_instanceDao, _hostDao, _configDao, _ksMgr,
|
||||||
_agentMgr));
|
_agentMgr, _ms));
|
||||||
_agentMgr.registerForHostEvents(_listener, true, true, false);
|
_agentMgr.registerForHostEvents(_listener, true, true, false);
|
||||||
|
|
||||||
_itMgr.registerGuru(VirtualMachine.Type.ConsoleProxy, this);
|
_itMgr.registerGuru(VirtualMachine.Type.ConsoleProxy, this);
|
||||||
@ -1884,15 +1887,6 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
|
|||||||
return sc.find();
|
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
|
@Override
|
||||||
public boolean plugNic(Network network, NicTO nic, VirtualMachineTO vm,
|
public boolean plugNic(Network network, NicTO nic, VirtualMachineTO vm,
|
||||||
ReservationContext context, DeployDestination dest) throws ConcurrentOperationException, ResourceUnavailableException,
|
ReservationContext context, DeployDestination dest) throws ConcurrentOperationException, ResourceUnavailableException,
|
||||||
@ -1912,4 +1906,5 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
|
|||||||
@Override
|
@Override
|
||||||
public void prepareStop(VirtualMachineProfile<ConsoleProxyVO> profile) {
|
public void prepareStop(VirtualMachineProfile<ConsoleProxyVO> profile) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -95,6 +95,9 @@ public interface ManagementServer extends ManagementService, PluggableService {
|
|||||||
Pair<List<StoragePoolVO>, Integer> searchForStoragePools(Criteria c);
|
Pair<List<StoragePoolVO>, Integer> searchForStoragePools(Criteria c);
|
||||||
|
|
||||||
String getHashKey();
|
String getHashKey();
|
||||||
|
String getEncryptionKey();
|
||||||
|
String getEncryptionIV();
|
||||||
|
void resetEncryptionKeyIV();
|
||||||
|
|
||||||
public void enableAdminUser(String password);
|
public void enableAdminUser(String password);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,8 @@ import java.net.InetAddress;
|
|||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@ -400,6 +402,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
|||||||
|
|
||||||
@Inject ClusterManager _clusterMgr;
|
@Inject ClusterManager _clusterMgr;
|
||||||
private String _hashKey = null;
|
private String _hashKey = null;
|
||||||
|
private String _encryptionKey = null;
|
||||||
|
private String _encryptionIV = null;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected AffinityGroupVMMapDao _affinityGroupVMMapDao;
|
protected AffinityGroupVMMapDao _affinityGroupVMMapDao;
|
||||||
@ -3000,15 +3004,66 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getHashKey() {
|
public String getHashKey() {
|
||||||
// although we may have race conditioning here, database transaction
|
// although we may have race conditioning here, database transaction serialization should
|
||||||
// serialization should
|
|
||||||
// give us the same key
|
// give us the same key
|
||||||
if (_hashKey == null) {
|
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;
|
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
|
@Override
|
||||||
public SSHKeyPair createSSHKeyPair(CreateSSHKeyPairCmd cmd) {
|
public SSHKeyPair createSSHKeyPair(CreateSSHKeyPairCmd cmd) {
|
||||||
Account caller = UserContext.current().getCaller();
|
Account caller = UserContext.current().getCaller();
|
||||||
|
|||||||
@ -16,13 +16,16 @@
|
|||||||
// under the License.
|
// under the License.
|
||||||
package com.cloud.servlet;
|
package com.cloud.servlet;
|
||||||
|
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
import javax.crypto.BadPaddingException;
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
import javax.crypto.NoSuchPaddingException;
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
import org.apache.commons.codec.binary.Base64;
|
||||||
@ -35,26 +38,26 @@ import com.google.gson.GsonBuilder;
|
|||||||
public class ConsoleProxyPasswordBasedEncryptor {
|
public class ConsoleProxyPasswordBasedEncryptor {
|
||||||
private static final Logger s_logger = Logger.getLogger(ConsoleProxyPasswordBasedEncryptor.class);
|
private static final Logger s_logger = Logger.getLogger(ConsoleProxyPasswordBasedEncryptor.class);
|
||||||
|
|
||||||
private String password;
|
|
||||||
private Gson gson;
|
private Gson gson;
|
||||||
|
|
||||||
|
// key/IV will be set in 128 bit strength
|
||||||
|
private KeyIVPair keyIvPair;
|
||||||
|
|
||||||
public ConsoleProxyPasswordBasedEncryptor(String password) {
|
public ConsoleProxyPasswordBasedEncryptor(String password) {
|
||||||
this.password = password;
|
|
||||||
gson = new GsonBuilder().create();
|
gson = new GsonBuilder().create();
|
||||||
|
keyIvPair = gson.fromJson(password, KeyIVPair.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String encryptText(String text) {
|
public String encryptText(String text) {
|
||||||
if(text == null || text.isEmpty())
|
if(text == null || text.isEmpty())
|
||||||
return text;
|
return text;
|
||||||
|
|
||||||
assert(password != null);
|
|
||||||
assert(!password.isEmpty());
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Cipher cipher = Cipher.getInstance("DES");
|
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||||
int maxKeySize = 8;
|
SecretKeySpec keySpec = new SecretKeySpec(keyIvPair.getKeyBytes(), "AES");
|
||||||
SecretKeySpec keySpec = new SecretKeySpec(normalizeKey(password.getBytes(), maxKeySize), "DES");
|
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
|
cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(keyIvPair.getIvBytes()));
|
||||||
|
|
||||||
byte[] encryptedBytes = cipher.doFinal(text.getBytes());
|
byte[] encryptedBytes = cipher.doFinal(text.getBytes());
|
||||||
return Base64.encodeBase64URLSafeString(encryptedBytes);
|
return Base64.encodeBase64URLSafeString(encryptedBytes);
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
@ -72,6 +75,9 @@ public class ConsoleProxyPasswordBasedEncryptor {
|
|||||||
} catch (InvalidKeyException e) {
|
} catch (InvalidKeyException e) {
|
||||||
s_logger.error("Unexpected exception ", e);
|
s_logger.error("Unexpected exception ", e);
|
||||||
return null;
|
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())
|
if(encryptedText == null || encryptedText.isEmpty())
|
||||||
return encryptedText;
|
return encryptedText;
|
||||||
|
|
||||||
assert(password != null);
|
|
||||||
assert(!password.isEmpty());
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Cipher cipher = Cipher.getInstance("DES");
|
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||||
int maxKeySize = 8;
|
SecretKeySpec keySpec = new SecretKeySpec(keyIvPair.getKeyBytes(), "AES");
|
||||||
SecretKeySpec keySpec = new SecretKeySpec(normalizeKey(password.getBytes(), maxKeySize), "DES");
|
cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(keyIvPair.getIvBytes()));
|
||||||
cipher.init(Cipher.DECRYPT_MODE, keySpec);
|
|
||||||
|
|
||||||
byte[] encryptedBytes = Base64.decodeBase64(encryptedText);
|
byte[] encryptedBytes = Base64.decodeBase64(encryptedText);
|
||||||
return new String(cipher.doFinal(encryptedBytes));
|
return new String(cipher.doFinal(encryptedBytes));
|
||||||
@ -105,6 +107,9 @@ public class ConsoleProxyPasswordBasedEncryptor {
|
|||||||
} catch (InvalidKeyException e) {
|
} catch (InvalidKeyException e) {
|
||||||
s_logger.error("Unexpected exception ", e);
|
s_logger.error("Unexpected exception ", e);
|
||||||
return null;
|
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);
|
return (T)gson.fromJson(json, clz);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] normalizeKey(byte[] keyBytes, int keySize) {
|
public static class KeyIVPair {
|
||||||
assert(keySize > 0);
|
String base64EncodedKeyBytes;
|
||||||
byte[] key = new byte[keySize];
|
String base64EncodedIvBytes;
|
||||||
|
|
||||||
for(int i = 0; i < keyBytes.length; i++)
|
public KeyIVPair() {
|
||||||
key[i%keySize] ^= keyBytes[i];
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -55,6 +55,8 @@ import com.cloud.utils.db.Transaction;
|
|||||||
import com.cloud.vm.VMInstanceVO;
|
import com.cloud.vm.VMInstanceVO;
|
||||||
import com.cloud.vm.VirtualMachine;
|
import com.cloud.vm.VirtualMachine;
|
||||||
import com.cloud.vm.VirtualMachineManager;
|
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
|
* Thumbnail access : /console?cmd=thumbnail&vm=xxx&w=xxx&h=xxx
|
||||||
@ -75,6 +77,8 @@ public class ConsoleProxyServlet extends HttpServlet {
|
|||||||
|
|
||||||
static ManagementServer s_ms;
|
static ManagementServer s_ms;
|
||||||
|
|
||||||
|
private Gson _gson = new GsonBuilder().create();
|
||||||
|
|
||||||
public ConsoleProxyServlet() {
|
public ConsoleProxyServlet() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,6 +331,14 @@ public class ConsoleProxyServlet extends HttpServlet {
|
|||||||
return new Ternary<String, String, String>(host, tunnelUrl, tunnelSession);
|
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) {
|
private String composeThumbnailUrl(String rootUrl, VMInstanceVO vm, HostVO hostVo, int w, int h) {
|
||||||
StringBuffer sb = new StringBuffer(rootUrl);
|
StringBuffer sb = new StringBuffer(rootUrl);
|
||||||
|
|
||||||
@ -340,7 +352,7 @@ public class ConsoleProxyServlet extends HttpServlet {
|
|||||||
tag = _identityService.getIdentityUuid("vm_instance", tag);
|
tag = _identityService.getIdentityUuid("vm_instance", tag);
|
||||||
String ticket = genAccessTicket(host, String.valueOf(portInfo.second()), sid, 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();
|
ConsoleProxyClientParam param = new ConsoleProxyClientParam();
|
||||||
param.setClientHostAddress(parsedHostInfo.first());
|
param.setClientHostAddress(parsedHostInfo.first());
|
||||||
param.setClientHostPort(portInfo.second());
|
param.setClientHostPort(portInfo.second());
|
||||||
@ -376,7 +388,7 @@ public class ConsoleProxyServlet extends HttpServlet {
|
|||||||
String tag = String.valueOf(vm.getId());
|
String tag = String.valueOf(vm.getId());
|
||||||
tag = _identityService.getIdentityUuid("vm_instance", tag);
|
tag = _identityService.getIdentityUuid("vm_instance", tag);
|
||||||
String ticket = genAccessTicket(host, String.valueOf(portInfo.second()), sid, 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();
|
ConsoleProxyClientParam param = new ConsoleProxyClientParam();
|
||||||
param.setClientHostAddress(parsedHostInfo.first());
|
param.setClientHostAddress(parsedHostInfo.first());
|
||||||
param.setClientHostPort(portInfo.second());
|
param.setClientHostPort(portInfo.second());
|
||||||
|
|||||||
@ -16,13 +16,16 @@
|
|||||||
// under the License.
|
// under the License.
|
||||||
package com.cloud.consoleproxy;
|
package com.cloud.consoleproxy;
|
||||||
|
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
import javax.crypto.BadPaddingException;
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
import javax.crypto.NoSuchPaddingException;
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
import org.apache.commons.codec.binary.Base64;
|
||||||
@ -33,33 +36,34 @@ 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.
|
* and deserialize it back.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class ConsoleProxyPasswordBasedEncryptor {
|
public class ConsoleProxyPasswordBasedEncryptor {
|
||||||
private static final Logger s_logger = Logger.getLogger(ConsoleProxyPasswordBasedEncryptor.class);
|
private static final Logger s_logger = Logger.getLogger(ConsoleProxyPasswordBasedEncryptor.class);
|
||||||
|
|
||||||
private String password;
|
|
||||||
private Gson gson;
|
private Gson gson;
|
||||||
|
|
||||||
|
// key/IV will be set in 128 bit strength
|
||||||
|
private KeyIVPair keyIvPair;
|
||||||
|
|
||||||
public ConsoleProxyPasswordBasedEncryptor(String password) {
|
public ConsoleProxyPasswordBasedEncryptor(String password) {
|
||||||
this.password = password;
|
|
||||||
gson = new GsonBuilder().create();
|
gson = new GsonBuilder().create();
|
||||||
|
keyIvPair = gson.fromJson(password, KeyIVPair.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String encryptText(String text) {
|
public String encryptText(String text) {
|
||||||
if(text == null || text.isEmpty())
|
if(text == null || text.isEmpty())
|
||||||
return text;
|
return text;
|
||||||
|
|
||||||
assert(password != null);
|
|
||||||
assert(!password.isEmpty());
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Cipher cipher = Cipher.getInstance("DES");
|
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||||
int maxKeySize = 8;
|
SecretKeySpec keySpec = new SecretKeySpec(keyIvPair.getKeyBytes(), "AES");
|
||||||
SecretKeySpec keySpec = new SecretKeySpec(normalizeKey(password.getBytes(), maxKeySize), "DES");
|
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
|
cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(keyIvPair.getIvBytes()));
|
||||||
|
|
||||||
byte[] encryptedBytes = cipher.doFinal(text.getBytes());
|
byte[] encryptedBytes = cipher.doFinal(text.getBytes());
|
||||||
return Base64.encodeBase64URLSafeString(encryptedBytes);
|
return Base64.encodeBase64URLSafeString(encryptedBytes);
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
@ -77,6 +81,9 @@ public class ConsoleProxyPasswordBasedEncryptor {
|
|||||||
} catch (InvalidKeyException e) {
|
} catch (InvalidKeyException e) {
|
||||||
s_logger.error("Unexpected exception ", e);
|
s_logger.error("Unexpected exception ", e);
|
||||||
return null;
|
return null;
|
||||||
|
} catch (InvalidAlgorithmParameterException e) {
|
||||||
|
s_logger.error("Unexpected exception ", e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,14 +91,10 @@ public class ConsoleProxyPasswordBasedEncryptor {
|
|||||||
if(encryptedText == null || encryptedText.isEmpty())
|
if(encryptedText == null || encryptedText.isEmpty())
|
||||||
return encryptedText;
|
return encryptedText;
|
||||||
|
|
||||||
assert(password != null);
|
|
||||||
assert(!password.isEmpty());
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Cipher cipher = Cipher.getInstance("DES");
|
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||||
int maxKeySize = 8;
|
SecretKeySpec keySpec = new SecretKeySpec(keyIvPair.getKeyBytes(), "AES");
|
||||||
SecretKeySpec keySpec = new SecretKeySpec(normalizeKey(password.getBytes(), maxKeySize), "DES");
|
cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(keyIvPair.getIvBytes()));
|
||||||
cipher.init(Cipher.DECRYPT_MODE, keySpec);
|
|
||||||
|
|
||||||
byte[] encryptedBytes = Base64.decodeBase64(encryptedText);
|
byte[] encryptedBytes = Base64.decodeBase64(encryptedText);
|
||||||
return new String(cipher.doFinal(encryptedBytes));
|
return new String(cipher.doFinal(encryptedBytes));
|
||||||
@ -110,6 +113,9 @@ public class ConsoleProxyPasswordBasedEncryptor {
|
|||||||
} catch (InvalidKeyException e) {
|
} catch (InvalidKeyException e) {
|
||||||
s_logger.error("Unexpected exception ", e);
|
s_logger.error("Unexpected exception ", e);
|
||||||
return null;
|
return null;
|
||||||
|
} catch (InvalidAlgorithmParameterException e) {
|
||||||
|
s_logger.error("Unexpected exception ", e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,13 +136,62 @@ public class ConsoleProxyPasswordBasedEncryptor {
|
|||||||
return (T)gson.fromJson(json, clz);
|
return (T)gson.fromJson(json, clz);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] normalizeKey(byte[] keyBytes, int keySize) {
|
public static class KeyIVPair {
|
||||||
assert(keySize > 0);
|
String base64EncodedKeyBytes;
|
||||||
byte[] key = new byte[keySize];
|
String base64EncodedIvBytes;
|
||||||
|
|
||||||
for(int i = 0; i < keyBytes.length; i++)
|
public KeyIVPair() {
|
||||||
key[i%keySize] ^= keyBytes[i];
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user