package hayashi.yuu.tools.properties;

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

/**
 * 暗号化モドキ
 * @author Hayashi,Yuu
 * @version 2010/02/07
 * @since 2009/01/27
 */
public class Encrypt
{
	/**
	 * @param args
	 */
    public static void main(String[] args) {
    	String source = "deister07";
        System.out.println("source \"" + source + "\"");
        try {
        	Encrypt.PASSWORD_KEY = "hayashihimitukagi";
        	
            // 暗号化
        	String encStr = encrypt(source);
            System.out.println("encStr \"" + encStr + "\"");

            // 復号
            String decStr = decode(encStr);
            System.out.println("decodeStr \"" + decStr + "\"");
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
    
	// ソルト
	private static final byte[] SALT = {(byte)0xc7, (byte)0x73, (byte)0x21, (byte)0x8c, (byte)0x7e, (byte)0xc8, (byte)0xee, (byte)0x99};
	private static final int NUM = 128;		// 繰り返し回数
	
	/**
	 * 暗号化のための秘密鍵
	 * 心配性の人は、この値を変えてください。
	 */
	public static String PASSWORD_KEY = "himitukagi";		// 秘密鍵

	/**
     * 指定された文字列を暗号化する。
     * @param source	暗号化したい文字列
     * @return	暗号化された文字列
     * @throws NoSuchAlgorithmException 
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException 
     * @throws BadPaddingException 
     * @throws IllegalBlockSizeException 
	 * @throws InvalidKeySpecException 
	 * @throws InvalidAlgorithmParameterException 
     */
    public static String encrypt(String source) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException {
		//16進数化
		return toHexString(encrypt(source.getBytes()));
    }
    
    /**
     * 指定されたバイト配列を暗号化する。
     * @param source	暗号化したいバイト配列
     * @return	暗号化されたバイト配列
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws InvalidKeySpecException
     * @throws InvalidAlgorithmParameterException
     */
    public static byte[] encrypt(byte[] source) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException {
		PBEParameterSpec pbeParam = new PBEParameterSpec(SALT, NUM);
		    
		char[] pass = PASSWORD_KEY.toCharArray();
		PBEKeySpec pbeKey = new PBEKeySpec(pass);
		SecretKeyFactory secKeys = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
		SecretKey secKey = secKeys.generateSecret(pbeKey);
		
		Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES");
		cipher.init(Cipher.ENCRYPT_MODE, secKey, pbeParam);
		
		//暗号化
		return cipher.doFinal(source);
    }
    
    /**
     * 復号
     * @param source	暗号化された文字列
     * @return	復号された文字列
     * @throws NoSuchAlgorithmException 
     * @throws InvalidKeySpecException 
     * @throws NoSuchPaddingException 
     * @throws InvalidAlgorithmParameterException
     * @throws InvalidKeyException 
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException 
     */
    public static String decode(String source) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
    	if (source == null) {
    		return null;
    	}
		byte[] bytepass = toHexBin(source);
		return new String(decode(bytepass));
    }

    /**
     * 復号
     * @param source	暗号化されたバイト配列
     * @return	復号されたバイト配列
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws InvalidAlgorithmParameterException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     */
    public static byte[] decode(byte[] source) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
		// PBE を新たに生成
        PBEParameterSpec pbeParamSpecDec = new PBEParameterSpec(SALT, NUM);
        
        PBEKeySpec pbeKeySpecDec = new PBEKeySpec(PASSWORD_KEY.toCharArray());
        SecretKeyFactory keyFacDec = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
        SecretKey pbeKeyDec = keyFacDec.generateSecret(pbeKeySpecDec);

        Cipher cDec = Cipher.getInstance("PBEWithMD5AndDES");
        cDec.init(Cipher.DECRYPT_MODE, pbeKeyDec, pbeParamSpecDec);
        
        return cDec.doFinal(source);
    }


    /**
     * バイナリ配列を文字列に変換する。
     * 
     * @param bs	バイト配列（バイナリ）
     * @return	16進表現文字列
     */
	private static String toHexString(byte[] binStr) {
		StringBuffer buffer = new StringBuffer(binStr.length * 2);
		for (int i = 0; i < binStr.length; i++) {
			if ((binStr[i] >= 0) && (binStr[i] < 0x10)) {
				buffer.append('0');
			}
			buffer.append(Integer.toHexString(0xff & binStr[i]));
		}
		return buffer.toString();
	}

    /**
     * バイナリ文字列をバイナリ配列に変換する。
     * 
     * @param bs	16進表現文字列
     * @return	バイナリ配列
     */
	private static byte[] toHexBin(String binStr) {
		byte[] bin = new byte[binStr.length() / 2];
		
		StringBuffer sbuf = new StringBuffer(binStr);
		for (int i = 0; i < bin.length; i++) {
			String s = sbuf.substring(i*2, i*2+2).toUpperCase();
			bin[i] = (byte)Integer.parseInt(s, 16);
		}
		return bin;
	}
}
