Spring属性ファイルの暗号解読応用


Springの開発では、多くの場合、プレースホルダを使用してプロパティファイルのプロパティ値を参照し、システムを簡素化し、システムの柔軟性と汎用性を向上させることができます.この配置方式には2つの明らかな利点がある:1、メンテナンスの作業量を減らす:資源の配置情報は多く応用して共有することができ、複数の応用が同じ資源を使用する場合、資源の住所、ユーザー名などの配置情報が変更された場合、あなたは属性ファイルを調整すればよい.2、配置をもっと簡単にする:Springプロファイルは主にアプリケーションのBeanを説明し、これらの構成情報は開発が完了した後、固定されるべきである.アプリケーションを配置する時、配置環境によってデータソース、メールサーバーの構成情報を調整し、それらの構成情報を属性ファイルに独立させる必要がある.アプリケーション・デプロイメント担当者は、リソース・プロパティ・ファイルを調整するだけで、複雑なSpringプロファイルに注目する必要はありません.導入とメンテナンスが容易になるだけでなく、エラーの確率も低下します.    Springは、外部属性値を参照したを処理し、実際の構成値に翻訳するBeanFactoryPostProcessorBeanファクトリのバックグラウンドプロセッサインタフェースの実装クラスを提供します.    一般的な属性情報は明示的に属性ファイルに格納されて問題はありませんが、データソースやメールサーバのユーザー名パスワードなどの重要な情報であれば、場合によっては密文で保存する必要がある場合があります.Webアプリケーションのクライアント・ユーザーにはプロファイルが表示されない場合がありますが、デプロイメント・マシンにアクセスできるすべてのユーザーに対して、保留せずにオープンするのではなく、特定の保守担当者が重要なリソースの構成情報を把握することを望んでいる場合があります.    このような高度なセキュリティ要件を有するシステム(電気通信、銀行、重点人口ライブラリなど)については、リソース接続などのプロパティプロファイルの構成情報を暗号化して保存する必要があります.次にSpringコンテナを起動させ、プロファイルを読み込んだ後、先に復号してからプレースホルダの置換を行います.    残念ながら、PropertyPlaceholderConfigurerは明文のプロパティファイルのみをサポートしています.しかし、Springフレームワークの拡張性を十分に活用し、P r e p e r t y PlaceholderConfigurerクラスを拡張することで、私たちの要件を達成することができます.この文書では、暗号化されたプロパティファイルを使用する原理について説明し、具体的な実装を提供します.    従来の方法でプロパティファイルを使用する    一般に、外部プロパティファイルは、データ・ソースやメール・サーバなどの構成情報を定義するために使用されます.ここでは、プロパティファイルを使用する方法を簡単な例で説明します.car.propertiesプロパティファイルがあるとします.ファイルの内容は次のとおりです.    brand=赤旗CA 72    maxSpeed=250     price=20000.00     このファイルはクラスパスのcom/baobaotao/ディレクトリの下に配置され、SpringプロファイルでP r o p e r t y PlaceholderConfigurerを使用してこのプロファイルを導入し、コードリスト1に示すように、属性ファイル内の属性項目をプレースホルダで参照します.コードリスト1は外部属性ファイルを使用して構成されます.
<!-- ①          -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
       <list>
          <value>classpath:com/baobaotao/car.properties</value> ②         
       </list>
    </property>
    <property name="fileEncoding" value="utf-8"/>
</bean>
<!-- ③         , car     -->
<bean id="car" class="com.baobaotao.place.Car">
    <property name="brand" value="${brand}" />
    <property name="maxSpeed" value="${maxSpeed}" />
    <property name="price" value="${price}" />
</bean>

    ①では、クラス参照外部のプロパティファイルをP r e t y P e r t y P r e c e holderConfigurerというBeanFactoryPostProcessorで実装し、そのlocationsプロパティでSpringプロファイルで参照されるプロパティファイルを指定します.P r e r t y P l e c e holderConfigurer内部では、locationsはResource配列なので、アドレスの前にリソースタイププレフィックスを追加することができます.②に示すように.複数のプロパティファイルを参照する必要がある場合は、②に対応するプロファイル項目を追加するだけです.    PropertyPlaceholderConfigurer構造の解析    SpringはP r o p e r t y P r e c e holderConfigurerを通じて外部属性ファイルのサポートを提供していることを知っています.暗号化された属性ファイルを使用するために、このクラスの動作メカニズムを分析し、改造する必要があります.まず、このクラスの構造を理解してみましょう(添付ファイル1を参照).    ここで、PropertiesLoaderSupportクラスには重要なprotected void loadProperties(Properties props)メソッドがあり、そのコメントを表示します.このメソッドの役割は、PropertyPlaceholderConfigurerのlocationsプロパティで定義されたプロパティファイルの内容をpropsパラメータオブジェクトに読み込むことです.この方法は比較的奇妙で,Javaはインパラメータによって値を返すことは少ないが,この方法はそうである.    したがって,この方法を簡単に再ロードし,リソースファイルの内容をPropertiesに変換する前に,復号化のステップを追加すればよい.しかし、PropertiesLoaderSupportの設計には残念な点があり、locations属性はprivateであり、setterのみを提供し、getterを提供していない.したがって、PropertiesLoaderSupportのlocations(リソースアドレス)をサブクラスで取得することはできません.そのため、サブクラスでlocationsプロパティを再定義し、PropertiesLoaderSupportのsetLocations()メソッドを上書きする必要があります.
 
