扩展Spring--外部属性文件安全(二)

 

编写支持加密属性文件的实现类
    通过以上分析,我们设计一个支持加密属性文件的增强型PropertyPlaceholderConfigurer,其代码如所示:
代码清单 2
Java代码
  1. import java.io.IOException;   
  2. import java.io.InputStream;   
  3. import java.io.InputStreamReader;   
  4. import java.security.Key;   
  5. import java.util.Properties;   
  6. import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;   
  7. import org.springframework.core.io.Resource;   
  8. import org.springframework.util.DefaultPropertiesPersister;   
  9. import org.springframework.util.PropertiesPersister;   
  10. public class DecryptPropertyPlaceholderConfigurer   
  11.         extends PropertyPlaceholderConfigurer ...{   
  12.     private Resource[] locations;   //① 重新定义父类中的这个同名属性   
  13.     private Resource keyLocation; //② 用于指定密钥文件   
  14.     public void setKeyLocation(Resource keyLocation) ...{   
  15.         this.keyLocation = keyLocation;   
  16.     }   
  17.     public void setLocations(Resource[] locations) ...{   
  18.         this.locations = locations;   
  19.     }   
  20.     public void loadProperties(Properties props) throws IOException ...{   
  21.         if (this.locations != null) ...{   
  22.             PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();   
  23.             for (int i = 0; i < this.locations.length; i++) ...{   
  24.                 Resource location = this.locations[i];   
  25.                 if (logger.isInfoEnabled()) ...{   
  26.                     logger.info("Loading properties file from " + location);   
  27.                 }   
  28.                 InputStream is = null;   
  29.                 try ...{   
  30.                     is = location.getInputStream();   
  31.                         //③ 加载密钥   
  32.                     Key key = DESEncryptUtil.getKey(keyLocation.getInputStream());   
  33.                     //④ 对属性文件进行解密   
  34. is = DESEncryptUtil.doDecrypt(key, is);   
  35. //⑤ 将解密后的属性流装载到props中   
  36.                     if(fileEncoding != null)...{   
  37.                         propertiesPersister.load(props,   
  38.         new InputStreamReader(is,fileEncoding));   
  39.                     }else...{   
  40.                         propertiesPersister.load(props ,is);   
  41.                     }   
  42.                 } finally ...{   
  43.                     if (is != null)   
  44.                         is.close();   
  45.                 }   
  46.             }   
  47.         }   
  48.     }   
  49.     }   
  50. }  
    对locations指定的属性文件流数据进行额外的解密工作,解密后再装载到props中。比起PropertyPlaceholderConfigurer,我们只做了额外的一件事:装载前对属性资源进行解密。

    在代码清单 2的③和④处,我们使用了一个DES解密的工具类对加密的属性文件流进行解密。
    对文件进行对称加密的算法很多,一般使用DES对称加密算法,因为它速度很快,破解困难,DESEncryptUtil不但提供了DES解密功能,还提供了DES加密的功能,因为属性文件在部署前必须经常加密:
    代码清单 3 加密解密工具类
