top / index / prev / next / target / source
日記形式でつづる いがぴょんコラム ウェブページです。
Javaで トリプルDES (Triple DES) アルゴリズムによるファイル暗号化ツールのトイプログラムを簡単に作ってみました。
Javaで トリプルDES (Triple DES) アルゴリズムによるファイル暗号化ツールのトイプログラムを簡単に作ってみました。これのソースコードをメモしておきます。
ポイント
JDK 5.0 のAPIのみで構成
暗号化鍵を SHA-256ダイジェスト値で作成 短い暗号化鍵の種の種を入力したとしても、ダイジェスト値採用によって十分なビット長を得ることが出来ます。
パスワードを間違った場合には無警告で正常終了 総当りによるパスワードクラックのリスクを低減化するために必要なことだと私は考えました。
トリプルDESアルゴリズム採用 AESは Java 6以降での提供になっています。JDK 5.0が全盛の現状では トリプルDESアルゴリズムの採用が妥当と考えました。
TODO: ファイルが既に存在するときの復号化について、現在は強行するようになっています。 これは、異常終了扱いとするのが良いかもしれいません。 BlancoDesede.java
/**
* トリプル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 Framework の blancoEncryptionへと発展しました。