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.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import javax.crypto.spec.SecretKeySpec; import er.extensions.foundation.ERXProperties; /** * <p> * ERXKeyStoreBlowfishCrypter is a blowfish implementation of the crypter * interface that loads its secret key from a java keystore. * </p> * <p> * Because the java keystore API is so damn ridiculous, you can use the main * method provided in this class to generate the keystore with your blowfish * password in it. * </p> * <p> * If you want to just use all of the default values, you can run this class's * main method from inside eclipse with the arguments: * </p> * * <code> * "" "" "" "" "yourblowfishpassword" * </code> * * <p> * The easiest way to choose to use this crypter is to override the default * blowfish crypter with the Properties entries: * </p> * * <code> * er.extensions.ERXCrypto.crypters=Blowfish * er.extensions.ERXCrypto.crypter.Blowfish=er.extensions.ERXKeyStoreBlowfishCrypter * </code> * * @author mschrag * @property er.extensions.ERXKeyStoreBlowfishCrypter.keystorePath the path of * the keystore that contains the blowfish key for this crypter. The * default keystore path is * "~/.er.extensions.ERXKeyStoreBlowfishCrypter.keystore" * @property er.extensions.ERXKeyStoreBlowfishCrypter.keystorePassword the * keystore password (if necessary) * @property er.extensions.ERXKeyStoreBlowfishCrypter.keyAlias the alias of the * blowfish key in the keystore * @property er.extensions.ERXKeyStoreBlowfishCrypter.keyPassword the password * of the blowfish key in the keystore (if necessary) */ public class ERXKeyStoreBlowfishCrypter extends ERXAbstractBlowfishCrypter { private static final String KEYSTORE_TYPE = "JCEKS"; private static final String DEFAULT_KEY_ALIAS = "er.extensions.ERXBlowfishCipherKey"; private static final String DEFAULT_KEY_PASSWORD = "er.extensions.ERXBlowfishCipherKey"; private static final String DEFAULT_KEYSTORE_PASSWORD = "er.extensions.ERXBlowfishCipherKey"; private static final String defaultKeyStorePath() { return new File(System.getProperty("user.home"), ".er.extensions.ERXKeyStoreBlowfishCrypter.keystore").getAbsolutePath(); } @Override protected Key secretBlowfishKey() throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException { String keystorePath = ERXProperties.stringForKeyWithDefault("er.extensions.ERXKeyStoreBlowfishCrypter.keystorePath", ERXKeyStoreBlowfishCrypter.defaultKeyStorePath()); File keystoreFile = new File(keystorePath); KeyStore keyStore = KeyStore.getInstance(ERXKeyStoreBlowfishCrypter.KEYSTORE_TYPE); if (!keystoreFile.exists()) { throw new FileNotFoundException("There is no keystore '" + keystoreFile.getAbsolutePath() + "'. Run ERXKeyStoreBlowfishCrypter's main method for help. Oh, and to hell with whoever wrote Java's KeyStore API."); } String keystorePasswordStr = ERXProperties.stringForKeyWithDefault("er.extensions.ERXKeyStoreBlowfishCrypter.keystorePassword", ERXKeyStoreBlowfishCrypter.DEFAULT_KEYSTORE_PASSWORD); char[] keystorePassword = null; if (keystorePasswordStr != null) { keystorePassword = keystorePasswordStr.toCharArray(); } try (FileInputStream keystoreInputStream = new FileInputStream(keystoreFile)) { keyStore.load(keystoreInputStream, keystorePassword); } String alias = ERXProperties.stringForKeyWithDefault("er.extensions.ERXKeyStoreBlowfishCrypter.keyAlias", ERXKeyStoreBlowfishCrypter.DEFAULT_KEY_ALIAS); // We can't use ERXProperties.decryptedStringForKey because it would // potentially result in an infinite look if this is the default // crypter. String keyPasswordStr = ERXProperties.stringForKeyWithDefault("er.extensions.ERXKeyStoreBlowfishCrypter.keyPassword", ERXKeyStoreBlowfishCrypter.DEFAULT_KEY_PASSWORD); char[] keyPassword = keyPasswordStr.toCharArray(); Key key = keyStore.getKey(alias, keyPassword); return key; } public static void main(String[] args) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, CertificateException, FileNotFoundException, IOException { if (args.length != 5) { System.out.println("ERXKeyStoreBlowfishCrypter.main: [keystorePath] [keystorePassword] [keyAlias] [keyPassword] [blowfishKey]"); System.out.println(" Note: you can set any of the parameters to \"\" to use the default values except for blowfishKey."); System.exit(0); } String keystorePath = ERXKeyStoreBlowfishCrypter.defaultKeyStorePath(); if (args[0].length() > 0) { keystorePath = args[0]; } String keystorePasswordStr = ERXKeyStoreBlowfishCrypter.DEFAULT_KEYSTORE_PASSWORD; if (args[1].length() > 0) { keystorePasswordStr = args[1]; } char[] keystorePassword = keystorePasswordStr.toCharArray(); String keyAlias = ERXKeyStoreBlowfishCrypter.DEFAULT_KEY_ALIAS; if (args[2].length() > 0) { keyAlias = args[2]; } String keyPasswordStr = ERXKeyStoreBlowfishCrypter.DEFAULT_KEY_PASSWORD; if (args[3].length() > 0) { keyPasswordStr = args[3]; } char[] keyPassword = keyPasswordStr.toCharArray(); String blowfishKey = args[4]; KeyStore keyStore = KeyStore.getInstance(ERXKeyStoreBlowfishCrypter.KEYSTORE_TYPE); File keystoreFile = new File(keystorePath); if (keystoreFile.exists()) { try (FileInputStream keystoreInputStream = new FileInputStream(keystoreFile)) { keyStore.load(keystoreInputStream, keystorePassword); } } else { keyStore.load(null, keystorePassword); } SecretKeySpec key = new SecretKeySpec(blowfishKey.getBytes(), "Blowfish"); keyStore.setKeyEntry(keyAlias, key, keyPassword, null); try (FileOutputStream keystoreOutputStream = new FileOutputStream(keystoreFile)) { keyStore.store(keystoreOutputStream, keystorePassword); } System.out.println("ERXKeyStoreBlowfishCrypter.main: Generated the keystore '" + keystorePath + "'."); } }