在java中加密和解密属性文件值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3769622/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 04:42:06  来源:igfitidea点击:

encrypt and decrypt property file value in java

javaencryption

提问by user234194

I am looking for a way to encrypt a password in a configuration file that is being read by a Java program. Currently, I read-in the password from the text file, but that leaves the password sitting right out in the open if someone were to look at the config file.

我正在寻找一种方法来加密 Java 程序正在读取的配置文件中的密码。目前,我从文本文件中读入了密码,但是如果有人要查看配置文件,那么密码就会处于公开状态。

I was thinking of building a simple class where user could type in their desired password, get an encrypted version of the password, then paste the encrypted version into the configuration text file. Then the application would read encrypted password, decrypt the password back into a string, and move on.

我正在考虑构建一个简单的类,用户可以在其中输入所需的密码,获取密码的加密版本,然后将加密版本粘贴到配置文本文件中。然后应用程序将读取加密的密码,将密码解密回字符串,然后继续。

I am having trouble with the string-->encrytped bytes-->string conversions.

我在字符串--> 加密字节--> 字符串转换方面遇到问题。

I am using the built-in java security classes to implement this code. Here is some sample test code:

我正在使用内置的 java 安全类来实现此代码。下面是一些示例测试代码:

    // 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=[B@2a4983


When I run the code, I get the following output:
password: passwd
encrypted: [B@2a4983
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

Any help on the error, structure, or process I am using to do this would be great. Thanks.

任何有关我用来执行此操作的错误、结构或过程的帮助都会很棒。谢谢。

采纳答案by Cheesle

A really simple solution would be to use Base64 encoding, see the code snippet below :-

一个非常简单的解决方案是使用 Base64 编码,请参阅下面的代码片段:-

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;
}

...

回答by Waldheinz

I am having trouble with the string-->encrytped bytes-->string conversions.

我在字符串--> 加密字节--> 字符串转换方面遇到问题。

I'd stuff the byte array through a base64 en/decoder, this way you'll have to persist strings that contain nothing but chars within (a subset of) ASCII, which should limit your trouble. Have a look e.g. at commons codecsand replace your new String(decrypted)with a call to one of the static methods in the org.apache.commons.codec.binary.Base64class.

我会通过 base64 en/decoder 填充字节数组,这样您就必须保留只包含 ASCII(的子集)中的字符的字符串,这应该可以限制您的麻烦。看一看commons 编解码器,并用new String(decrypted)org.apache.commons.codec.binary.Base64类中的一个静态方法的调用替换你的。

Besides that I think what you ultimately want to do is not strictly to "encrypt" the password but rather to store only a hash of the password, which was already discussed on SO.

除此之外,我认为您最终想要做的不是严格“加密”密码,而是仅存储密码的哈希值,这已经在SO 上讨论过。

回答by AngerClown

Take a look at Jasypt. It has already done the heavy lifting for you. Specifically, the org.jasypt.encryption.pbe.StandardPBEStringEncryptorand org.jasypt.properties.PropertyValueEncryptionUtilsclasses.

看看Jasypt。它已经为您完成了繁重的工作。具体来说,org.jasypt.encryption.pbe.StandardPBEStringEncryptororg.jasypt.properties.PropertyValueEncryptionUtils类。

Create an encryptor:

创建加密器:

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

Then use PropertyValueEncryptionUtilsto encrypt / decrypt values:

然后用于PropertyValueEncryptionUtils加密/解密值:

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

Note the encoded value will start with ENC(and end with ), so it's easy to tell if a property from a file is encrypted.

请注意,编码值将以 开头ENC(和结尾),因此很容易判断文件中的属性是否已加密。

Also, note that the password used for config.setPassword()is notthe password you are encoding to store in the properties file. Instead, it is the password to encrypt / decrypt the valueyou are storing. What this password is and how to set it is up to you. I default to the fully qualified classname of whatever is reading the Properties file.

此外,请注意使用的密码config.setPassword()不是要在编码存储在属性文件中的密码。相反,它是加密/解密您存储的值的密码。此密码是什么以及如何设置它取决于您。我默认为读取属性文件的任何内容的完全限定类名。

Finally, if you are using Spring, Jasypt has an EncryptablePropertyPlaceholderConfigurerclass that you can use to load the properties file and use ${foo}syntax in your Spring XML files to do variable substitution for things like DB passwords.

最后,如果您使用的是 Spring,Jasypt 有一个EncryptablePropertyPlaceholderConfigurer类,您可以使用它来加载属性文件并使用${foo}Spring XML 文件中的语法对 DB 密码等内容进行变量替换。

回答by sujesh

Here are some helpers to encrypt or decrypt using AES in Java:

以下是一些在 Java 中使用 AES 加密或解密的助手:

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;
}

Just call the appropiate method.

只需调用适当的方法。