暗号化プロパティファイルをサポートする実装クラスの作成    以上の解析により、暗号化されたプロパティファイルをサポートする拡張PropertyPlaceholderConfigurerを設計しました.コードは、コードリスト2に示されています.
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.Key;
import java.util.Properties;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.core.io.Resource;
import org.springframework.util.DefaultPropertiesPersister;
import org.springframework.util.PropertiesPersister;
public class DecryptPropertyPlaceholderConfigurer
        extends PropertyPlaceholderConfigurer ...{
    private Resource[] locations;   //①               
    private Resource keyLocation; //②         
    public void setKeyLocation(Resource keyLocation) ...{
        this.keyLocation = keyLocation;
    }
    public void setLocations(Resource[] locations) ...{
        this.locations = locations;
    }
    public void loadProperties(Properties props) throws IOException ...{
        if (this.locations != null) ...{
            PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();
            for (int i = 0; i < this.locations.length; i++) ...{
                Resource location = this.locations[i];
                if (logger.isInfoEnabled()) ...{
                    logger.info("Loading properties file from " + location);
                }
                InputStream is = null;
                try{
                    is = location.getInputStream();
                        //③     
                    Key key = DESEncryptUtil.getKey(keyLocation.getInputStream());
                    //④          
                    is = DESEncryptUtil.doDecrypt(key, is);
                    //⑤            props 
                    if(fileEncoding != null)...{
                        propertiesPersister.load(props,new InputStreamReader(is,fileEncoding));
                    }else {
                        propertiesPersister.load(props ,is);
                    }
                } finally{
                    if (is != null)
                        is.close();
                }
            }
        }
    }
    }
}

 
    locationsで指定したプロパティファイルストリームデータを追加で復号し、復号してpropsにロードします.PropertyPlaceholderConfigurerよりも、ロード前に属性リソースを復号することしかできません.    コードリスト2の③と④では,暗号化された属性ファイルストリームをDES復号化のツールクラスを用いて復号化した.    ファイルを対称的に暗号化するアルゴリズムは多く、一般的にDES対称暗号化アルゴリズムを使用している.それは速度が速く、解読が困難であるため、DESEncryptutilはDES復号機能を提供するだけでなく、DES暗号化の機能も提供している.属性ファイルは配置前に常に暗号化しなければならないからだ.    コードリスト3暗号解読ツールクラス
