/*********************************************************************
* All Rights reserved,Copyright (c) K-Opticom
**********************************************************************
*＜プログラム内容＞
*   システム名      ：eo顧客基幹システム
*	モジュール名	：JCCcomEncryptionUtil
*	ソースファイル名：JCCcomEncryptionUtil.java
*	作成者			：富士通
*	日付			：2011年05月23日
*＜機能概要＞
*	文字列を暗号化および復号化する。
*＜修正履歴＞
*	バージョン	修正日		修正者		修正内容
*	ｖ1.00.00
*
********************************************************************************/

package eo.common.util;

import javax.crypto.IllegalBlockSizeException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.io.UnsupportedEncodingException;
import javax.crypto.BadPaddingException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Cipher;

/**
 * <B>クラス名：</B>暗号化・復号化<BR>
 * 
 * <B>処理概要：</B><BR>
 * 　Browfish方式で文字列を暗号化および復号化する。<BR>
 * 
 * <B>前提条件</B><BR>
 * 　Java2SDK1.4以降を使用すること。<BR>
 * 
 * <B>補足事項</B><BR>
 * 　Browfish方式は可変長の鍵を用いたブロック暗号。<BR>
 */
public class JCCcomEncryptionUtil {

    private static final String ENCRYPT_FORMAT = "Blowfish"; // 暗号化方式
    private static final String TRUE_KEY = "ENCRYPT_KEY";
    
    /** 
     * 暗号化処理を行います。
     * @param  String key
     * @param  String text
     * @throws  IllegalBlockSizeException,InvalidKeyException,NoSuchAlgorithmException
     *           UnsupportedEncodingException,BadPaddingException,NoSuchPaddingException
     */
    public static String encrypt(String key, String text)
    throws IllegalBlockSizeException,
    InvalidKeyException,
    NoSuchAlgorithmException,
    UnsupportedEncodingException,
    BadPaddingException,
    NoSuchPaddingException
    {
    	// 2012/02/24 ダミーのキーを本当のキーに復号化する start
    	key = decryptKey(TRUE_KEY, key);
    	// 2012/02/24 ダミーのキーを本当のキーに復号化する end
        SecretKeySpec sksSpec = new SecretKeySpec(key.getBytes(), ENCRYPT_FORMAT);
        Cipher cipher = Cipher.getInstance(ENCRYPT_FORMAT);
        cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, sksSpec);
        byte[] encrypted = cipher.doFinal(text.getBytes());
        return byteArrayToHexString(encrypted);
    }

    /** 
     * 複合化処理を行います。
     * @param  String key
     * @param  String text
     * @throws  IllegalBlockSizeException,InvalidKeyException,NoSuchAlgorithmException
     *           UnsupportedEncodingException,BadPaddingException,NoSuchPaddingException
     */
    public static String decrypt(String key, String encrypted)
    throws IllegalBlockSizeException,
    InvalidKeyException,
    NoSuchAlgorithmException,
    UnsupportedEncodingException,
    BadPaddingException,
    NoSuchPaddingException
    {
    	// 2012/02/24 ダミーのキーを本当のキーに復号化する start
   		key = decryptKey(TRUE_KEY, key);
    	// 2012/02/24 ダミーのキーを本当のキーに復号化する end
    	
        SecretKeySpec sksSpec = new SecretKeySpec(key.getBytes(), ENCRYPT_FORMAT);
        Cipher cipher = Cipher.getInstance(ENCRYPT_FORMAT);
        cipher.init(javax.crypto.Cipher.DECRYPT_MODE, sksSpec);
        byte[] decrypted = cipher.doFinal(hexStringToByteArray(encrypted));
        return new String(decrypted);
    }
    
    /** 
     * 暗号化キーの複合化処理を行います。
     * @param  String key
     * @param  String text
     * @throws  IllegalBlockSizeException,InvalidKeyException,NoSuchAlgorithmException
     *           UnsupportedEncodingException,BadPaddingException,NoSuchPaddingException
     */
    public static String decryptKey(String key, String encrypted)
    throws IllegalBlockSizeException,
    InvalidKeyException,
    NoSuchAlgorithmException,
    UnsupportedEncodingException,
    BadPaddingException,
    NoSuchPaddingException
    {
        SecretKeySpec sksSpec = new SecretKeySpec(key.getBytes(), ENCRYPT_FORMAT);
        Cipher cipher = Cipher.getInstance(ENCRYPT_FORMAT);
        cipher.init(javax.crypto.Cipher.DECRYPT_MODE, sksSpec);
        byte[] decrypted = cipher.doFinal(hexStringToByteArray(encrypted));
        return new String(decrypted);
    }

    // ----- バイト列と文字列との間の変換処理

    private static final int STRING_SIZE = 2; // 1byteのデータから生成される文字数
    
    /**
     * バイト配列を16進数の文字列に変換
     * @param arg      バイト配列
     * @return String  16進数に変換した文字列
     */
    private static String byteArrayToHexString(byte[] arg) {
        StringBuffer byteArray = new StringBuffer();
        for (int i = 0;i < arg.length; i++) {
            // 暗号化されたバイト配列の要素を2進表現の文字列に変換
            String strByteCode = Integer.toHexString((int)arg[i]);
            // STRING_SIZEの桁数に整形
            if (strByteCode.length() >= STRING_SIZE) {
                strByteCode = strByteCode.substring(strByteCode.length() - STRING_SIZE, strByteCode.length());
            } else {
                while (strByteCode.length() < STRING_SIZE) {
                    strByteCode = "0" + strByteCode;
                }
            }
            byteArray.append(strByteCode);
        }
        return byteArray.toString();
    }

    /**
     * 16進数の文字列をバイト配列に変換
     * @param arg      16進数の文字列
     * @return byte[]  バイト配列
     */
    private static byte[] hexStringToByteArray(String arg) {
        byte[] byteArray = new byte[arg.length() / STRING_SIZE];
        int j = 0;
        for(int i = 0; i < arg.length() / STRING_SIZE; i++) {
            // 文字列を1byteづつ取得
            byteArray[i] = Integer.valueOf(arg.substring(j, j + STRING_SIZE), 16).byteValue();
            j += STRING_SIZE;
        }
        return byteArray;
    }

}
