package er.extensions.crypting;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.Key;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import org.apache.commons.lang3.CharEncoding;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webobjects.foundation.NSForwardException;
import er.extensions.foundation.ERXCompressionUtilities;
import er.extensions.foundation.ERXFileUtilities;
/**
* ERXDESCrypto is a DES implementation of the crypter interface.
*
* @author mschrag
*/
public class ERXDESCrypter implements ERXCrypterInterface {
private static final Logger log = LoggerFactory.getLogger(ERXCrypto.class);
private Key _secretDESKey;
private String _secretKeyPathFramework;
private String _secretKeyPath;
public ERXDESCrypter() {
}
public ERXDESCrypter(String secretKeyPathFramework, String secretKeyPath) {
_secretKeyPathFramework = secretKeyPathFramework;
_secretKeyPath = secretKeyPath;
}
public void setSecretKeyPathFramework(String secretKeyPathFramework) {
_secretKeyPathFramework = secretKeyPathFramework;
}
public void setSecretKeyPath(String secretKeyPath) {
_secretKeyPath = secretKeyPath;
}
/**
* Returns the DES java.security.Key found in the key file. The Key is
* cached once it's found so further hits to the disk are unnecessary. If
* the key file cannot be found, the method creates a key and writes out a
* key file.
*/
protected Key defaultSecretKey() {
if (_secretDESKey == null) {
InputStream is = null;
if (_secretKeyPath != null) {
try {
is = new FileInputStream(new File(_secretKeyPath));
}
catch (FileNotFoundException e) {
log.warn("Couldn't recover Secret key file, generating new");
try {
KeyGenerator gen = KeyGenerator.getInstance("DES");
gen.init(new SecureRandom());
_secretDESKey = gen.generateKey();
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(new File(_secretKeyPath)))) {
out.writeObject(_secretDESKey);
}
is = new FileInputStream(new File(_secretKeyPath));
}
catch (java.security.NoSuchAlgorithmException ex) {
throw new NSForwardException(ex, "Couldn't find the DES algorithm; perhaps you do not have the SunJCE security provider installed properly?");
}
catch (Exception ex) {
throw NSForwardException._runtimeExceptionForThrowable(ex);
}
}
}
else {
String fn = "SecretKey.ser";
is = ERXFileUtilities.inputStreamForResourceNamed(fn, _secretKeyPathFramework, null);
}
if (is != null) {
log.debug("About to try to recover key");
try (ObjectInputStream in = new ObjectInputStream(is)) {
_secretDESKey = (Key) in.readObject();
}
catch (Exception e) {
throw NSForwardException._runtimeExceptionForThrowable(e);
}
}
else {
throw new RuntimeException("No secret key found. You should add a 'SecretKey.ser' file into your app's resources or use setSecretKeyPath(String aPath)");
}
try {
is.close();
}
catch (IOException e) {
// ignore
}
}
return _secretDESKey;
}
/**
* Base64 decodes and then DES decrypts the passed in string using the
* secret key returned by <code>secretKey</code>.
*/
public String decrypt(String cryptedText) {
return decrypt(cryptedText, defaultSecretKey());
}
/**
* Base64 decodes and then DES decrypts the passed in string using the
* passed in secret key.
*/
public String decrypt(String cryptedText, Key secretKey) {
if (cryptedText == null) {
return cryptedText;
}
try {
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] raw = ERXCrypto.base64Decode(cryptedText);
byte[] stringBytes = cipher.doFinal(raw);
stringBytes = ERXCompressionUtilities.inflateByteArray(stringBytes);
String decString = new String(stringBytes, "UTF8");
return decString;
}
catch (java.security.NoSuchAlgorithmException ex) {
throw new NSForwardException(ex, "Couldn't find the DES algorithm; perhaps you do not have the SunJCE security provider installed properly?");
}
catch (Exception ex) {
throw new NSForwardException(ex);
}
}
/**
* DES Encrypts and then base64 encodes the passed in String using the
* secret key returned by <code>secretKey</code>. The base64 encoding is
* performed to ensure that the encrypted string can be stored in places
* that don't support extended character sets.
*/
public String encrypt(String clearText) {
return encrypt(clearText, defaultSecretKey());
}
/**
* DES Encrypts and then base64 encodes the passed in String using the
* passed in secret key. The base64 encoding is performed to ensure that the
* encrypted string can be stored in places that don't support extended
* character sets.
*/
public String encrypt(String clearText, Key secretKey) {
if (clearText == null) {
return clearText;
}
try {
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] stringBytes = clearText.getBytes(CharEncoding.UTF_8);
stringBytes = ERXCompressionUtilities.deflateByteArray(stringBytes);
byte[] raw = cipher.doFinal(stringBytes);
String encBase64String = ERXCrypto.base64Encode(raw);
return encBase64String;
}
catch (java.security.NoSuchAlgorithmException ex) {
throw new NSForwardException(ex, "Couldn't find the DES algorithm; perhaps you do not have the SunJCE security provider installed properly?");
}
catch (Exception ex) {
throw new NSForwardException(ex);
}
}
}