/**
* Licensed to JumpMind Inc under one or more contributor
* license agreements. See the NOTICE file distributed
* with this work for additional information regarding
* copyright ownership. JumpMind Inc licenses this file
* to you under the GNU General Public License, version 3.0 (GPLv3)
* (the "License"); you may not use this file except in compliance
* with the License.
*
* You should have received a copy of the GNU General Public License,
* version 3.0 (GPLv3) along with this library; if not, see
* <http://www.gnu.org/licenses/>.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jumpmind.security;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.NotImplementedException;
import org.jumpmind.exception.IoException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @see ISecurityService
*/
public class SecurityService implements ISecurityService {
protected Logger log = LoggerFactory.getLogger(getClass());
protected SecretKey secretKey;
protected SecureRandom secRand;
protected SecurityService() {
}
public void init() {
}
public void installDefaultSslCert(String host) {
throw new NotImplementedException();
}
protected void checkThatKeystoreFileExists() {
String keyStoreLocation = System.getProperty(SecurityConstants.SYSPROP_KEYSTORE);
if (!new File(keyStoreLocation).exists()) {
throw new IoException(
"Could not find the keystore file. We expected it to exist here: "
+ keyStoreLocation);
}
}
public String encrypt(String plainText) {
try {
checkThatKeystoreFileExists();
byte[] bytes = plainText.getBytes(SecurityConstants.CHARSET);
byte[] enc = getCipher(Cipher.ENCRYPT_MODE).doFinal(bytes);
return new String(Base64.encodeBase64(enc), SecurityConstants.CHARSET);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String decrypt(String encText) {
try {
checkThatKeystoreFileExists();
byte[] dec = Base64.decodeBase64(encText.getBytes());
byte[] bytes = getCipher(Cipher.DECRYPT_MODE).doFinal(dec);
return new String(bytes, SecurityConstants.CHARSET);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected Cipher getCipher(int mode) throws Exception {
if (secretKey == null) {
secretKey = getSecretKey();
}
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
initializeCipher(cipher, mode);
log.debug("Using {} algorithm provided by {}.", cipher.getAlgorithm(), cipher.getProvider()
.getName());
return cipher;
}
protected void initializeCipher(Cipher cipher, int mode) throws Exception {
AlgorithmParameterSpec paramSpec = Cipher.getMaxAllowedParameterSpec(cipher.getAlgorithm());
if (paramSpec instanceof PBEParameterSpec
|| (paramSpec == null && cipher.getAlgorithm().startsWith("PBE"))) {
paramSpec = new PBEParameterSpec(SecurityConstants.SALT,
SecurityConstants.ITERATION_COUNT);
cipher.init(mode, secretKey, paramSpec);
} else if (paramSpec instanceof IvParameterSpec) {
paramSpec = new IvParameterSpec(SecurityConstants.SALT);
cipher.init(mode, secretKey, paramSpec);
} else {
cipher.init(mode, secretKey, (AlgorithmParameterSpec) null);
}
}
protected String getKeyStorePassword() {
String password = System.getProperty(SecurityConstants.SYSPROP_KEYSTORE_PASSWORD);
password = (password != null) ? password : SecurityConstants.KEYSTORE_PASSWORD;
return password;
}
protected SecretKey getSecretKey() throws Exception {
String password = getKeyStorePassword();
KeyStore.ProtectionParameter param = new KeyStore.PasswordProtection(password.toCharArray());
KeyStore ks = getKeyStore(password);
KeyStore.SecretKeyEntry entry = (KeyStore.SecretKeyEntry) ks.getEntry(
SecurityConstants.ALIAS_SYM_SECRET_KEY, param);
if (entry == null) {
log.debug("Generating secret key");
entry = new KeyStore.SecretKeyEntry(getDefaultSecretKey());
ks.setEntry(SecurityConstants.ALIAS_SYM_SECRET_KEY, entry, param);
saveKeyStore(ks, password);
} else {
log.debug("Retrieving secret key");
}
return entry.getSecretKey();
}
private SecureRandom getSecRan() {
if (secRand == null) {
secRand = new SecureRandom();
secRand.setSeed(System.currentTimeMillis());
}
return secRand;
}
public String nextSecureHexString(int len) {
if (len <= 0)
throw new IllegalArgumentException("length must be positive");
SecureRandom secRan = getSecRan();
MessageDigest alg = null;
try {
alg = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException ex) {
return null;
}
alg.reset();
int numIter = len / 40 + 1;
StringBuffer outBuffer = new StringBuffer();
for (int iter = 1; iter < numIter + 1; iter++) {
byte randomBytes[] = new byte[40];
secRan.nextBytes(randomBytes);
alg.update(randomBytes);
byte hash[] = alg.digest();
for (int i = 0; i < hash.length; i++) {
Integer c = new Integer(hash[i]);
String hex = Integer.toHexString(c.intValue() + 128);
if (hex.length() == 1)
hex = "0" + hex;
outBuffer.append(hex);
}
}
return outBuffer.toString().substring(0, len);
}
protected SecretKey getDefaultSecretKey() throws Exception {
String keyPassword = nextSecureHexString(8);
KeySpec keySpec = new PBEKeySpec(keyPassword.toCharArray(), SecurityConstants.SALT,
SecurityConstants.ITERATION_COUNT, 56);
SecretKey secretKey = SecretKeyFactory.getInstance(SecurityConstants.ALGORITHM)
.generateSecret(keySpec);
return secretKey;
}
protected KeyStore getKeyStore(String password) throws Exception {
String keyStoreType = System.getProperty(SecurityConstants.SYSPROP_KEYSTORE_TYPE,
SecurityConstants.KEYSTORE_TYPE);
KeyStore ks = KeyStore.getInstance(keyStoreType);
FileInputStream is = new FileInputStream(
System.getProperty(SecurityConstants.SYSPROP_KEYSTORE));
ks.load(is, password.toCharArray());
is.close();
return ks;
}
protected void saveKeyStore(KeyStore ks, String password) throws Exception {
FileOutputStream os = new FileOutputStream(
System.getProperty(SecurityConstants.SYSPROP_KEYSTORE));
ks.store(os, password.toCharArray());
os.close();
}
}