Perl関数で暗号化 - MD5編
前章では、DES環境下でのcrypt関数について解説しましたが、本章ではMD5環境下でのcrypt関数について見ていきます。

3-1. デモ

今回のcrypt関数(MD5方式)のデモです。saltの8文字をアトランダムに付加し、パスワードを暗号化します。
pagetop

3-2. MD5方式の場合

MD5方式下でcrypt関数を使用する場合には、$1$ で始まる8文字($1$ を含めて11文字)以内のsalt(種)を用意します。
関数 備考
crypt(passwd, salt) passwd : 暗号化する文字列。8文字以内
salt : 英数字、ドット、スラッシュのいずれかの文字列で、0〜8文字を$1$〜$で囲む。
たとえば、saltを「12345678」とし、暗号化する文字列を「abcd」とします。
ちなみに、saltの末尾の「$」に限っては、これを省略しても構いません。
use strict;

# パスワード
my $passwd = 'abcd';

# salt
my $salt = '$1$12345678$';

# 暗号化
my $crypt = crypt($passwd, $salt);
print "$crypt\n";
>  $1$12345678$N/3zBznjFuq/Ila9YecQl.
上記の出力内容で、先頭の「$1$12345678$」までがsaltで、それ以降の「N/3zBznjFuq/Ila9YecQl.」が暗号化文字となります。 DES方式と比べて、文字列も長く、強度が増していることが分かります。
次に、saltの8文字を自動化するコード例は次のとおりです。
use strict;

# パスワード
my $passwd = 'abcd';

# saltの8文字をアトランダムに生成
my @str = (0 .. 9, 'a' .. 'z', 'A' .. 'Z', '.', '/');
my $salt;
for (1 .. 8) {
	$salt .= $str[int(rand(@str))];
}

# 暗号化
my $crypt = crypt($passwd, '$1$' . $salt . '$');
print "$crypt\n";
>  $1$IHnmFf/z$9numQmChmSOOO1mz8odh/1
上記のコード例では、任意の8文字「IHnmFf/z」がsaltになっていますが、起動するたびに異なったsaltが取得され、それに連動して出力結果(暗号文字)も異なります。
上記の暗号化コードを、さらに使いやすいようにサブルーチン化してみます。
use strict;

# パスワード
my $passwd = 'abcd';

# 暗号化
print &encrypt($passwd);

#-----------------------------------------------------------
#  MD5暗号化
#-----------------------------------------------------------
sub encrypt {
	my $passwd = shift;

	# salt生成
	my @str = (0 .. 9, 'a' .. 'z', 'A' .. 'Z', '.', '/');
	my $salt;
	for (1 .. 8) {
		$salt .= $str[int(rand(@str))];
	}

	# 暗号化
	return crypt($passwd, '$1$' . $salt);
}
pagetop

3-3. 暗号文字の照合

照合の仕方についてはDES方式と同じで、saltの取得方法が異なるだけです。
前章のときは substr関数で抜き出しましたが、今回はsaltの並び方がやや複雑なので、正規表現で抜き出してみましょう。
use strict;

# パスワード(元の文字列)
my $passwd = 'abcd';

# 暗号化された文字列
my $crypt = '$1$IHnmFf/z$9numQmChmSOOO1mz8odh/1';

# 暗号化された文字列からsaltを正規表現で取得
my $salt = $crypt =~ /^(\$1\$.*\$)/ && $1;

# 判定
if ($crypt eq crypt($passwd, $salt)) {
	print "OK\n";
} else {
	print "NG\n";
}
>  OK
無事に照合ができました。
最後に、もっと使いやすくするために、サブルーチン化します。
use strict;

# パスワード
my $passwd = 'abcd';

# 暗号文字
my $crypt = '$1$IHnmFf/z$9numQmChmSOOO1mz8odh/1';

# 照合
if (&decrypt($crypt, $passwd)) {
	print "OK\n";
} else {
	print "NG\n";
}

#-----------------------------------------------------------
#  MD5照合
#-----------------------------------------------------------
sub decrypt {
	my ($crypt, $plain) = @_;

	# salt抽出
	my $salt = $crypt =~ /^(\$1\$.*\$)/ && $1;

	# 判定
	return $crypt eq crypt($plain, $salt) ? 1 : 0;
}
pagetop