package org.maachang.util;

import java.io.IOException;
import java.security.MessageDigest;
import java.util.Random;

/**
 * 独自暗号/解析オブジェクト.
 * 
 * @version 2007/10/18
 * @author masahito suzuki
 * @since MaachangBase 1.00
 */
public class Encryption {

    /**
     * 変換コード値.
     */
    private static final String CHARSET = "UTF8";

    /**
     * ステップコードマスク.
     */
    private static final int STEP_MASK = 0x0000007f;

    /**
     * ステップ条件マスク.
     */
    private static final int STEP_IF_MASK = 0x0000002;

    /**
     * セキュリティキー変換長.
     */
    private static final int CODE32_TO_CODE256CNV = 256;

    /**
     * 基本プライベートキー.
     */
    private static final byte[] BACK_PRIVATE_KEY = { (byte) 0xfe, (byte) 0x01,
            (byte) 0xed, (byte) 0x12, (byte) 0xdc, (byte) 0x23, (byte) 0xcb,
            (byte) 0x34, (byte) 0x45, (byte) 0xba, (byte) 0x56, (byte) 0xa9,
            (byte) 0x67, (byte) 0x98, (byte) 0x78, (byte) 0x87, (byte) 0x76,
            (byte) 0x89, (byte) 0x65, (byte) 0x9a, (byte) 0x54, (byte) 0xab,
            (byte) 0x43, (byte) 0xbc, (byte) 0xcd, (byte) 0x32, (byte) 0xde,
            (byte) 0x21, (byte) 0xef, (byte) 0x10, (byte) 0xff, (byte) 0x00 };

    /**
     * メッセージダイジェスト : MD5.
     */
    private static final String MD5_DIGEST = "MD5";

    /**
     * メッセージダイジェスト : SHA1.
     */
    private static final String SHA1_DIGEST = "SHA1";

    /**
     * MD5データ長.
     */
    private static final int MD5_LENGTH = 16;

    /**
     * チェックステップコードマスキング範囲.
     */
    public static final int CHECK_STEPMASK = 0x000000ff;

    /**
     * 無効ステップコード.
     */
    public static final int NOT_STEP = -1;

    /**
     * セキュリティーキー長.
     */
    public static final int ENCRYPION_KEY_LENGTH = 32;

    /**
     * ステップコード長.
     */
    public static final int STEP_CODE_LENGTH = 1;

