ハッシュ関数

ハッシュ関数は入力データから、その入力と全く異なる内容のあるビット幅となる出力を算出する関数です。
そして、その出力データから入力データを求めることができない(復元できない)ように作られています。 この性質は「一方向性」と呼ばれます。
またハッシュ関数は、異なる入力データで同じ出力が得られる可能性が理論的に存在します。 この状態は衝突と呼ばれますが、 この衝突が起きる異なった入力を見つけることが、極めて困難になるよう作られます。 この性質は「衝突困難性」と呼ばれます。
セキュリティで使われるハッシュ関数は、「高速」で、しかもこの2つの性質 (「一方向性」、「衝突困難性」) を 満足する目標でアルゴリズムが作られます。以下に代表的なハッシュ関数を挙げます

アルゴリズム名出力ビット幅概要
MD5128bit Message Digest Algorithm 5の略です。RSA社で開発され、RFC1321に記される。1996年に衝突の脆弱性が判明している
SHA-1
160bit SECURE HASH STANDARDの略です。アメリカ 国家安全保障局(NSA)によって開発され、 政府標準として使われています。2010年より次のSHA-2へ移行している。
SHA-2
224〜512bit SHA-224、SHA-256、SHA-384、SHA-512の総称

これらセキュリティ用のハッシュ関数は、 入力データとなるメッセージの特徴を要約するように 計算する関数なので要約関数と呼ばれることもあります。
また、このハッシュ値をメッセージダイジェスト (message digest) と 呼ぶことがあります。
このメッセージダイジェストは次のように利用されます。

 ハッシュ関数の実験


次のソースをコピーし、 HASH.htaの名前で保存することで、 MD5とSHA-1の ハッシュ関数でテキストエリア内データのハシュ値を得る実験ができます。

<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<meta http-equiv="Content-Style-Type" content="text/css">
<meta http-equiv="Content-Script-Type" content="text/javascript">
<style type="text/css">
 p, input { font-size: 12pt; font-family: monospace; }
</style>

<script language="javascript">

var doc = document;
var hash_name ="";

function getFile(){
	var path = doc.getElementById("file1").value;
	if(path == ""){
		alert("ファイルを参照ボタンで選んでください。");
		return;
	}
	var fso = new ActiveXObject("Scripting.FileSystemObject");
	var tf = fso.OpenTextFile(path, 1, true);
	var fileData = tf.ReadAll();
	tf.Close();
	doc.getElementById("txtarea1").value = fileData;
}

function getMD5(){
	//テキストエリアの内容を「texta1」に記憶後、バイト列に変換し「bytes」に記憶
	var texta1 = doc.getElementById("txtarea1").value;
	//alert(texta1);
	var e = new ActiveXObject("System.Text.UTF8Encoding");
	//スクリプトから.Net側メソッドを呼び出し時、オーバーロードの自動的呼び出しをしない。
	var bytes = e.GetBytes_4(texta1);//『_4』の追加で、強制的に指定のオーバーロードメソッドを呼び出す裏技

	//ハッシュ値のバイト配列を「sha1」に求める
	var md5 = new ActiveXObject("System.Security.Cryptography.MD5CryptoServiceProvider");
	md5.ComputeHash_2(bytes);

	//バイト配列から文字列を生成して、テキストへ表示設定
	var str = new ActiveXObject("System.IO.MemoryStream");
	str.Write(md5.Hash, 0, 16);
	str.Position = 0;
	var sb = new ActiveXObject("System.Text.StringBuilder");
	var cnt = 0;
	while(cnt < 16){//128bit分のバイト列を、byteごと16進文字列へ変換しながら「sb]に文字列へ追加
 		sb.AppendFormat("{0:x}", str.readByte());
 		cnt++;
	}
	doc.getElementById("text1").value = sb.ToString();
	hash_name="MD5";
}

function getSHA1()
{
	//テキストエリアの内容を「texta1」に記憶後、バイト列に変換し「bytes」に記憶
	var texta1 = doc.getElementById("txtarea1").value;
	//alert(texta1);
	var e = new ActiveXObject("System.Text.UTF8Encoding");
	//スクリプトから.Net側メソッドを呼び出し時、オーバーロードの自動的呼び出しをしない。
	var bytes = e.GetBytes_4(texta1);//『_4』の指定で、強制的に指定のオーバーロードメソッドを呼び出す裏技

	//ハッシュ値のバイト配列を「sha1」に求める
	var sha1 = new ActiveXObject("System.Security.Cryptography.SHA1CryptoServiceProvider");
	sha1.ComputeHash_2(bytes);

	//バイト配列から文字列を生成して、テキストへ表示設定
	var str = new ActiveXObject("System.IO.MemoryStream");
	str.Write(sha1.Hash, 0, 20);
	str.Position = 0;
	var sb = new ActiveXObject("System.Text.StringBuilder");
	var cnt = 0;
	while(cnt < 20){//160bit分のバイト列を、byteごと16進文字列へ変換しながら「sb]に文字列へ追加
 		sb.AppendFormat("{0:x}", str.readByte());
 		cnt++;
	}
	doc.getElementById("text1").value = sb.ToString();
	hash_name="SHA1";
}