Java代码
  1. public class DESEncryptUtil ...{   
  2.     public static Key createKey() throws NoSuchAlgorithmException {//创建一个密钥   
  3.         Security.insertProviderAt(new com.sun.crypto.provider.SunJCE(), 1);   
  4.         KeyGenerator generator = KeyGenerator.getInstance("DES");   
  5.         generator.init(new SecureRandom());   
  6.         Key key = generator.generateKey();   
  7.         return key;   
  8.     }   
  9.     public static Key getKey(InputStream is) {   
  10.         try ...{   
  11.             ObjectInputStream ois = new ObjectInputStream(is);   
  12.             return (Key) ois.readObject();   
  13.         } catch (Exception e) ...{   
  14.             e.printStackTrace();   
  15.             throw new RuntimeException(e);   
  16.         }   
  17.     }   
  18.     private static byte[] doEncrypt(Key key, byte[] data) {//对数据进行加密   
  19.         try {   
  20.             Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");   
  21.             cipher.init(Cipher.ENCRYPT_MODE, key);   
  22.             byte[] raw = cipher.doFinal(data);   
  23.             return raw;   
  24.         } catch (Exception e) {   
  25.             e.printStackTrace();   
  26.             throw new RuntimeException(e);   
  27.         }   
  28.     }   
  29.     public static InputStream doDecrypt(Key key, InputStream in) {//对数据进行解密   
  30.         try {   
  31.             Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");   
  32.             cipher.init(Cipher.DECRYPT_MODE, key);   
  33.             ByteArrayOutputStream bout = new ByteArrayOutputStream();   
  34.             byte[] tmpbuf = new byte[1024];   
  35.             int count = 0;   
  36.             while ((count = in.read(tmpbuf)) != -1) {   
  37.                 bout.write(tmpbuf, 0, count);   
  38.                 tmpbuf = new byte[1024];   
  39.             }   
  40.             in.close();   
  41.             byte[] orgData = bout.toByteArray();   
  42.             byte[] raw = cipher.doFinal(orgData);   
  43.             ByteArrayInputStream bin = new ByteArrayInputStream(raw);   
  44.             return bin;   
  45.         } catch (Exception e) {   
  46.             e.printStackTrace();   
  47.             throw new RuntimeException(e);   
  48.         }   
  49.     }   
  50.     public static void main(String[] args) throws Exception {//提供了Java命令使用该工具的功能   
  51.         if (args.length == 2 && args[0].equals("key")) {// 生成密钥文件   
  52.             Key key = DESEncryptUtil.createKey();   
  53.             ObjectOutputStream oos = new ObjectOutputStream(   
  54.                     new FileOutputStream(args[1]));   
  55.             oos.writeObject(key);   
  56.             oos.close();   
  57.             System.out.println("成功生成密钥文件。");   
  58.         } else if (args.length == 3 && args[0].equals("encrypt")) {//对文件进行加密   
  59.             File file = new File(args[1]);   
  60.             FileInputStream in = new FileInputStream(file);   
  61.             ByteArrayOutputStream bout = new ByteArrayOutputStream();   
  62.             byte[] tmpbuf = new byte[1024];   
  63.             int count = 0;   
  64.             while ((count = in.read(tmpbuf)) != -1) {   
  65.                 bout.write(tmpbuf, 0, count);   
  66.                 tmpbuf = new byte[1024];   
  67.             }   
  68.             in.close();   
  69.             byte[] orgData = bout.toByteArray();   
  70.             Key key = getKey(new FileInputStream(args[2]));   
  71.             byte[] raw = DESEncryptUtil.doEncrypt(key, orgData);   
  72.             file = new File(file.getParent() + "//en_" + file.getName());   
  73.             FileOutputStream out = new FileOutputStream(file);   
  74.             out.write(raw);   
  75.             out.close();   
  76.             System.out.println("成功加密,加密文件位于:"+file.getAbsolutePath());   
  77.         } else if (args.length == 3 && args[0].equals("decrypt")) {//对文件进行解密   
  78.             File file = new File(args[1]);   
  79.             FileInputStream fis = new FileInputStream(file);   
  80.             Key key = getKey(new FileInputStream(args[2]));   
  81.             InputStream raw = DESEncryptUtil.doDecrypt(key, fis);   
  82.             ByteArrayOutputStream bout = new ByteArrayOutputStream();   
  83.             byte[] tmpbuf = new byte[1024];   
  84.             int count = 0;   
  85.             while ((count = raw.read(tmpbuf)) != -1) {   
  86.                 bout.write(tmpbuf, 0, count);   
  87.                 tmpbuf = new byte[1024];   
  88.             }   
  89.             raw.close();   
  90.             byte[] orgData = bout.toByteArray();   
  91.             file = new File(file.getParent() + "//rs_" + file.getName());   
  92.             FileOutputStream fos = new FileOutputStream(file);   
  93.             fos.write(orgData);   
  94.             System.out.println("成功解密,解密文件位于:"+file.getAbsolutePath());   
  95.         }   
  96.     }   
  97. }    

    解密工作主要涉及到两个类Cipher和Key,前者是加密器,可以通过init()方法设置工作模式和密钥,在这里,我们设置为解密工作模式:Cipher.DECRYPT_MODE。Cipher通过doFinal()方法对字节数组进行加密或解密。

你可能感兴趣的:(扩展Spring--外部属性文件安全(二))