    /**
     * マスク配列.
     */
    private static final int[] MASK_ARRAY = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
            11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
            28, 29, 30, 31, 1 };

    /**
     * 4ビット値情報から16進文字変換テーブル.
     */
    protected static final char[] convert10To16 = { '0', '1', '2', '3', '4',
            '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

    /**
     * 乱数オブジェクト.
     */
    private static final Random RAND = new Random(System.currentTimeMillis());

    /**
     * プライベート格納キー.
     */
    private final byte[] privateKey = new byte[Encryption.ENCRYPION_KEY_LENGTH];

    /**
     * パブリック格納キー.
     */
    private final byte[] publicKey = new byte[Encryption.ENCRYPION_KEY_LENGTH];

    /**
     * Encryptionステップコード.
     */
    private int encryptonStepCode = 0;

    /**
     * コンストラクタ. <BR>
     * <BR>
     * 情報を生成します.<BR>
     * この情報で生成した場合、デフォルトのプライベートキーを ベースとした暗号処理を実施します.
     */
    public Encryption() {
        this.create();
    }

    /**
     * コンストラクタ. <BR>
     * <BR>
     * 情報を生成します.<BR>
     * この情報で生成した場合、プライベートキーを指定した 暗号処理を実施します. <BR>
     * 
     * @param privateKey
     *            対象のプライベートキーを設定します.
     * @exception Exception
     *                例外.
     */
    public Encryption(byte[] privateKey) throws Exception {

        this.create(privateKey);
    }

    /**
     * ファイナライズ処理定義. <BR>
     * <BR>
     * ファイナライズ処理定義. <BR>
     * 
     * @exception Exception
     *                例外処理が返されます.
     */
    protected final void finalize() throws Exception {
        this.clear();
    }

    /**
     * 情報生成. <BR>
     * <BR>
     * 情報を生成します.<BR>
     * この情報で生成した場合、デフォルトのプライベートキーを ベースとした暗号処理を実施します.
     */
    public final void create() {
        this.clear();
    }

    /**
     * 情報生成. <BR>
     * <BR>
     * 情報を生成します.<BR>
     * この情報で生成した場合、プライベートキーを指定した 暗号処理を実施します. <BR>
     * 
     * @param privateKey
     *            対象のプライベートキーを設定します.
     * @exception Exception
     *                例外.
     */
    public final void create(byte[] privateKey) throws Exception {
        if (privateKey == null
                || privateKey.length != Encryption.ENCRYPION_KEY_LENGTH) {
            throw new IllegalArgumentException("対象のプライベートキーは不正です");
        }

        this.clear();
        System.arraycopy(privateKey, 0, this.privateKey, 0,
                Encryption.ENCRYPION_KEY_LENGTH);

    }

    /**
     * 情報のクリア. <BR>
     * <BR>
     * 情報をクリアします.
     */
    public final void clear() {
        System.arraycopy(Encryption.BACK_PRIVATE_KEY, 0, this.privateKey, 0,
                Encryption.ENCRYPION_KEY_LENGTH);

        this.encryptonStepCode = Encryption.NOT_STEP;
    }

    /**
     * 暗号処理. <BR>
     * <BR>
     * 暗号処理を実施します. <BR>
     * 
     * @param key
     *            対象のパブリックキー情報を設定します.<BR>
     *            パブリックキーを内部で自動生成する場合は、[ null ]を設定してください.
     * @param binary
     *            暗号対象のバイナリリソースを設定します.
     * @param offset
     *            暗号化開始位置を設定します.
     * @param size
     *            暗号化開始位置[ offset ]から暗号を行うサイズを設定します.
     * @return int 暗号ステップコード値が返されます.<BR>
     *         暗号化されたバイナリ情報の解析処理には、暗号ステップコード値が必要と なります.<BR>
     *         また、[ Encryption#getStepCode() ]メソッドからも同一の情報が取得されます.
     * @exception Exception
     *                例外.
     */
    public final int encryption(byte[] key, byte[] binary, int offset, int size)
            throws Exception {

        int step;

        byte[] baseKey = null;
        byte[] code32Key = null;
        byte[] cd32 = null;
        byte[] cd256 = null;

        if (binary == null || offset < 0 || size < 0
                || binary.length < offset + size) {
            throw new IllegalArgumentException("引数は不正です");
        }

        code32Key = this.publicKey;

        try {

            // Encryptionキーが設定されていない場合、新規作成.
            if (key == null) {
                Encryption.createPublicKey(code32Key);
                baseKey = new byte[Encryption.ENCRYPION_KEY_LENGTH];
                System.arraycopy(code32Key, 0, baseKey, 0,
                        Encryption.ENCRYPION_KEY_LENGTH);
            } else {
                baseKey = key;
                System.arraycopy(key, 0, code32Key, 0,
                        Encryption.ENCRYPION_KEY_LENGTH);
            }

            // 暗号キーをデフォルトキーで変換.
            cd32 = Encryption.encryptionEncryptionKey(code32Key,
                    this.privateKey);

            // 暗号キーを256バイトに引き伸ばす.
            cd256 = Encryption.code32To256(cd32);
            cd32 = null;

            // ステップコードの生成.
            step = Encryption.getStepNo(baseKey, binary, offset, size);

            // ステップコードをマスキングしてメンバー変数にセット.
            this.encryptonStepCode = step & Encryption.CHECK_STEPMASK;

            // ステップコードを利用可能な状態に変換.
            step = step & Encryption.STEP_MASK;

            // 暗号キー( 256 )をデフォルトキーで変換.
            step = Encryption.cnv256(cd256, this.privateKey, step);

            // 指定バイナリの暗号処理.
            Encryption.cnvBinary(binary, offset, size, cd256, step);

        } catch (RuntimeException e) {
            throw e;
        } finally {

            baseKey = null;
            cd32 = null;
            cd256 = null;
            code32Key = null;

        }

        return this.encryptonStepCode;
    }

    /**
     * 解析処理. <BR>
     * <BR>
     * 暗号化された情報を解析します. <BR>
     * 
     * @param key
     *            対象のパブリックキー情報を設定します.
     * @param step
     *            暗号ステップコードを設定します.<BR>
     *            この情報は、暗号処理時に生成されたステップコード値を設定する 必要があります.
     * @param binary
     *            暗号対象のバイナリリソースを設定します.
     * @param offset
     *            暗号化開始位置を設定します.
     * @param size
     *            暗号化開始位置[ offset ]から暗号するサイズを設定します.
     * @exception Exception
     *                例外.
     */
    public final void analysis(byte[] key, int step, byte[] binary, int offset,
            int size) throws Exception {

        int checkStep, setStep;

        byte[] code32Key = null;
        byte[] cd32 = null;
        byte[] cd256 = null;

        if (key == null || binary == null || offset < 0 || size < 0
                || binary.length < offset + size) {
            throw new IllegalArgumentException("引数は不正です");
        }

        code32Key = this.publicKey;

        try {

            // Encryptionキーをメンバー変数に登録.
            System.arraycopy(key, 0, code32Key, 0,
                    Encryption.ENCRYPION_KEY_LENGTH);

            // 暗号キーをデフォルトキーで変換.
            cd32 = Encryption.encryptionEncryptionKey(code32Key,
                    this.privateKey);

            // 暗号キーを256バイトに引き伸ばす.
            cd256 = Encryption.code32To256(cd32);
            cd32 = null;

            // セットステップコードをマスク.
            setStep = step & Encryption.CHECK_STEPMASK;

            // ステップコードを利用可能な状態に変換.
            step = step & Encryption.STEP_MASK;

            // 暗号キー( 256 )をデフォルトキーで変換.
            step = Encryption.cnv256(cd256, this.privateKey, step);

            // 指定バイナリの暗号解析処理.
            Encryption.cnvBinary(binary, offset, size, cd256, step);

            // チェック用ステップコードの生成.
            checkStep = Encryption.getStepNo(key, binary, offset, size);

            cd32 = null;
            cd256 = null;

            // 指定ステップコードが、解析結果のステップコードと一致しない場合.
            if (setStep != (checkStep & Encryption.CHECK_STEPMASK)) {

                // 一致しない場合は、情報内容が不正である.
                throw new IOException("解析対象条件(setStep:" + setStep
                        + " : checkStep:" + checkStep + ")は一致しません");

            }

        } catch (Exception e) {
            throw e;
        } finally {

            cd32 = null;
            cd256 = null;

        }

        // ステップコードをメンバー変数にセット.
        this.encryptonStepCode = setStep;

    }

    /**
     * ユーザ名／パスワード情報から、Keyを生成. <BR>
     * <BR>
     * ユーザ名／パスワード情報から、Key情報を生成します. <BR>
     * 
     * @param user
     *            ユーザ名を指定します.
     * @param passwd
     *            パスワード情報を指定します.
     * @return byte[] 生成されたKeyが返されます.
     * @exception Exception
     *                例外.
     */
    public static final byte[] getUserPasswdByKey(String user, String passwd)
            throws Exception {

        byte[] ret = null;

        if (user == null) {
            throw new IllegalArgumentException("引数は不正です");
        }

        try {

            StringBuffer buf = new StringBuffer();
            buf.append("-uSer+");
            buf.append(user);
            buf.append("+PasSwD-");
            buf.append(((passwd == null || passwd.length() <= 0) ? "nOpASSwoRd"
                    : passwd));

            ret = Encryption.convertStringByCode32Key(buf.toString());

        } catch (Exception e) {
            throw e;
        }

        return ret;
    }

    /**
     * 指定文字列から、key情報を生成. <BR>
     * <BR>
     * 指定された文字列から、key情報を生成します.<BR>
     * また、文字列はAsciiコードである必要があります. <BR>
     * 
     * @param string
     *            変換対象の文字列を設定します.
     * @return byte[] 変換されたkeyコードが返されます.
     * @exception Exception
     *                例外.
     */
    public static final byte[] convertStringByCode32Key(String string)
            throws Exception {

        int i, j, k;
        int before;
        int beforeTmp;
        int stepRoop;
        int len;
        int stepLen;
        int stepMask;
        int originStep;
        int step;

        int next;
        int tmp;
        int tmpBin;

        int[] mask_ary;
        byte[] code = null;
        byte[] ret = null;

        if (string == null || string.length() <= 0) {
            throw new IllegalArgumentException("引数は不正です");
        }

        mask_ary = MASK_ARRAY;

        try {

            code = Digest.convertBinary(SHA1_DIGEST, string.getBytes(CHARSET));
            code = Encryption
                    .convertBinaryToUnicodeBinary(code, 0, code.length);

            len = code.length;
            step = Encryption.getStepNo(code, code, 0, len);
            step = ((len & STEP_IF_MASK) != 0) ? step - len : step + len;
            originStep = (step & 0x000000ff);

            ret = new byte[Encryption.ENCRYPION_KEY_LENGTH];
            System.arraycopy(Encryption.BACK_PRIVATE_KEY, 0, ret, 0,
                    Encryption.ENCRYPION_KEY_LENGTH);
            for (i = 0; i < Encryption.ENCRYPION_KEY_LENGTH; i++) {
                ret[i] = (byte) (((ret[i] & 0x000000ff) + step + i) & 0x000000ff);
            }
            stepLen = (step & 0x0000001f);
            stepMask = Encryption.ENCRYPION_KEY_LENGTH - 1;

            for (stepRoop = 0, before = originStep; stepRoop < stepLen; stepRoop++) {
                for (i = 0, j = 0, k = (stepRoop & stepMask), next = (step & 0x00000007), tmp = 0; i < len; i++, j++, k++) {

                    j &= 0x00000007;
                    k = mask_ary[k];

                    switch (j) {

                    case 0:
                        next += (int) ((~code[i]) & 0x000000fe);
                        tmp = ((((next + 1) + (~step)) & 0x00000020) >> 4);
                        break;
                    case 1:
                        next += (int) ((code[i]) & 0x000000df);
                        tmp = ((((next) ^ originStep) & 0x000000060) >> 5);
                        break;
                    case 2:
                        next += (int) ((~code[i]) & 0x0000007f);
                        tmp = ((((next + 1) + (~step)) & 0x0000018) >> 3);
                        break;
                    case 3:
                        next += (int) ((~code[i]) & 0x000000fb);
                        tmp = (((next) ^ step) & 0x00000002);
                        break;
                    case 4:
                        next += (int) ((code[i]) & 0x000000ef);
                        tmp = ((((next + 1) + (~originStep)) & 0x0000000c0) >> 6);
                        break;
                    case 5:
                        next += (int) ((~code[i]) & 0x000000bf);
                        tmp = ((((next) + step) & 0x00000006) >> 1);
                        break;
                    case 6:
                        next += (int) ((code[i]) & 0x000000f7);
                        tmp = ((((next + 1) + (~step)) & 0x0000000c) >> 2);
                        break;
                    case 7:
                        next += (int) ((~code[i]) & 0x000000fd);
                        tmp = ((((next) + originStep) & 0x00000020) >> 4);
                        break;

                    }

                    step = (step & next) & 0x00000007;
                    tmpBin = (byte) Encryption.flipCode(
                            (byte) (next & 0x000000ff), step);

                    switch (tmp) {

                    case 0:
                        ret[k] += (byte) ((((~ret[k]) & 0x000000ff) + (~tmpBin)) & 0x000000ff);
                        break;
                    case 1:
                        ret[k] += (byte) ((((ret[k] & 0x000000ff) ^ tmpBin) + originStep) & 0x000000ff);
                        break;
                    case 2:
                        ret[k] += (byte) ((ret[k] + (tmpBin & 0x000000ff)) & 0x000000ff);
                        break;
                    case 3:
                        ret[k] += (byte) (((ret[k] + ((~tmpBin) & 0x000000ff)) + originStep) & 0x000000ff);
                        break;

                    }

                    beforeTmp = before;
                    before = (int) (ret[k] & 0x000000ff);
                    ret[k] = (byte) ((((ret[k] & 0x000000ff) + beforeTmp) >> 1) & 0x000000ff);

                }

            }

        } catch (Exception e) {
            ret = null;
            throw e;
        } finally {
            code = null;
        }

        return ret;
    }

    /**
     * パブリックキーコードを発行. <BR>
     * <BR>
     * 新しいパブリックキーコードを発行します. <BR>
     * 
     * @return byte[] 発行されたパブリックキーコードを取得します.
     */
    public static final byte[] getPublicKey() {

        byte[] ret = null;

        ret = new byte[Encryption.ENCRYPION_KEY_LENGTH];
        Encryption.createPublicKey(ret);

        return ret;
    }

    /**
     * 設定されているプライベートキー情報の取得. <BR>
     * <BR>
     * 設定されているプライベートキー情報を取得します. <BR>
     * 
     * @return byte[] 設定されているプライベートキー情報が返されます.
     */
    public final byte[] getSettingPrivateKey() {
        return this.privateKey;
    }

    /**
     * 設定されているパブリックキー情報の取得. <BR>
     * <BR>
     * 設定されているパブリックキー情報を取得します. <BR>
     * 
     * @return byte[] 設定されているパブリックキー情報が返されます.
     */
    public final byte[] getSettingPublicKey() {
        return this.publicKey;
    }

    /**
     * 暗号ステップコード情報の取得. <BR>
     * <BR>
     * 暗号化されたときのステップコード情報を取得します.<BR>
     * この情報は、暗号の解析を行うときに必要となります. <BR>
     * 
     * @return int 設定されているステップコード値を取得します.<BR>
     *         暗号/解析処理を実施していない場合[Encryption.NOT_STEP]が返されます.
     */
    public final int getStepCode() {
        return this.encryptonStepCode;
    }

    /**
     * パブリックキーコードを発行.
     * @param cb32 暗号データを設定するバイナリを設定します.
     */
    public static final void createPublicKey(byte[] cd32) {
        int i;
        int len;
        len = Encryption.ENCRYPION_KEY_LENGTH;
        for (i = 0; i < len; i++) {
            cd32[i] = (byte) Encryption.random(0x00000100);
        }
    }

    /**
     * コンバート処理.
     */
    private static final byte convert(byte[] key, int no, byte pause) {
        switch ((no & 0x00000001)) {
        case 0:
            return (byte) (~(pause ^ key[no]));
        case 1:
            return (byte) (~(pause ^ key[no]));
        }
        return -1;
    }

    /**
     * 暗号キー( EncryptionKey )をデフォルトキーで暗号化.
     */
    private static final byte[] encryptionEncryptionKey(byte[] cd32,
            byte[] pri32Key) throws Exception {
        int i, j, k;
        int len;

        byte[] low = null;
        byte[] hight = null;
        byte[] ret = null;

        low = Digest.convertBinary(MD5_DIGEST, cd32);
        hight = Digest.convertBinary(SHA1_DIGEST, pri32Key);

        ret = new byte[Encryption.ENCRYPION_KEY_LENGTH];
        len = low.length;

        for (i = 0, j = 0, k = len - 1; i < len; i++, j += 2, k--) {
            ret[j] = Encryption.convert(low, i, cd32[j]);
            ret[j + 1] = Encryption.convert(hight, i, low[k]);
        }
        return ret;
    }

    /**
     * Encryptionキーコードを256バイト変換.
     */
    private static final byte[] code32To256(byte[] key) {

        int i, j, k;
        int len;
        int oneLen;
        int next;

        byte[] tmp = null;
        byte[] binary = null;
        MessageDigest md = null;

        try {

            // 32バイトデータを256バイトデータに引き伸ばす.
            binary = new byte[CODE32_TO_CODE256CNV];
            len = Encryption.ENCRYPION_KEY_LENGTH - 1;

            for (i = 1, k = 0, next = 0; i < len; i++) {

                next = ((key[i] << 8) & 0x0000ff00) | key[i + 1];

                binary[k] = (byte) (next & 0x000000ff);
                binary[k + 1] = (byte) (((next & 0x000001fe) >> 1) & 0x000000ff);
                binary[k + 2] = (byte) (((next & 0x000003fc) >> 2) & 0x000000ff);
                binary[k + 3] = (byte) (((next & 0x000007f8) >> 3) & 0x000000ff);
                binary[k + 4] = (byte) (((next & 0x00000ff0) >> 4) & 0x000000ff);
                binary[k + 5] = (byte) (((next & 0x00001fe0) >> 5) & 0x000000ff);
                binary[k + 6] = (byte) (((next & 0x00003fc0) >> 6) & 0x000000ff);
                binary[k + 7] = (byte) (((next & 0x00007f80) >> 7) & 0x000000ff);

                k += 8;

            }

            next = ((key[len] << 8) & 0x0000ff00) | key[0];

            binary[k] = (byte) (next & 0x000000ff);
            binary[k + 1] = (byte) (((next & 0x000001fe) >> 1) & 0x000000ff);
            binary[k + 2] = (byte) (((next & 0x000003fc) >> 2) & 0x000000ff);
            binary[k + 3] = (byte) (((next & 0x000007f8) >> 3) & 0x000000ff);
            binary[k + 4] = (byte) (((next & 0x00000ff0) >> 4) & 0x000000ff);
            binary[k + 5] = (byte) (((next & 0x00001fe0) >> 5) & 0x000000ff);
            binary[k + 6] = (byte) (((next & 0x00003fc0) >> 6) & 0x000000ff);
            binary[k + 7] = (byte) (((next & 0x00007f80) >> 7) & 0x000000ff);

            // 生成されたバイナリをMD5に変換.
            oneLen = Encryption.MD5_LENGTH;
            len = CODE32_TO_CODE256CNV;
            md = MessageDigest.getInstance(Encryption.MD5_DIGEST);

            for (i = 0, j = oneLen; j < len; i++) {

                md.update(binary, j, oneLen);

                tmp = md.digest();
                oneLen = tmp.length;
                oneLen = (j + oneLen >= len) ? len - j : oneLen;
                System.arraycopy(tmp, 0, binary, j, oneLen);
                md.reset();

                j += oneLen;

            }

        } catch (Exception e) {
            e.printStackTrace();
            binary = null;
        } finally {
            md = null;
        }

        return binary;
    }

    /**
     * 対象バイナリ情報をUnicode変換. [ binary ] -> [ string 注１ ] -> [ unicode( byte ) ]..
     * 注１: 文字列に変換する方法として、以下のような方法を 用いています.. binary [ 0x01 ] [ 0x02 ] [ 0x03
     * ]..... string [ 010203..... ]
     */
    private static final byte[] convertBinaryToUnicodeBinary(byte[] binary,
            int offset, int size) throws Exception {
        String tmp = Encryption.convertBinaryTo16String(binary, offset, size);
        return tmp.getBytes(Encryption.CHARSET);
    }

    /**
     * ステップNo情報の取得.
     */
    private static final int getStepNo(byte[] publicKey, byte[] binary,
            int offset, int size) {
        int i, j;
        int addCd;
        int len;

        int bin;
        int ret;

        ret = 0;
        len = offset + size;

        addCd = (int) ((publicKey[(int) (binary[offset + (size / 2)] & 0x0000001f)] & 0x00000003) + 1);

        for (i = offset, j = 0; i < len; i += addCd, j += addCd) {

            bin = (int) ((~binary[i]) & 0x000000ff);

            ret = ((bin & 0x00000001) + ((bin & 0x00000002) >> 1)
                    + ((bin & 0x00000004) >> 2) + ((bin & 0x00000008) >> 3)
                    + ((bin & 0x00000010) >> 4) + ((bin & 0x00000020) >> 5)
                    + ((bin & 0x00000040) >> 6) + ((bin & 0x00000080) >> 7))
                    + (j & 0x000000ff) + ret;

        }

        if ((ret & 0x00000001) == 0) {

            for (i = 0; i < Encryption.ENCRYPION_KEY_LENGTH; i++) {

                bin = (int) (((publicKey[i] & 0x00000001) == 0) ? ((~publicKey[i]) & 0x000000ff)
                        : (publicKey[i] & 0x000000ff));

                ret += ((bin & 0x00000001) + ((bin & 0x00000002) >> 1)
                        + ((bin & 0x00000004) >> 2) + ((bin & 0x00000008) >> 3)
                        + ((bin & 0x00000010) >> 4) + ((bin & 0x00000020) >> 5)
                        + ((bin & 0x00000040) >> 6) + ((bin & 0x00000080) >> 7));

            }

        } else {

            for (i = 0; i < Encryption.ENCRYPION_KEY_LENGTH; i++) {

                bin = (int) (((publicKey[i] & 0x00000001) == 0) ? ((~publicKey[i]) & 0x000000ff)
                        : (publicKey[i] & 0x000000ff));

                ret -= ((bin & 0x00000001) + ((bin & 0x00000002) >> 1)
                        + ((bin & 0x00000004) >> 2) + ((bin & 0x00000008) >> 3)
                        + ((bin & 0x00000010) >> 4) + ((bin & 0x00000020) >> 5)
                        + ((bin & 0x00000040) >> 6) + ((bin & 0x00000080) >> 7));

            }

        }

        return (int) (((~ret) | 0x00000080) & 0x000000ff);
    }

    /**
     * 生成256コード加工処理.
     */
    private static final int cnv256(byte[] code256, byte[] def32, int step) {
        int i, j;
        int len;
        int nowStep;

        nowStep = step & 0x0000007f;
        len = Encryption.CODE32_TO_CODE256CNV;

        for (i = 0, j = 0; i < len; i++, j = ((j + 1) & 0x0000001f)) {

            nowStep = (nowStep & (int) code256[i]) & 0x00000007;
            code256[i] = (byte) Encryption.flipCode((int) code256[i], nowStep);
            code256[i] = Encryption.convert(def32, j, code256[i]);

        }
        return nowStep;
    }

    /**
     * 指定バイナリ情報の加工.
     */
    private static final void cnvBinary(byte[] binary, int offset, int size,
            byte[] code256, int step) {

        int i, j;
        int len;
        int nowStep;

        nowStep = step;
        len = offset + size;

        for (i = offset, j = 0; i < len; i++, j = ((j + 1) & 0x000000ff)) {

            nowStep = ((nowStep & (int) (code256[j] & 0x000000ff)) & 0x00000007);
            binary[i] = (byte) Encryption.flipCode((byte) binary[i], nowStep);
            binary[i] = Encryption.convert(code256, j, (byte) binary[i]);

        }
    }

    /**
     * 指定バイト情報のフリップ処理. <BR>
     * <BR>
     * 指定バイト情報に対してステップ番号に対するフリップ処理を行います. <BR>
     * 
     * @param pause
     *            フリップ対象の指定バイト情報を設定します.
     * @param step
     *            フリップに対するステップコードを指定します.
     * @return byte フリップされた情報が格納されます.
     */
    protected static final int flipCode(int pause, int step) {
        int ret;

        step = (step & 0x00000007);

        switch (step) {
        case 1:
            ret = ((((pause & 0x00000003) << 6) & 0x000000c0) | (((pause & 0x000000fc) >> 2) & 0x0000003f));
            break;
        case 2:
            ret = ((((pause & 0x0000003f) << 2) & 0x000000fc) | (((pause & 0x000000c0) >> 6) & 0x00000003));
            break;
        case 3:
            ret = ((((pause & 0x00000001) << 7) & 0x00000080) | (((pause & 0x000000fe) >> 1) & 0x0000007f));
            break;
        case 4:
            ret = ((((pause & 0x0000000f) << 4) & 0x000000f0) | (((pause & 0x000000f0) >> 4) & 0x0000000f));
            break;
        case 5:
            ret = ((((pause & 0x0000007f) << 1) & 0x000000fe) | (((pause & 0x00000080) >> 7) & 0x00000001));
            break;
        case 6:
            ret = ((((pause & 0x00000007) << 5) & 0x000000e0) | (((pause & 0x000000f8) >> 3) & 0x0000001f));
            break;
        case 7:
            ret = ((((pause & 0x0000001f) << 3) & 0x000000f8) | (((pause & 0x000000e0) >> 5) & 0x00000007));
            break;
        default:
            ret = pause;
            break;
        }
        return ret;
    }

    /**
     * 対象の４バイトコード(0x3f->6Bit)に対して、ビット配列を逆転. <BR>
     * <BR>
     * 対象の４バイトコード(0x3f->6Bit)に対して、ビット配列を逆転させます. <BR>
     * 
     * @param byte4
     *            対象の条件を設定します.
     * @return int 変換された条件が返されます.
     */
    protected static final int flipBit0x3f(int byte4) {
        return ((byte4 & 0x00000040) | (byte4 & 0x00000080)
                | (((byte4 & 0x00000001) << 5) & 0x00000020)
                | (((byte4 & 0x00000002) << 3) & 0x00000010)
                | (((byte4 & 0x00000004) << 1) & 0x00000008)
                | (((byte4 & 0x00000008) >> 1) & 0x00000004)
                | (((byte4 & 0x00000010) >> 3) & 0x00000002) | (((byte4 & 0x00000020) >> 5) & 0x00000001));
    }

    /**
     * 対象の４バイトコード(0x3c->8Bit)に対して、ビット配列を逆転. <BR>
     * <BR>
     * 対象の４バイトコード(0x3c->8Bit)に対して、ビット配列を逆転させます. <BR>
     * 
     * @param byte4
     *            対象の条件を設定します.
     * @return int 変換された条件が返されます.
     */
    protected static final int flipBit0x3c(int byte4) {
        return ((byte4 & 0x00000001) | (byte4 & 0x00000002)
                | (byte4 & 0x00000040) | (byte4 & 0x00000080)
                | (((byte4 & 0x00000004) << 3) & 0x00000020)
                | (((byte4 & 0x00000008) << 1) & 0x00000010)
                | (((byte4 & 0x00000010) >> 1) & 0x00000008) | (((byte4 & 0x00000020) >> 3) & 0x00000004));
    }

    /**
     * 対象の４バイトコード(0xc3->8Bit)に対して、ビット配列を逆転. <BR>
     * <BR>
     * 対象の４バイトコード(0xc3->8Bit)に対して、ビット配列を逆転させます. <BR>
     * 
     * @param byte4
     *            対象の条件を設定します.
     * @return int 変換された条件が返されます.
     */
    protected static final int flipBit0xc3(int byte4) {
        return ((byte4 & 0x00000004) | (byte4 & 0x00000008)
                | (byte4 & 0x00000010) | (byte4 & 0x00000020)
                | (((byte4 & 0x00000001) << 7) & 0x00000080)
                | (((byte4 & 0x00000002) << 5) & 0x00000040)
                | (((byte4 & 0x00000040) >> 5) & 0x00000002) | (((byte4 & 0x00000080) >> 7) & 0x00000001));
    }

    /**
     * 対象の４バイトコード(0xff->8Bit)に対して、ビット配列を逆転. <BR>
     * <BR>
     * 対象の４バイトコード(0xff->8Bit)に対して、ビット配列を逆転させます. <BR>
     * 
     * @param byte4
     *            対象の条件を設定します.
     * @return int 変換された条件が返されます.
     */
    protected static final int flipBit0xff(int byte4) {
        return ((((byte4 & 0x00000001) << 7) & 0x00000080)
                | (((byte4 & 0x00000002) << 5) & 0x00000040)
                | (((byte4 & 0x00000004) << 3) & 0x00000020)
                | (((byte4 & 0x00000008) << 1) & 0x00000010)
                | (((byte4 & 0x00000010) >> 1) & 0x00000008)
                | (((byte4 & 0x00000020) >> 3) & 0x00000004)
                | (((byte4 & 0x00000040) >> 5) & 0x00000002) | (((byte4 & 0x00000080) >> 7) & 0x00000001));
    }

    /**
     * 対象のバイナリ情報を16進数に変換. <BR>
     * <BR>
     * 対象のバイナリ情報を16進数情報に変換します. <BR>
     * 
     * @param resource
     *            対象のバイナリ情報を設定します.
     * @param offset
     *            対象のオフセット情報値を設定します.
     * @param length
     *            対象の情報長を設定します.
     * @return String 変換された情報が返されます.
     */
    private static final String convertBinaryTo16String(byte[] binary,
            int offset, int length) {

        int i;
        int len;

        int tmp;

        char[] cnv = null;
        StringBuffer buf = null;
        String ret = null;

        try {

            cnv = Encryption.convert10To16;
            buf = new StringBuffer();

            len = offset + length;
            for (i = offset; i < len; i++) {

                tmp = (int) (binary[i] & 0x000000ff);
                buf.append(cnv[((tmp & 0x000000f0) >> 4)]);
                buf.append(cnv[(tmp & 0x0000000f)]);

            }

            ret = buf.toString();

        } catch (Exception e) {
            ret = " ";
        } finally {

            buf = null;

        }

        return ret;

    }

    /**
     * ランダムな値を取得. <BR>
     * <BR>
     * ランダムな値を取得します. <BR>
     * 
     * @param max
     *            ランダム値の最大番号を指定します。
     * @return int 上記指定した情報のランダム値を取得します。
     */
    public static final int random(int max) {
        int ret;

        synchronized (RAND) {

            ret = (int) RAND.nextInt(max);

        }

        return ret;
    }

}
