2010-09-22 9 views
12

Je cherche un moyen de crypter un mot de passe dans un fichier de configuration qui est lu par un programme Java. Actuellement, j'ai lu le mot de passe à partir du fichier texte, mais cela laisse le mot de passe assis à l'air libre si quelqu'un devait regarder le fichier de configuration.crypter et décrypter la valeur du fichier de propriétés dans java

Je pensais à construire une classe simple où l'utilisateur pouvait taper le mot de passe désiré, obtenir une version cryptée du mot de passe, puis coller la version cryptée dans le fichier texte de configuration. Ensuite, l'application lirait le mot de passe crypté, décrypterait le mot de passe dans une chaîne, et passer à autre chose.

J'ai des problèmes avec la chaîne -> encrytped octets -> chaîne conversions. J'utilise les classes de sécurité java intégrées pour implémenter ce code. Voici un code de test d'échantillon:

// 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 

Toute aide sur l'erreur, la structure ou un processus que je utilise pour ce faire serait génial. Merci.

+0

Si votre application se trouve sur l'ordinateur du client, elle peut être endommagée et la clé de déchiffrement découverte. Une fois que cela arrive, vous pouvez aussi bien utiliser le texte en clair. La seule façon de vraiment sécuriser ce flux de travail est d'avoir le décryptage sur un serveur sécurisé. Quel problème essayez-vous de résoudre qui vous a amené à essayer cela? (Pourquoi avez-vous besoin de stocker un mot de passe comme ça?) – Daenyth

+0

Je ne voulais pas stocker le mot de passe dans le fichier de configuration en texte brut. – user234194

+0

@ user234194: Vous manquez le point. Si vous stockez la clé de déchiffrement sur le client, elle n'est pas sécurisée et peut être déchiffrée par n'importe qui avec le client. – Daenyth

Répondre

-7

Une solution très simple serait d'utiliser le codage base64, voir l'extrait de code ci-dessous: -

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

l'utilisation des classes de sun.misc rend ce non-portable, bien que – Waldheinz

+0

pourquoi est-il non portable? et de quelle manière? –

+12

en utilisant l'encodage en base64 empêcherait la propriété d'être révélée à l'œil nu, mais une fois que quelqu'un obtient un hold-up du hachage, ne pourrait-il pas l'exécuter par une méthode base64 inverse? – Haoest

2

J'ai des problèmes avec la chaîne -> encrytped octets -> conversions de chaîne.

je aimerais farcir le tableau d'octets par un base64 en/décodeur, cette façon, vous devez persister des chaînes qui ne contiennent que les caractères à l'intérieur (un sous-ensemble) ASCII, ce qui devrait limiter votre problème. Regardez par exemple à commons codecs et remplacez votre new String(decrypted) par un appel à l'une des méthodes statiques dans la classe org.apache.commons.codec.binary.Base64. En outre, je pense que ce que vous voulez faire en fin de compte, ce n'est pas de "crypter" le mot de passe, mais plutôt de stocker uniquement un hachage du mot de passe, déjà discuté sur SO.

11

Jetez un oeil à Jasypt. Il a déjà fait le gros du travail pour vous. Plus précisément, les classes org.jasypt.encryption.pbe.StandardPBEStringEncryptor et org.jasypt.properties.PropertyValueEncryptionUtils.

Créer un chiffreur:

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(); 

Utilisez ensuite PropertyValueEncryptionUtils pour crypter/décrypter des valeurs:

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

Notez la valeur codée commencera par ENC( et se terminent par ), il est donc facile de dire si une propriété d'un fichier est chiffrée.

De plus, notez que le mot de passe utilisé pour config.setPassword() est et non le mot de passe que vous enregistrez dans le fichier de propriétés. Au lieu de cela, c'est le mot de passe pour crypter/décrypter la valeur que vous stockez. Ce que ce mot de passe est et comment le définir est à vous.Je donne par défaut le nom de classe complet de tout ce qui lit le fichier de propriétés. Finalement, si vous utilisez Spring, Jasypt a une classe EncryptablePropertyPlaceholderConfigurer que vous pouvez utiliser pour charger le fichier de propriétés et utiliser la syntaxe ${foo} dans vos fichiers Spring XML pour faire une substitution de variable pour des choses comme les mots de passe de base de données.

3

Voici quelques aides pour chiffrer ou déchiffrer en utilisant 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; 
} 

Il suffit d'appeler la méthode correspond le.

+0

La source complète est sur code.google.com [ici] (https://code.google.com/a/eclipselabs.org/p/android-openid/source/browse/trunk/CryptoUtils/src/CryptoUtils.java? r = 29). – 32U