【问题标题】:encrypt and decrypt property file value in java在java中加密和解密属性文件值
【发布时间】:2011-04-15 17:44:40
【问题描述】:

我正在寻找一种在配置文件中加密密码的方法 正在由 Java 程序读取。目前,我阅读了 来自文本文件的密码,但这会使密码保持正确 如果有人要查看配置文件,请公开。

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

我遇到了字符串问题-->加密字节-->字符串 转化率。

我正在使用内置的 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

有关我用于执行此操作的错误、结构或流程的任何帮助 会很好。谢谢。

【问题讨论】:

  • 如果您的应用程序在客户端机器上,它可以被破解并发现解密密钥。一旦发生这种情况,您也可以使用纯文本。真正保护该工作流程的唯一方法是在安全服务器上进行解密。你试图解决什么问题导致你尝试这个? (为什么需要存储这样的密码?)
  • 我不想将密码作为纯文本存储在配置文件中。
  • @user234194:你没抓住重点。如果将解密密钥存储在客户端,则不安全,任何拥有客户端的人都可以解密。
  • 最终,不,这不安全,但它是链条中的一个环节。这个想法是,您不想让任何有权访问机器/文件系统的人轻松查看数据库密码是什么。如果安全性仅基于看不到此配置文件的人,那么您将遇到更大的问题:)

标签: java encryption


【解决方案1】:

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

创建一个加密器:

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

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

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

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

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

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

【讨论】:

    【解决方案2】:

    这里有一些在 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;
    }
    

    只需调用适当的方法即可。

    【讨论】:

    • 完整源代码在 code.google.com here
    【解决方案3】:

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

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

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

      • 虽然使用 sun.misc 中的类使得它不可移植
      • 为什么不便携?以什么方式?
      • 使用base64编码会阻止属性被肉眼看到,但是一旦有人掌握了哈希,他们不能通过反向base64方法运行它吗?
      • @Haoest 更具体地说 - 它首先不是哈希。完全没有。
      • Base64 是一种存储格式,不是散列函数,当然也不是合适的加密方案。以“编码”形式,肉眼也可以清楚地看到(使用它的事实)。
      猜你喜欢
      • 2015-12-29
      • 1970-01-01
      • 2012-02-18
      • 1970-01-01
      • 2015-09-10
      • 2011-01-27
      • 2015-06-13
      • 1970-01-01
      • 2010-11-27
      相关资源
      最近更新 更多