2010-09-22 56 views
12

Estoy buscando una forma de encriptar una contraseña en un archivo de configuración que está siendo leído por un programa Java. Actualmente, leo en la contraseña desde el archivo de texto, pero deja abierta la contraseña al a la vista si alguien mira el archivo de configuración.cifrar y descifrar el valor del archivo de propiedad en java

Estaba pensando en crear una clase simple donde el usuario pudiera escribir la contraseña deseada, obtener una versión encriptada de la contraseña, luego pegar la versión encriptada en el archivo de texto de configuración. Luego, la aplicación leerá la contraseña cifrada, descifrará la contraseña en una cadena y continuará.

Tengo problemas con la cadena -> bytes cifrados -> cadena conversiones.

Estoy utilizando las clases de seguridad incorporadas de Java para implementar este código. Aquí hay un código de prueba de ejemplo:

// Reads password from config file 
String password = ScriptConfig.getString("password"); 

// Generate Key 
KeyGenerator kg = KeyGenerator.getInstance("DES"); 
Key key = kg.generateKey(); 

// Create Encryption cipher 
Cipher cipher = Cipher.getInstance("DES"); 
cipher.init(Cipher.ENCRYPT_MODE, key); 

// Encrypt password 
byte[] encrypted = cipher.doFinal(password.getBytes()); 

// Create decryption cipher 
cipher.init(Cipher.DECRYPT_MODE, key); 
byte[] decrypted = cipher.doFinal(encrypted); 

// Convert byte[] to String 
String decryptedString = new String(decrypted); 

System.out.println("password: " + password); 
System.out.println("encrypted: " + encrypted); 
System.out.println("decrypted: " + decryptedString); 

// Read encrypted string from config file 
String encryptedPassword = ScriptConfig.getString("encryptedPassword" 
); 

// Convert encryptedPassword string into byte[] 
byte[] encryptedPasswordBytes = new byte[1024]; 
encryptedPasswordBytes = encryptedPassword.getBytes(); 

// Decrypt encrypted password from config file 
byte[] decryptedPassword = cipher.doFinal(encryptedPasswordBytes);//error here 

System.out.println("encryptedPassword: " + encryptedPassword); 
System.out.println("decryptedPassword: " + decryptedPassword); 


