JavaでSHA-1とか使って暗号化する

liquidfunc2010-09-30

登録制のWebアプリケーションって、たぶんユーザーのパスワードを暗号化してDBに格納すると思うんですよ。そのためには、SHA-1みたいなアルゴリズムで暗号化するのが一般的(?)だと思うので、そのサンプルコードです。
main文の中に全部つっこんで書いた方が、ブログ閲覧者的には理解しやすいんだろうけど、実際はたぶんこうやって使うんだろなって思ってクラスにしてやった。

サンプルコード

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/*
 * いわゆるハッシュ関数を使った暗号化クラス。
 */
public class Encryption {

	/* メッセージダイジェストアルゴリズム */
	public MessageDigest md = null;

	/*
	 * テストコード
	 */
	public static void main(String[] args) {
		final String algorithmName = "SHA-256";
		final String password = "p12345";

		Encryption e = new Encryption(algorithmName);
		byte[] bytes = e.toHashValue(password);
		String result = e.toEncryptedString(bytes);
		System.out.println(result);
		// 出力→ f63e7ba71de88a877e320318d0112356c381276dc6b6a58381726bef4bbc1481
	}

	/*
	 * 引数でメッセージダイジェストアルゴリズムを指定する。
	 *  MD2, MD5, SHA, SHA-256, SHA-384, SHA-512が利用可能。
	 */
	public Encryption(String algorithmName) {
		try {
			md = MessageDigest.getInstance(algorithmName);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
	}

	/*
	 * メッセージダイジェストアルゴリズムを使い、文字列をハッシュ値へ変換する。
	 */
	public byte[] toHashValue(String password) {
		md.update(password.getBytes());
		return md.digest();
	}

	/*
	 * バイト配列を16進数の文字列に変換し、連結して返す。
	 */
	public String toEncryptedString(byte[] bytes) {
		StringBuilder sb = new StringBuilder();
		for (byte b : bytes) {
			String hex = String.format("%02x", b);
			sb.append(hex);
		}
		return sb.toString();
	}

}

ポイント

強いてあげるなら、String.format("%02x", b) のとこ。
C言語のprintf関数みたいな記述法は、プログラマの基本素養だと思ってますが、JavaではJDK1.5から利用可能になりました。JavaAPIドキュメントではFormatterクラスの説明(API仕様リンク)を見ると良いです。
もしこれを使わない場合、Javaでダイジェストを生成する:JavaTips 〜Javaプログラミング編 - @ITみたいな負値の補正や0埋めするコードを書かなくちゃならないので、ちょっとだけ面倒ですね。

アルゴリズムはどれがいいの?

SHA-1って理論上はもう破られちゃうことがわかってるらしいですね。アメリカ政府でも2010年、つまり今年の年末からは新規案件でSHA-1を採用しないことに決まってるらしいです。
SHA-256やSHA-512っていう、もうちょっと強度のある第2世代のアルゴリズムがあって、しばらくはこれ使うことになるみたい。けど、これもやっぱり近い将来破られちゃうことがわかってる。コンピュータの進歩は凄まじいですからね。
実はいま絶賛コンペ中(リンク)で、第3世代(総称してSHA-3と呼ぶ)の実装を何にしようかっていう動きがあります。日本からは日立のLuffaというアルゴリズムが候補に残ってますね。
これが確定するのは2年後で、そっからはそれが世界標準となります(PDFリンク)。なので、当面はSHA-256使っとけばいいはずです。

復号は?

暗号化の対義語は復号(復号化とは言わないらしい)。
このサンプルコードはパスワードをSHA-256で暗号化してるんだけど、じゃあどうやって復号すんの?って思いますよね。ハッシュ関数なんだから、一方通行の暗号化です。そのまま復号ってのは不可能。
この辺は電子署名で用いられている公開鍵と秘密鍵を使ったなんちゃら(よくわかってない)な感じでやるんじゃないでしょうか。勉強しないと…!