function saveFile() {
	var path = doc.getElementById("file1").value;
	if(path == ""){
		alert("最上部のファイルに名前を入れてください。");
		return;
	}
	path = path + "_" + hash_name + ".txt";
	var data = doc.getElementById("text1").value;
	if(data == ""){
		alert("ハッシュを算出ください。");
		return;
	}

	var fso = new ActiveXObject("Scripting.FileSystemObject");
	var a = fso.CreateTextFile(path, true);
	a.WriteLine( data );
	a.Close();
	alert(path + "の名前で作成しました。");
}

</script>
</head>

<body>

<p>
ファイルパス<input type="file" size="50" id="file1"><br>
上記パスのファイルを読み取り下記へ表示<input type="button" value="ロード" onclick="getFile()">
<br>
<textarea name="txtarea1" rows="20" cols="80" >abc

	wxyz</textarea><br>

上記テキストエリアのハッシュ値を計算して下記へ表示
<input type="button" value="MD5" onclick="getMD5()">
<input type="button" value="SHA1" onclick="getSHA1()"><br>

ハッシュ値<input type="text" size="60" id="text1"><br>
<input type="button" value="テキスト保存" onclick="saveFile()">


</p>
使っているActiveXのドキュメント<br>
<a href="http://msdn.microsoft.com/ja-jp/library/cc409798.aspx" target="FileSystemObject">
FileSystemObject参照</a><br>

<a href="http://msdn.microsoft.com/ja-jp/library/system.text.utf8encoding(VS.71).aspx" target="ADO">
UTF8Encoding参考</a><br>

<a href="http://msdn.microsoft.com/ja-jp/library/system.text.stringbuilder.aspx">
StringBuilder参考</a><br>

</body>
</html>

 ハッシュ関数の実験2 Javaプログラム

次のJavaのプログラムで任意のファイルの ハッシュ値を得る実験ができます。
このクラスは、ここよりダウンロードして実験できます。

import java.io.*;
import java.util.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.swing.JFileChooser;//ファイルオープンダイアログ用

public class Hash {
	static final String MD5 = "MD5";
	static final String SHA1 = "SHA-1";
	static final String SHA256 = "SHA-256";
	static final String SHA384 = "SHA-384";
	static final String SHA512 = "SHA-512";

	public static void main(String []z) throws Exception{
		String []a = {MD5,SHA1,SHA256,SHA384,SHA512};
		Scanner sc = new Scanner(System.in);
		String algo =null;
		while(true){
			for(int i=0; i<a.length; i++){//アルゴリズム選択のメニュー表示
				System.out.print(i+1+":"+a[i] +" ");
			}
			System.out.print(">");
			String op = sc.next(); 
			if(op.length() == 0) continue;
			int idx = op.charAt(0)-'0'-1;
			if(idx >=0 && idx < a.length) {
				algo = a[idx];
				break;
			}
		}
		MessageDigest md;
		try {
			md = MessageDigest.getInstance( algo ); //アルゴリズムの設定
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		}

		System.out.println(md.getAlgorithm()+"のハッシュアルゴリズムを使用します。");

		javax.swing.JFileChooser chooser = new javax.swing.JFileChooser(new File("."));
		chooser.setDialogTitle("対象のファイル選択ください。");
		if (chooser.showOpenDialog(null) == JFileChooser.CANCEL_OPTION)
			System.exit(0);
		String filepath = chooser.getSelectedFile().getPath();//ファイルパス
		File file = new File(filepath);
		int  size = (int)file.length();
		byte bi[] = new byte[size];//ファイルサイズのbyte配列を用意。
		FileInputStream is = new FileInputStream(filepath);
		is.read(bi);//ファイルバイナリーを一括読み取る。
		is.close();	//ファイルを閉じる。
		System.out.println(filepath + "のハッシュ値を求めます。");
		md.update(bi);
		byte[] hash = md.digest();//ハッシュ値を得る
		for(int i=0; i < hash.length; i++){//表示
			System.out.print(Integer.toString( (0x00ff & hash[i]) , 16) );
		}
		System.out.println();
	}
}