Perlモジュールで暗号化 - Digest::MD5編
これまでは、crypt関数による暗号化について解説しました。
crypt関数による暗号化方式の場合、不可逆式に限定され、さらにDES/MD5方式についてもサーバー環境に依存するため、効率的な暗号処理が実現できませんでした。
そこで、本章ではPerlモジュールを使った暗号化方式を紹介しましょう。Perlモジュールであれば、バラエティに富んだ数々の暗号形式のものが用意されていて、より使いやすいものから、より強固な暗号化形式まで、自由に利用することができます。
5-1. デモ
Digest::MD5のデモです。文字列をMD5形式で変換します。
5-2. Digest::MD5モジュール
Digest::MD5モジュールは、標準モジュール (*1) のため利用しやく、MD5形式のインターフェースをPerlから利用することができます。
さらにはバイナリ形式のものも扱うことができます。
暗号化用途以外にも、例えばチェックサム(誤り検出符号の一種)にも利用できますので、MD5形式でのユニークな文字列を生成するためのツールと理解しておいたほうがいいかもしれません。
暗号化用途以外にも、例えばチェックサム(誤り検出符号の一種)にも利用できますので、MD5形式でのユニークな文字列を生成するためのツールと理解しておいたほうがいいかもしれません。
それから、前章で解説したcrypt関数MD5方式の文字列とは直接互換性はありませんので、混同しないように注意してください。
あくまでもcrypt関数は文字列の暗号化のためのアルゴリズムにMD5方式を使用したというように理解しておきましょう。
Digest::MD5は、基本的には返ってくる値はバイナリ形式です。
そのため、そのままでは扱いにくいので、hex形式 (*2) か Base64形式 (*3) でテキストへ変換して使用するケースのほうが多いようです。
また、そのための関数も用意されています。
データ長は、バイナリ形式では16ビットです。また、hex形式だと32文字、Base64形式だと22文字となります。
基本構文は次のとおりです。
関数形式 |
use Digest::MD5 qw(md5 md5_hex md5_base64); $digest = md5($data); $digest = md5_hex($data); $digest = md5_base64($data); |
---|---|
OO形式 |
use Digest::MD5; $ctx = Digest::MD5->new; $ctx->add($data); $ctx->addfile(*FILE); $digest = $ctx->digest; $digest = $ctx->hexdigest; $digest = $ctx->b64digest; |
コード例は次のとおりです。文字列をMD5変換し、hex形式で出力する例です。
use strict; # モジュールを宣言 use Digest::MD5 qw(md5_hex); # 文字列 my $str = 'abcd'; # MD5変換(hex形式) my $digest = md5_hex($str); print "$digest\n";
> e2fc714c4727ee9395f324cd2e7f331f
hex形式なので、上記のとおり32文字で出力されました。
このようにして、テキスト1文字であれ、10MGに及ぶ動画ファイル(バイナリ)であれ、32文字のユニークな(それぞれが異なる)文字列で表現することができます。
このようにして、テキスト1文字であれ、10MGに及ぶ動画ファイル(バイナリ)であれ、32文字のユニークな(それぞれが異なる)文字列で表現することができます。
5-3. 暗号として使うには
前置きが長くなりました。
Digest::MD5を実際に暗号ツールとして利用する場合ですが、ここからは利用者の考え方だと思います。どこまで強度を図るかが問題です。
それほど強度を求めないケース(たとえば、個人の仲間内のみで使用するようなケース等)であれば、上記のコード例でもいいと思います。
しかしながら、多少強度を求めたい場合には、crypt関数の例のように、saltを利用すると良いでしょう。
それほど強度を求めないケース(たとえば、個人の仲間内のみで使用するようなケース等)であれば、上記のコード例でもいいと思います。
しかしながら、多少強度を求めたい場合には、crypt関数の例のように、saltを利用すると良いでしょう。
このsaltの扱い方には大きく2種類あり、1つは公開してしまう方法ともう1つは非公開で扱うケースがあります。
「公開」してしまう方法は、crypt関数での扱い方と同じで、パスワードの先頭や末尾に連結して保管する等の方法が一般的のようです。 つまり、暗号文字の一部にsaltを付加して保管することになります。
それに対して、「非公開」として扱うケースでは、むしろ第二のパスワードとして保管し、パスワードとは別にsaltが揃わないと暗号文字を決して復号・照合できないことになります。つまり、パスワードを2つに分けて保管するようなものです。銀行へお金を預けるときに、通帳と印鑑を分けて保管するようなイメージです。
「公開」してしまう方法は、crypt関数での扱い方と同じで、パスワードの先頭や末尾に連結して保管する等の方法が一般的のようです。 つまり、暗号文字の一部にsaltを付加して保管することになります。
それに対して、「非公開」として扱うケースでは、むしろ第二のパスワードとして保管し、パスワードとは別にsaltが揃わないと暗号文字を決して復号・照合できないことになります。つまり、パスワードを2つに分けて保管するようなものです。銀行へお金を預けるときに、通帳と印鑑を分けて保管するようなイメージです。
ポイント
saltの扱い方は、公開式と非公開式の2つがある。
5-4. 暗号/照合コード(hex出力)
今回は、salt文字を(crypt関数のように)公開することを前提に、次のようなルールとしましょう。
【ルール例】
(1) saltは8文字とする。
(2) hex文字列は、数字0〜8、英小文字a〜fの16文字なので、saltもそれに準ずる(その方が悟られにくい)。
(3) saltは暗号化文字列の先頭に連結する。
(1) saltは8文字とする。
(2) hex文字列は、数字0〜8、英小文字a〜fの16文字なので、saltもそれに準ずる(その方が悟られにくい)。
(3) saltは暗号化文字列の先頭に連結する。
saltを上記のルール例に従えば、暗号化コードは、次のように書くことができます。
use strict; # モジュールを宣言 use Digest::MD5 qw(md5_hex); # 文字列 my $passwd = '1234'; # saltの8文字を16進でアトランダムに生成 my @str = ('a' .. 'f', 0 .. 9); my $salt; for (1 .. 8) { $salt .= $str[int(rand(@str))]; } # MD5変換(hex形式) my $digest = $salt . md5_hex($salt . $passwd); print "$digest\n";
> 30a7f6a328cd0752669ae4768ffc99a2617e7877
上記の場合、先頭の8文字「
」がsalt、その後の文字列「 」が暗号文字になります。
実践的な場面を想定して、上記の暗号化コードをもっと使いやすいようにサブルーチン化してみます。
use strict; # モジュールを宣言 use Digest::MD5 qw(md5_hex); # 文字列 my $passwd = '1234'; # 暗号化 print &encrypt($passwd); #----------------------------------------------------------- # Digest::MD5 (hex) 暗号化 #----------------------------------------------------------- sub encrypt { my $plain = shift; # saltの8文字を16進でアトランダムに生成 my @str = ('a' .. 'f', 0 .. 9); my $salt; for (1 .. 8) { $salt .= $str[int(rand(@str))]; } # MD5変換(hex形式) return $salt . md5_hex($salt . $passwd); }
次に、照合処理です。
暗号文字の先頭の8文字を抜き出し、MD5変換して、照合することになります。
暗号文字の先頭の8文字を抜き出し、MD5変換して、照合することになります。
use strict; # モジュールを宣言 use Digest::MD5 qw(md5_hex); # 文字列 my $passwd = '1234'; # 暗号文字 my $crypt = '30a7f6a328cd0752669ae4768ffc99a2617e7877'; # saltは先頭の8文字を抜き出す my $salt = substr($crypt, 0, 8); # 判定 if ($crypt eq ($salt . md5_hex($salt . $passwd))) { print "OK\n"; } else { print "NG\n"; }
> OK
暗号/照合コードを、もっと実践的に使いやすいように、サブルーチン化してみましょう。
両方一緒に記述してみます(パスワードを暗号化し、それが元の文字列と正しいか照合します)。
両方一緒に記述してみます(パスワードを暗号化し、それが元の文字列と正しいか照合します)。
use strict; # モジュールを宣言 use Digest::MD5 qw(md5_hex); # 文字列 my $passwd = '1234'; # 暗号 my $crypt = &encrypt($passwd); print "$crypt\n"; # 照合 if (&decrypt($crypt, $passwd)) { print "OK\n"; } else { print "NG\n"; } #----------------------------------------------------------- # Digest::MD5 (hex) 暗号化 #----------------------------------------------------------- sub encrypt { my $plain = shift; # saltの8文字を16進でアトランダムに生成 my @str = ('a' .. 'f', 0 .. 9); my $salt; for (1 .. 8) { $salt .= $str[int(rand(@str))]; } # MD5変換(hex形式) return $salt . md5_hex($salt . $passwd); } #----------------------------------------------------------- # Digest::MD5 (hex) 照合 #----------------------------------------------------------- sub decrypt { my ($crypt, $plain) = @_; # saltは先頭の8文字を抜き出す my $salt = substr($crypt, 0, 8); # 照合 return $crypt eq ($salt . md5_hex($salt . $plain)) ? 1 : 0; }
> ace7beb0634cb29d7d098d96fcf08dc3b3eb94a6 > OK
5-5. 暗号/照合コード(Base64出力)
Digest::MD5を利用するにあたって、出力形式をBase64形式のほうがいいという方もいると思います。
その場合のコード例も考えてみます。異なるのはsaltの文字構成で、Base64の場合は「英数字」の他に「+」と「/」から構成されますので、暗号/照合コードは次のとおり記述できます。
その場合のコード例も考えてみます。異なるのはsaltの文字構成で、Base64の場合は「英数字」の他に「+」と「/」から構成されますので、暗号/照合コードは次のとおり記述できます。
use strict; # モジュールを宣言 use Digest::MD5 qw(md5_base64); # 文字列 my $passwd = '1234'; # 暗号 my $crypt = &encrypt($passwd); print "$crypt\n"; # 照合 if (&decrypt($crypt, $passwd)) { print "OK\n"; } else { print "NG\n"; } #----------------------------------------------------------- # Digest::MD5 (Base64) 暗号化 #----------------------------------------------------------- sub encrypt { my $plain = shift; # saltの8文字をアトランダムに生成 my @str = (0 .. 9, 'a' .. 'z', 'A' .. 'Z', '+', '/'); my $salt; for (1 .. 8) { $salt .= $str[int(rand(@str))]; } # MD5変換(Base64形式) return $salt . md5_base64($salt . $passwd); } #----------------------------------------------------------- # Digest::MD5 (Base64) 照合 #----------------------------------------------------------- sub decrypt { my ($crypt, $plain) = @_; # saltは先頭の8文字を抜き出す my $salt = substr($crypt, 0, 8); # 照合 return $crypt eq ($salt . md5_base64($salt . $plain)) ? 1 : 0; }
> 9x5vY9SppZCKXcUgchw6xZcjoZfUBQ > OK