The config file has the following variables: 
password=password 
encryptedPassword=[[email protected] 


When I run the code, I get the following output: 
password: passwd 
encrypted: [[email protected] 
decrypted: passwd 
javax.crypto.IllegalBlockSizeException: Input length must be multiple 
of 8 when decrypting with padded cipher 
at com.sun.crypto.provider.SunJCE_h.b(DashoA12275) 
at com.sun.crypto.provider.SunJCE_h.b(DashoA12275) 
at com.sun.crypto.provider.DESCipher.engineDoFinal(Da shoA12275) 
at javax.crypto.Cipher.doFinal(DashoA12275) 
at com.sapient.fbi.uid.TestEncryption.main(TestEncryp tion.java:4 

Cualquier ayuda en el error, estructura o proceso que estoy utilizando para ello sería grande. Gracias.

+0

Si su aplicación está en la máquina del cliente, se puede descifrar y descubrir la clave de descifrado. Una vez que eso sucede, también puede estar utilizando texto plano. La única manera de asegurar realmente ese flujo de trabajo es hacer que la descifrado tenga lugar en un servidor seguro. ¿Qué problema estás tratando de resolver que te llevó a probar esto? (¿Por qué necesita almacenar una contraseña como esa?) – Daenyth

+0

No quise almacenar la contraseña en el archivo de configuración como texto sin formato. – user234194

+0

@ user234194: Te estás perdiendo el punto. Si almacena la clave de descifrado en el cliente, no es segura y cualquiera puede descifrarla con el cliente. – Daenyth

Respuesta

-7

Una solución muy simple sería utilizar la codificación Base64, ver el siguiente fragmento de código: -

import sun.misc.BASE64Decoder; 
import sun.misc.BASE64Encoder; 

... 

private String encode(String str) { 
    BASE64Encoder encoder = new BASE64Encoder(); 
    str = new String(encoder.encodeBuffer(str.getBytes())); 
    return str; 
} 

private String decode(String str) { 
    BASE64Decoder decoder = new BASE64Decoder(); 
    try { 
     str = new String(decoder.decodeBuffer(str)); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    }  
    return str; 
} 

... 
+1

el uso de clases de sun.misc hace que esto no sea portátil, aunque – Waldheinz

+0

¿por qué no es portátil? y de qué manera? –

+12

usando la codificación base64 evitaría que la propiedad se revelara a simple vista, pero una vez que alguien agarra el hash, ¿no podrían ejecutarlo a través de un método de base64 inverso? – Haoest

2

Tengo problemas con la cadena -> bytes cifrados -> conversiones de cadenas.

me gustaría Cosas de la matriz de bytes a través de una base 64 es/decodificador, así que tendrá que persisten las cadenas que contienen nada más que caracteres dentro de (un subconjunto de) ASCII, lo que debería limitar su problema. Eche un vistazo, por ej. en commons codecs y reemplace su new String(decrypted) con una llamada a uno de los métodos estáticos en la clase org.apache.commons.codec.binary.Base64.

Además de eso, creo que lo que en última instancia quiero hacer no es estrictamente "cifrar" la contraseña, sino almacenar solo un hash de la contraseña, que ya se discutió en SO.

11

Tome un vistazo a Jasypt. Ya ha hecho el trabajo pesado por ti. Específicamente, las clases org.jasypt.encryption.pbe.StandardPBEStringEncryptor y org.jasypt.properties.PropertyValueEncryptionUtils.

Crear un encriptador:

SimplePBEConfig config = new SimplePBEConfig(); 
config.setAlgorithm("PBEWithMD5AndTripleDES"); 
config.setKeyObtentionIterations(1000); 
config.setPassword("propertiesFilePassword"); 

StandardPBEStringEncryptor encryptor = new org.jasypt.encryption.pbe.StandardPBEStringEncryptor(); 
encryptor.setConfig(config); 
encryptor.initialize(); 

A continuación, utilice PropertyValueEncryptionUtils para cifrar los valores/descifrar:

PropertyValueEncryptionUtils.encrypt(value, encryptor); 
PropertyValueEncryptionUtils.decrypt(encodedValue, encryptor) 

Nota el valor codificado se iniciará con ENC( y terminar con ), por lo que es fácil saber si una propiedad de un archivo está encriptada.

Además, tenga en cuenta que la contraseña utilizada para config.setPassword() es no la contraseña que está codificando para almacenar en el archivo de propiedades. En cambio, es la contraseña para cifrar/descifrar el valor que está almacenando. Lo que es esta contraseña y cómo configurarla depende de usted.De manera predeterminada, el nombre de clase completo de todo lo que está leyendo el archivo de propiedades.

Finalmente, si está utilizando Spring, Jasypt tiene una clase EncryptablePropertyPlaceholderConfigurer que puede usar para cargar el archivo de propiedades y usar la sintaxis ${foo} en sus archivos Spring XML para realizar la sustitución de variables como contraseñas de base de datos.

3

Éstos son algunos ayudantes para cifrar o descifrar usando un algoritmo AES en Java:

public static final String AES = "AES"; 

/** 
* Encrypt a value and generate a keyfile. 
* If the keyfile is not found, then a new one will be created. 
* 
* @throws GeneralSecurityException 
* @throws IOException if an I/O error occurs 
*/ 
public static String encrypt(String value, File keyFile) 
     throws GeneralSecurityException, IOException { 
    if (!keyFile.exists()) { 
     KeyGenerator keyGen = KeyGenerator.getInstance(CryptoUtils.AES); 
     keyGen.init(128); 
     SecretKey sk = keyGen.generateKey(); 
     FileWriter fw = new FileWriter(keyFile); 
     fw.write(byteArrayToHexString(sk.getEncoded())); 
     fw.flush(); 
     fw.close(); 
    } 

    SecretKeySpec sks = getSecretKeySpec(keyFile); 
    Cipher cipher = Cipher.getInstance(CryptoUtils.AES); 
    cipher.init(Cipher.ENCRYPT_MODE, sks, cipher.getParameters()); 
    byte[] encrypted = cipher.doFinal(value.getBytes()); 
    return byteArrayToHexString(encrypted); 
} 

/** 
* Decrypt a value. 
* 
* @throws GeneralSecurityException 
* @throws IOException if an I/O error occurs 
*/ 
public static String decrypt(String message, File keyFile) 
     throws GeneralSecurityException, IOException { 
    SecretKeySpec sks = getSecretKeySpec(keyFile); 
    Cipher cipher = Cipher.getInstance(CryptoUtils.AES); 
    cipher.init(Cipher.DECRYPT_MODE, sks); 
    byte[] decrypted = cipher.doFinal(hexStringToByteArray(message)); 
    return new String(decrypted); 
} 

private static SecretKeySpec getSecretKeySpec(File keyFile) 
     throws NoSuchAlgorithmException, IOException { 
    byte[] key = readKeyFile(keyFile); 
    SecretKeySpec sks = new SecretKeySpec(key, CryptoUtils.AES); 
    return sks; 
} 

private static byte[] readKeyFile(File keyFile) 
     throws FileNotFoundException { 
    Scanner scanner = new Scanner(keyFile).useDelimiter("\\Z"); 
    String keyValue = scanner.next(); 
    scanner.close(); 
    return hexStringToByteArray(keyValue); 
} 

private static String byteArrayToHexString(byte[] b) { 
    StringBuffer sb = new StringBuffer(b.length * 2); 
    for (int i = 0; i < b.length; i++) { 
     int v = b[i] & 0xff; 
     if (v < 16) { 
      sb.append('0'); 
     } 
     sb.append(Integer.toHexString(v)); 
    } 
    return sb.toString().toUpperCase(); 
} 

private static byte[] hexStringToByteArray(String s) { 
    byte[] b = new byte[s.length()/2]; 
    for (int i = 0; i < b.length; i++) { 
     int index = i * 2; 
     int v = Integer.parseInt(s.substring(index, index + 2), 16); 
     b[i] = (byte) v; 
    } 
    return b; 
} 

Sólo tiene que llamar el método apropiado.

+0

La fuente completa está en code.google.com [aquí] (https://code.google.com/a/eclipselabs.org/p/android-openid/source/browse/trunk/CryptoUtils/src/CryptoUtils.java? r = 29). – 32U

Cuestiones relacionadas