Una forma sencilla de hacerlo es utilizar el cifrado basado en contraseña en Java. Esto le permite cifrar y descifrar un texto utilizando una contraseña.
Esto básicamente significa inicializar un javax.crypto.Cipher
algoritmo con "AES/CBC/PKCS5Padding"
y obtener una clave javax.crypto.SecretKeyFactory
con el "PBKDF2WithHmacSHA512"
algoritmo.
Aquí hay un ejemplo de código (actualizado para reemplazar la variante menos segura basada en MD5):
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
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.SecretKeySpec;
public class ProtectedConfigFile {
public static void main(String[] args) throws Exception {
String password = System.getProperty("password");
if (password == null) {
throw new IllegalArgumentException("Run with -Dpassword=<password>");
}
// The salt (probably) can be stored along with the encrypted data
byte[] salt = new String("12345678").getBytes();
// Decreasing this speeds down startup time and can be useful during testing, but it also makes it easier for brute force attackers
int iterationCount = 40000;
// Other values give me java.security.InvalidKeyException: Illegal key size or default parameters
int keyLength = 128;
SecretKeySpec key = createSecretKey(password.toCharArray(),
salt, iterationCount, keyLength);
String originalPassword = "secret";
System.out.println("Original password: " + originalPassword);
String encryptedPassword = encrypt(originalPassword, key);
System.out.println("Encrypted password: " + encryptedPassword);
String decryptedPassword = decrypt(encryptedPassword, key);
System.out.println("Decrypted password: " + decryptedPassword);
}
private static SecretKeySpec createSecretKey(char[] password, byte[] salt, int iterationCount, int keyLength) throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterationCount, keyLength);
SecretKey keyTmp = keyFactory.generateSecret(keySpec);
return new SecretKeySpec(keyTmp.getEncoded(), "AES");
}
private static String encrypt(String property, SecretKeySpec key) throws GeneralSecurityException, UnsupportedEncodingException {
Cipher pbeCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
pbeCipher.init(Cipher.ENCRYPT_MODE, key);
AlgorithmParameters parameters = pbeCipher.getParameters();
IvParameterSpec ivParameterSpec = parameters.getParameterSpec(IvParameterSpec.class);
byte[] cryptoText = pbeCipher.doFinal(property.getBytes("UTF-8"));
byte[] iv = ivParameterSpec.getIV();
return base64Encode(iv) + ":" + base64Encode(cryptoText);
}
private static String base64Encode(byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
}
private static String decrypt(String string, SecretKeySpec key) throws GeneralSecurityException, IOException {
String iv = string.split(":")[0];
String property = string.split(":")[1];
Cipher pbeCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
pbeCipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(base64Decode(iv)));
return new String(pbeCipher.doFinal(base64Decode(property)), "UTF-8");
}
private static byte[] base64Decode(String property) throws IOException {
return Base64.getDecoder().decode(property);
}
}
Queda un problema: ¿dónde debe almacenar la contraseña que utiliza para cifrar las contraseñas? Puede almacenarlo en el archivo fuente y ofuscarlo, pero no es demasiado difícil volver a encontrarlo. Alternativamente, puede asignarlo como una propiedad del sistema cuando inicia el proceso Java ( -DpropertyProtectionPassword=...
).
El mismo problema persiste si usa KeyStore, que también está protegido por una contraseña. Básicamente, necesitará tener una contraseña maestra en algún lugar, y es bastante difícil de proteger.
Sí, definitivamente no escriba su propio algoritmo. Java tiene muchas API de criptografía.
Si el sistema operativo en el que está instalando tiene un almacén de claves, puede usarlo para almacenar sus claves de cifrado que necesitará para cifrar y descifrar los datos confidenciales en su configuración u otros archivos.
fuente
Echa un vistazo a jasypt , que es una biblioteca que ofrece capacidades de cifrado básicas con un mínimo esfuerzo.
fuente
Creo que el mejor enfoque es garantizar que su archivo de configuración (que contiene su contraseña) solo sea accesible para una cuenta de usuario específica . Por ejemplo, es posible que tenga un usuario específico de la aplicación
appuser
para el que solo las personas de confianza tienen la contraseña (y para la cualsu
deben hacerlo).De esa manera, no hay una sobrecarga de criptografía molesta y todavía tiene una contraseña segura.
EDITAR: supongo que no está exportando la configuración de su aplicación fuera de un entorno confiable (lo que no estoy seguro tendría sentido, dada la pregunta)
fuente
Bueno, para resolver los problemas de la contraseña maestra: el mejor enfoque es no almacenar la contraseña en ningún lugar, la aplicación debe cifrar las contraseñas por sí misma, de modo que solo pueda descifrarlas. Entonces, si estaba usando un archivo .config, haría lo siguiente, mySettings.config :
así que leería las claves que se mencionan en encryptTheseKeys, aplicaría el ejemplo de Brodwalls de arriba y las escribiría en el archivo con un marcador de algún tipo (digamos crypt :) para que la aplicación sepa que no debe hacerlo de nuevo, la salida se vería así:
Solo asegúrese de guardar los originales en su propio lugar seguro ...
fuente
El gran punto, y el elefante en la habitación y todo eso, es que si su aplicación puede obtener la contraseña, ¡un pirata informático con acceso a la caja también puede obtenerla!
La única forma de evitar esto es que la aplicación solicita la "contraseña maestra" en la consola usando la entrada estándar, y luego la usa para descifrar las contraseñas almacenadas en el archivo. Por supuesto, esto hace completamente imposible que la aplicación se inicie desatendida junto con el sistema operativo cuando se inicia.
Sin embargo, incluso con este nivel de molestia, si un hacker logra obtener acceso de root (o incluso solo el acceso como el usuario que ejecuta su aplicación), podría volcar la memoria y encontrar la contraseña allí.
Lo que hay que garantizar es no permitir que toda la empresa tenga acceso al servidor de producción (y, por lo tanto, a las contraseñas), ¡y asegurarse de que sea imposible descifrar esta casilla!
fuente
Intente utilizar los métodos de cifrado de ESAPI. Es fácil de configurar y también puede cambiar fácilmente sus claves.
http://owasp-esapi-java.googlecode.com/svn/trunk_doc/latest/org/owasp/esapi/Encryptor.html
Tú
1) cifrar 2) descifrar 3) firmar 4) cancelar firma 5) hashing 6) firmas basadas en el tiempo y mucho más con solo una biblioteca.
fuente
Vea lo que está disponible en Jetty para almacenar la contraseña (o hashes) en los archivos de configuración, y considere si la codificación OBF podría ser útil para usted. Luego vea en la fuente cómo se hace.
http://www.eclipse.org/jetty/documentation/current/configuring-security-secure-passwords.html
fuente
Dependiendo de cuán seguro necesite los archivos de configuración o cuán confiable sea su aplicación, http://activemq.apache.org/encrypted-passwords.html puede ser una buena solución para usted.
Si no tiene demasiado miedo de que se descifre la contraseña y puede ser realmente simple de configurar usando un bean para almacenar la clave de contraseña. Sin embargo, si necesita más seguridad, puede establecer una variable de entorno con el secreto y eliminarla después del inicio. Con esto, debe preocuparse de que la aplicación / servidor se caiga y no que la aplicación no se reinicie automáticamente.
fuente
Si está utilizando Java 8, se puede evitar el uso del codificador y decodificador interno Base64 reemplazando
return new BASE64Encoder().encode(bytes);
con
return Base64.getEncoder().encodeToString(bytes);
y
return new BASE64Decoder().decodeBuffer(property);
con
return Base64.getDecoder().decode(property);
Tenga en cuenta que esta solución no protege sus datos, ya que los métodos para descifrar se almacenan en el mismo lugar. Simplemente hace que sea más difícil de romper. Principalmente evita imprimirlo y mostrárselo a todos por error.
fuente