public class DESEncryptUtil ...{
    public static Key createKey() throws NoSuchAlgorithmException {//      
        Security.insertProviderAt(new com.sun.crypto.provider.SunJCE(), 1);
        KeyGenerator generator = KeyGenerator.getInstance("DES");
        generator.init(new SecureRandom());
        Key key = generator.generateKey();
        return key;
    }
    public static Key getKey(InputStream is) {
        try{
            ObjectInputStream ois = new ObjectInputStream(is);
            return (Key) ois.readObject();
        } catch (Exception e) ...{
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    private static byte[] doEncrypt(Key key, byte[] data) {//       
        try {
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] raw = cipher.doFinal(data);
            return raw;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    public static InputStream doDecrypt(Key key, InputStream in) {//       
        try {
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, key);
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            byte[] tmpbuf = new byte[1024];
            int count = 0;
            while ((count = in.read(tmpbuf)) != -1) {
                bout.write(tmpbuf, 0, count);
                tmpbuf = new byte[1024];
            }
            in.close();
            byte[] orgData = bout.toByteArray();
            byte[] raw = cipher.doFinal(orgData);
            ByteArrayInputStream bin = new ByteArrayInputStream(raw);
            return bin;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    public static void main(String[] args) throws Exception {//   Java          
        if (args.length == 2 && args[0].equals("key")) {//       
            Key key = DESEncryptUtil.createKey();
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(args[1]));
            oos.writeObject(key);
            oos.close();
            System.out.println("        。");
        } else if (args.length == 3 && args[0].equals("encrypt")) {//       
            File file = new File(args[1]);
            FileInputStream in = new FileInputStream(file);
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            byte[] tmpbuf = new byte[1024];
            int count = 0;
            while ((count = in.read(tmpbuf)) != -1) {
                bout.write(tmpbuf, 0, count);
                tmpbuf = new byte[1024];
            }
            in.close();
            byte[] orgData = bout.toByteArray();
            Key key = getKey(new FileInputStream(args[2]));
            byte[] raw = DESEncryptUtil.doEncrypt(key, orgData);
            file = new File(file.getParent() + "\\en_" + file.getName());
            FileOutputStream out = new FileOutputStream(file);
            out.write(raw);
            out.close();
            System.out.println("    ,      :"+file.getAbsolutePath());
        } else if (args.length == 3 && args[0].equals("decrypt")) {//       
            File file = new File(args[1]);
            FileInputStream fis = new FileInputStream(file);
            Key key = getKey(new FileInputStream(args[2]));
            InputStream raw = DESEncryptUtil.doDecrypt(key, fis);
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            byte[] tmpbuf = new byte[1024];
            int count = 0;
            while ((count = raw.read(tmpbuf)) != -1) {
                bout.write(tmpbuf, 0, count);
                tmpbuf = new byte[1024];
            }
            raw.close();
            byte[] orgData = bout.toByteArray();
            file = new File(file.getParent() + "\\rs_" + file.getName());
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(orgData);
            System.out.println("    ,      :"+file.getAbsolutePath());
        }
    }
}  

    復号化作業は主に2つのクラスCipherとKeyに関連し、前者は暗号化器であり、init()メソッドで動作モードと鍵を設定することができる.ここでは、復号化動作モード:Cipher.DECRYPT_MODE.CipherはdoFinal()法によりバイト配列を暗号化または復号化する.
    プロパティ・ファイルの暗号化を完了するには、まず、明文のプロパティ・ファイルを暗号化する前に、キー・ファイルを取得する必要があります.プロパティファイルの情報を調整する必要がある場合は、暗号化されたプロパティファイルを鍵で復号し、プロパティ情報を調整してから暗号化する逆のプロセスを実行する必要があります.DESEncryptUtilツールクラスでは、上記の3つの作業を完了できます.1、鍵ファイルjava com.baobaotao.DESEncryptUtil key D:key.datの最初のパラメータはkeyであり、鍵ファイルの作成を示し、2番目のパラメータは鍵ファイルの保存先です.2、鍵ファイルで属性ファイルを暗号化javacom.baobaotao.DESEncryptUtil encrypt d:test.properties d:key.dat最初のパラメータはencryptであり、暗号化を表し、2番目のパラメータは暗号化が必要な属性ファイルであり、3番目のパラメータは鍵ファイルである.暗号化に成功するとen_が生成されますtest.propertiesの暗号化ファイル.3、暗号化された属性ファイルを鍵ファイルで復号するjavacom.baobaotao.DESEncryptUtil decrypt d:test.properties d:key.dat最初のパラメータはdecryptであり、復号を表し、2番目のパラメータは復号が必要な属性ファイルであり、3番目のパラメータは鍵ファイルである.暗号化に成功するとrs_が生成されますtest.propertiesの復号ファイル.    Springでの暗号化プロパティファイルの構成    DESEncryptUtilツールクラスでkey.batキーを作成し、car.propertiesプロパティを暗号化して暗号化ファイルen_を生成するとします.car.properties.次に、Springコンテナが暗号化されたプロパティファイルをサポートするように、D e c r y p p t P r e t y P e r t y P r e r t y P e r t y P e r t y P e r t y P r e r t y P r e r t y P r e r t y P r e m a c e holderConfi    DESEncryptUtilツールクラスでkey.batキーを作成し、car.propertiesプロパティを暗号化して暗号化ファイルen_を生成するとします.car.properties.次に、Springコンテナが暗号化されたプロパティファイルをサポートするように、D e c r y p p t P r e t y P e r t y P r e r t y P e r t y P e r t y P e r t y P r e r t y P r e r t y P r e r t y P r e m a c e holderConfi
<bean class="com.baobaotao.place.DecryptPropertyPlaceholderConfigurer"> ①
    <property name="locations">
        <list>
            <value>classpath:com/baobaotao/en_car.properties</value>
        </list>
    </property>
    <property name="keyLocation" value="classpath:com/baobaotao/key.dat" />
    <property name="fileEncoding" value="utf-8" />
</bean>
<bean id="car" class="com.baobaotao.place.Car"> ②
    <property name="brand" value="${brand}" />
    <property name="maxSpeed" value="${maxSpeed}" />
    <property name="price" value="${price}" />
</bean>

     注意①での構成は、SpringのP r e r t y P e r y P e r y p t P r o p t P r e t y P r e t y P r e t y P r e t y P r e r C o f i gurerの代わりに、SpringのP r e t y P e r y P e r t y P e r e r o n f i gurerを使用します.car.propertiesのプロパティ・アイテム.    小結    Springを構成するには、いくつかの重要な情報をプロパティ・ファイルに独立させるのが一般的です.Springは、明示的に保存されているプロパティ・ファイルのみをサポートします.場合によっては、重要な情報の安全を保証するために、プロパティ・ファイルを暗号化して保存することができます.PropertyPlaceholderConfigurerを拡張することで,属性ファイルストリームのロード後に適用する前に復号化すればこの問題をうまく解決できる.