ハッシュ関数 (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
にバイト配列を与えるだけで取得できます。
Java
byte[] binary = "hello, world".getBytes();
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] digest = md.digest(binary);
対象のバイナリが大きい場合は update() メソッドを使用することで内容を何度かに
分けて更新することができます。
Java
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
クラスを使用することでストリームに入出力されるデータから透過的にハッシュ値を算出することができます。
Java
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();
実行環境で使用できるハッシュアルゴリズムは
Security
クラスで列挙することができます。
Java
Set<String> algorithms = Security.getAlgorithms("MessageDigest");
for(String algorithm: algorithms) {
MessageDigest md = MessageDigest.getInstance(algorithm);
System.out.printf("%-7s: %dbit%n", algorithm, md.getDigestLength()*8);
}
Java SE 6 でのデフォルトの実行結果は以下の通り。
実行結果
SHA-256: 256bit
SHA-512: 512bit
SHA : 160bit
SHA-384: 384bit
MD5 : 128bit
MD2 : 128bit