ハッシュ関数
ハッシュ関数 (Hash Function) とは任意長のバイナリデータから数十〜数百ビット程度の固定長バイナリを算出する関数です。古くから CRC のようなアルゴリズムが 誤り検出の用途で使用されてきましたが、暗号などのセキュリティで使用されるハッシュ関数はような特徴を持ちます。
- 同じバイナリデータに対して常に同じ値が算出される。
- 異なるバイナリデータに対して同じ値が算出される (衝突) 確率が極めて低い。
- 算出された値から元のバイナリデータの推測が極めて難しい。
最近ではマシンパフォーマンスの向上もあって、セキュリティの用途で使用できる強いハッシュ関数がバイナリデータの誤り検出や同一性検証 (インデックス付け) などにも使用されています。
ハッシュアルゴリズム
Java で使用できるハッシュアルゴリズムには Message Digest と SHA があります。誤り検出の用途であればツール等にも広く普及している MD5 (128bit)、セキュリティ的な用途では SHA-256 (256bit) 以上で十分と思われます (速度は大差ない)。
- MD5
- [128bit] Message Digest Algorithm 5: RSA と組み合わせて電子署名を行えるよう開発されたアルゴリズム。データの誤り検出からパスワードの保存まで広く使用されていますが、現在では (緊急性はないものの) いくつかの脆弱性が報告されているためセキュリティの用途では SHA を使用した方が良い。
- SHA
- [160,224,256,384,512bit] Secure Hash Algorithm: SHA-1 (160bit), SHA-224〜SHA-512 まで存在するアルゴリズム。MD5 より攻撃に強いと言われ SSL, SSH や IPSec などにも使用されている。
ハッシュ値の生成
ハッシュ値はアルゴリズムを指定した MessageDigestにバイト配列を与えるだけで取得できます。
byte[] binary = "hello, world".getBytes(); MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] digest = md.digest(binary);
対象のバイナリが大きい場合は update() メソッドを使用することで内容を何度かに分けて更新することができます。
InputStream in = // ... MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] buffer = new byte[1024]; while(true){ int len = in.read(buffer); if(len < 0) break; md.update(buffer, 0, len); } byte[] digest = md.digest();
DigestInputStream,DigestOutputStream クラスを使用することでストリームに入出力されるデータから透過的にハッシュ値を算出することができます。
InputStream in = // ... MessageDigest md = MessageDigest.getInstance("SHA-256"); in = new DigestInputStream(in, md); byte[] buffer = new byte[1024]; while(true){ int len = in.read(buffer); if(len < 0) break; } byte[] digest = md.digest();