¿Cifrar contraseña en archivos de configuración?

Tengo un progtwig que lee información del servidor de un archivo de configuración y me gustaría cifrar la contraseña en esa configuración que mi progtwig puede leer y descifrar.

Requirimientos:

  • Encriptar contraseña de texto plano para que se almacene en el archivo
  • Descifrar la contraseña encriptada desde el archivo de mi progtwig

¿Alguna recomendación sobre cómo voy a hacer esto? Estaba pensando en escribir mi propio algoritmo, pero creo que sería terriblemente inseguro.

Una forma simple de hacerlo es usar cifrado basado en contraseña en Java. Esto le permite cifrar y descifrar un texto usando una contraseña.

Esto básicamente significa inicializar un javax.crypto.Cipher con el algoritmo "AES/CBC/PKCS5Padding" y obtener una clave de javax.crypto.SecretKeyFactory con el algoritmo "PBKDF2WithHmacSHA512" .

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="); } // 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 guardar la contraseña que usa para encriptar las contraseñas? Puede almacenarlo en el archivo de origen y ofuscarlo, pero no es demasiado difícil encontrarlo de nuevo. Alternativamente, puede darle como propiedad del sistema cuando inicie el proceso de Java ( -DpropertyProtectionPassword=... ).

El mismo problema persiste si usa KeyStore, que también está protegido por una contraseña. Básicamente, necesitarás tener una contraseña maestra en alguna parte, y es bastante difícil de proteger.

Sí, definitivamente no escribas tu 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 criptográficas que necesitará cifrar y descifrar los datos confidenciales en su configuración u otros archivos.

Consulte jasypt , que es una biblioteca que ofrece capacidades de cifrado básicas con el mínimo esfuerzo.

Creo que el mejor enfoque es asegurarse de 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 de la aplicación usuario específico a la que solo las personas de confianza tengan la contraseña (y a la que su ).

De esta forma, no hay una sobrecarga de criptografía molesta y aún tienes una contraseña segura.

EDITAR: supongo que no está exportando la configuración de la aplicación fuera de un entorno confiable (que no estoy seguro de que tenga sentido, dada la pregunta)

Bueno, para resolver los problemas de la contraseña maestra, el mejor enfoque es no almacenar la contraseña en ninguna parte, la aplicación debe encriptar las contraseñas por sí misma, para que solo pueda descifrarlas. Entonces, si estuviera usando un archivo .config, haría lo siguiente, mySettings.config :

encryptTheseKeys = secretKey, anotherSecret

secretKey = desprotectedPasswordThatIputHere

anotherSecret = anotherPass

someKey = desprotegidoSettingIdontCareAbout

así que leería las claves que se mencionan en el encryptTheseKeys, aplicaré el ejemplo de Brodwalls de arriba y las escribiré de nuevo 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í:

encryptTheseKeys = secretKey, anotherSecret

secretKey = crypt: ii4jfj304fjhfj934fouh938

anotherSecret = crypt: jd48jofh48h

someKey = desprotegidoSettingIdontCareAbout

Solo asegúrate de guardar los originales en tu propio lugar seguro …

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 hacker con acceso a la caja también puede hacerse con ella!

La única forma de evitar esto, es que la aplicación solicita la “contraseña maestra” en la consola mediante la entrada estándar, y luego utiliza esto 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 pirata informático logra obtener acceso de administrador (o incluso solo accede como el usuario que ejecuta su aplicación), podría volcar la memoria y encontrar allí la contraseña.

Lo que hay que asegurarse es no permitir que toda la empresa tenga acceso al servidor de producción (y, por lo tanto, a las contraseñas), ¡y asegúrese de que sea imposible abrir esta caja!

Intente utilizar los métodos de encriptación de ESAPI. Es fácil de configurar y también puedes cambiar tus llaves fácilmente.

http://owasp-esapi-java.googlecode.com/svn/trunk_doc/latest/org/owasp/esapi/Encryptor.html

1) encriptar 2) desencriptar 3) firmar 4) borrar 5) mezclar 6) firmas basadas en el tiempo y mucho más con una sola biblioteca.

Vea lo que está disponible en Jetty para almacenar la contraseña (o hash) en archivos de configuración, y considere si la encoding OBF puede 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

Dependiendo de qué tan seguro necesita los archivos de configuración o qué tan confiable es su aplicación, http://activemq.apache.org/encrypted-passwords.html puede ser una buena solución para usted.

Si no tiene miedo de que se descifre la contraseña, puede ser muy fácil de configurar con un bean para almacenar la clave de la 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 tienes que preocuparte de que la aplicación / servidor se caiga y no que la aplicación no se reinicie automáticamente.

Si está utilizando java 8, se puede evitar el uso del codificador y decodificador Base64 interno mediante el reemplazo

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 descifrarlos se almacenan en el mismo lugar. Simplemente hace que sea más difícil de romper. Principalmente evita imprimirlo y mostrárselo a todo el mundo por error.