top / index / prev / next / target / source

2007-05-10 diary: Java: サンプル: トリプルDES アルゴリズムによるファイル暗号化

いがぴょんの日記 日記形式でつづる いがぴょんコラム ウェブページです。

old-v2

Java: サンプル: トリプルDES アルゴリズムによるファイル暗号化

Javaで トリプルDES (Triple DES) アルゴリズムによるファイル暗号化ツールのトイプログラムを簡単に作ってみました。

Java: サンプル: トリプルDES アルゴリズムによるファイル暗号化

Javaで トリプルDES (Triple DES) アルゴリズムによるファイル暗号化ツールのトイプログラムを簡単に作ってみました。これのソースコードをメモしておきます。

ポイント

/**
 * トリプルDES (Triple DES) アルゴリズムによるファイル暗号化ツール。
 */
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;

/**
 * トリプルDES (Triple DES) アルゴリズムによるファイル暗号化ツール。
 * 
 * ファイルの拡張子により、暗号化/復号化の判断をおこなうようになっています。
 * 
 * @author 伊賀敏樹
 * @version 2007.05.10
 */
public class BlancoDesede {
    /**
     * 利用するアルゴリズム。
     */
    private static final String ALGORITHM = "DESede";

    /**
     * 暗号化後のファイルに付与される文字列。
     */
    private static final String ENCRYPTED_FILE_EXT = ".blancodesede";

    private static final String KEY_DIGEST_ALGORITHM = "SHA-256";

    /**
     * パスワード。一時的に記憶しておくためにこのフィールド変数を利用します。
     */
    private String fPass = null;

    /**
     * パスワードを設定します。
     * 
     * @param argPass
     */
    public void setPass(final String argPass) {
        fPass = argPass;
    }

    /**
     * このツールの使い方説明です。
     */
    private static final void usage() {
        System.out.println("Usage:");
        System.out.println("  暗号化: java BlancoDesede 入力ファイル password");
        System.out.println("  復号化: java BlancoDesede 入力ファイル"
                + ENCRYPTED_FILE_EXT + " password");
        System.out.println("※ファイル名には空白を含まないようにしてください。");
        System.out.println("※拡張子が [" + ENCRYPTED_FILE_EXT + "]"
                + " の場合には復号化モードと判断します。");
    }

    /**
     * このクラスのエントリポイント。
     * 
     * @param args
     */
    public static void main(final String[] args) {
        if (args.length < 2) {
            // 引数の数が足りません。
            usage();
            return;
        }

        final BlancoDesede angou = new BlancoDesede();
        angou.setPass(args[1]);

        final String inFileName = args[0];

        System.out.println("ファイルを処理します。入力[" + inFileName + "]");

        if (new File(inFileName).exists() == false) {
            System.out.println("指定のファイル[" + inFileName + "]は見つかりません。処理中断します。");
            return;
        }

        try {
            if (inFileName.endsWith(ENCRYPTED_FILE_EXT) == false) {
                System.out.println("  モード:暗号化");
                angou.encrypt(new File(inFileName), new File(inFileName
                        + ENCRYPTED_FILE_EXT));
            } else {
                System.out.println("  モード:復号化");
                angou.decrypt(new File(inFileName), new File(inFileName
                        .substring(0, inFileName.length()
                                - ENCRYPTED_FILE_EXT.length())));
            }

            System.out.println("処理完了。");
        } catch (IOException e) {
            System.out.println("入出力例外が発生しました。処理中断します。:" + e.toString());
            e.printStackTrace();
        }
    }

    /**
     * 与えられた文字列をもとに秘密鍵を作成します。
     * 
     * @param strKey
     *            秘密鍵の種となる文字列。
     * @return
     */
    private SecretKey getSecretKey(final String strKey) {
        // 鍵の種をダイジェスト化して鍵として利用します。
        // ポイント: SHA-256は32バイトあるので、DESedeで必要となる24バイトより長いので都合が良い。
        final byte[] digestedKey;
        try {
            final MessageDigest msgDigest = MessageDigest
                    .getInstance(KEY_DIGEST_ALGORITHM);
            msgDigest.update(strKey.getBytes());
            digestedKey = msgDigest.digest();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new IllegalArgumentException("ダイジェスト[" + KEY_DIGEST_ALGORITHM
                    + "]取得に失敗しました。:" + e.toString());
        }

        // 実際に利用するキーを作成します。
        final byte[] keySeed = new byte[24];
        for (int index = 0; index < keySeed.length; index++) {
            keySeed[index] = digestedKey[index];
        }

        try {
            final SecretKeyFactory factory = SecretKeyFactory
                    .getInstance(ALGORITHM);
            return factory.generateSecret(new DESedeKeySpec(keySeed));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new IllegalArgumentException("アルゴリズムの取得に失敗しました。:"
                    + e.toString());
        } catch (InvalidKeyException e) {
            e.printStackTrace();
            throw new IllegalArgumentException("秘密鍵が不正です。:" + e.toString());
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
            throw new IllegalArgumentException("秘密鍵の仕様が不正です。:" + e.toString());
        }
    }

    /**
     * 暗号化をおこないます。
     * 
     * @param inFile
     * @param outFile
     * @throws IOException
     */
    private void encrypt(final File inFile, final File outFile)
            throws IOException {
        final Cipher cipher;
        try {
            final InputStream inStream = new BufferedInputStream(
                    new FileInputStream(inFile));

            cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(fPass));

            final OutputStream outStream = new CipherOutputStream(
                    new BufferedOutputStream(new FileOutputStream(outFile)),
                    cipher);

            copy(inStream, outStream);

            outStream.flush();
            outStream.close();

            inStream.close();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new IllegalArgumentException("アルゴリズムの取得に失敗しました。:"
                    + e.toString());
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
            throw new IllegalArgumentException("パディング不正で失敗しました。:"
                    + e.toString());
        } catch (InvalidKeyException e) {
            e.printStackTrace();
            throw new IllegalArgumentException("秘密鍵不正で失敗しました。:" + e.toString());
        }
    }

    /**
     * 復号化を行います。
     * 
     * @param inFile
     * @param outFile
     * @throws IOException
     */
    private void decrypt(final File inFile, final File outFile)
            throws IOException {
        try {
            final Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, getSecretKey(fPass));
            final InputStream inStream = new CipherInputStream(
                    new BufferedInputStream(new FileInputStream(inFile)),
                    cipher);

            final OutputStream outStream = new BufferedOutputStream(
                    new FileOutputStream(outFile));

            copy(inStream, outStream);

            inStream.close();

            outStream.flush();
            outStream.close();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new IllegalArgumentException("アルゴリズムの取得に失敗しました。:"
                    + e.toString());
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
            throw new IllegalArgumentException("パディング不正で失敗しました。:"
                    + e.toString());
        } catch (InvalidKeyException e) {
            e.printStackTrace();
            throw new IllegalArgumentException("秘密鍵不正で失敗しました。:" + e.toString());
        }
    }

    /**
     * ストリームをコピーします。
     * 
     * @param inStream
     * @param outStream
     * @throws IOException
     */
    private void copy(final InputStream inStream, final OutputStream outStream)
            throws IOException {
        final byte[] byteBuf = new byte[1024];
        for (;;) {
            int length = inStream.read(byteBuf);
            if (length <= 0) {
                break;
            }
            outStream.write(byteBuf, 0, length);
        }
    }
}

2007.10.17追記 このソースコードは blanco FrameworkblancoEncryptionへと発展しました。


この日記について