ブログ記事

RADIUSにおけるPerlを用いたEAP-MSCHAPV2認証について③

2022/07/08

当ページをご覧いただきありがとうございます。

RADIUSにおけるPerlを用いたEAP-MSCHAPV2認証についての第3回となります。

第3回目では、「PerlでのEAP-MSCHAPV2認証の実装」について説明いたします。

 

【前提】

 RADIUSにおけるPerlを用いたEAP-MSCHAPV2認証について①②で構築した環境

 

【使用するソフトウェア・モジュール】

 ・FreeRADIUS (Version 3.0.13)

 ・eapol_test(Version 2.6)

 ・perl(Version 5.16.3)

 ・Sys::Syslog

 

【MSCHAPV2認証の仕組み】

 認証については文字で説明するのが難しいので下図をご確認ください。

 基本的にはClientとRadiusServer間で複数回のやり取りを行っており、それぞれで受け取った値を使用して

 特定の値を算出し、それをRadiusの属性値として送信することで認証を行っています。

 

【MSCHAPV2でのClient⇔Radiusサーバ間の認証処理】

 ※こちらは下記wikiを参考にPerl向けに分かりやすく書き直したものとなります。

  より詳細な内容は下記wikiをご確認ください。

https://en.wikipedia.org/wiki/File:MSCHAPv2_Flow.pdf

 

【Perlへの実装】

 では、上記をもとにPerl上への実装を行ってみます。

 まず②のサーバーからclientに送信しているChallenge(以降ServerChallenge)を

 Radius属性値の「MS-CHAP-Challenge」から取得します。

 ServerChallengeは16進数で出力されているため

 下記のようにpackを使用して16進数から10進数に戻します。

my $ServerChallenge_hex = substr($RAD_REQUEST{'MS-CHAP-Challenge'}, 2);
my $ServerChallenge = pack("H*", $ServerChallenge_hex);

 

 次に、client側で算出された③④⑥をRadius側で取得します。

 ③(以降PeerChallenge)はClient側で出力された値で、Radius属性値の「MS-CHAP2-Response」から取得します。

 こちらも16進数で出力されているためpackで戻します。

my $PeerChallenge_hex = substr($RAD_REQUEST{'MS-CHAP2-Response'}, 6, 32);
my $PeerChallenge = pack("H*", $PeerChallenge_hex);

 

 次に、④(以降Challenge)は上記の図にもある通り、PeerChallenge・ServerChallenge・MS-CHAP-User-Nameを

 1つの文字列に結合し、SHA1でハッシュ化を行います。

 その後、16進数化した値から先頭の16文字を取得したものがChallengeとなります。

my $Challenge = $PeerChallenge.$ServerChallenge.$RAD_REQUEST{'MS-CHAP-User-Name'};
my $Challenge_SHA1_hex = sha1_hex($Challenge);
$Challenge = substr($Challenge_SHA1_hex, 0, 16);
my $ChallengePack = pack("H*", $Challenge);

 

次に、⑥(以降NtResponse)はRadius属性値の「MS-CHAP2-Response」から取得します。

my $NtResponse = substr($RAD_REQUEST{'MS-CHAP2-Response'}, 54);
my $NtResponsePack = pack("H*", $NtResponse);

 

ここからかなりややこしくなりますが、次にMagic定数と呼ばれるものとSHSpadsと呼ばれる定数をそれぞれ用意します。

こちらは、RFC2759に16進数で記載されているものがあるためそちらをそのままpackで戻して使用します。

my @Magic1 = (0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74);
my @Magic2 = (0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E);
my @Magic3 = (0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79);
my @Magic4 = (0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x2e);
my @Magic5 = (0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, 0x2e);
my @SHSpad1 = (0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
my @SHSpad2 = (0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2);

my $Magic1 = pack("C*", @Magic1);
my $Magic2 = pack("C*", @Magic2);
my $Magic3 = pack("C*", @Magic3);
my $Magic4 = pack("C*", @Magic4);
my $Magic5 = pack("C*", @Magic5);
my $SHSpad1 = pack("C*", @SHSpad1);
my $SHSpad2 = pack("C*", @SHSpad2);

 

次に⑧(以降NtHashHash)で行っているパスワードのハッシュ化を行います。

ここで注意したいのはそのままパスワードを2回MD4でハッシュ化するのではなく、16進数化したパスワードに2文字ごとに00を挿入してからMD4でハッシュ化することに注意してください。

my $count;
my $passzero;
my $passun;
$passun = unpack("H*", $pass);

for ($count= 0; $count < length($passun); $count++) {
  $passzero = $passzero.substr($passun, $count, 2)."00";
  $count++;
}

my $NTHash = md4(pack("H*", $passzero));
my $NTHashHash = md4($NTHash);

 

ここからいよいよ定義したMagic定数を使用します。

まず、⑨(以降Digest)を算出します。DigestはNTHashHash、NtResponse(16進数)、Magic1を一つの文字列に結合したものにSHA1でハッシュ化を行ったものになります。

my $Digest = sha1($NTHashHash.$NtResponsePack.$Magic1);

 

次に⑩(以降AuthResponse)を算出します。AuthResponseはDigest、Challenge(16進数)、Magic2を一つの文字列に結合したものにSHA1でハッシュ化を行い16進数化したものに特定の文字列”0x02533d”を付与したものになります。

my $AuthResponse = sha1($Digest.$ChallengePack.$Magic2);
$AuthResponse = unpack("H*", $AuthResponse);	
$AuthResponse = "0x02533d".unpack("H*", uc($AuthResponse));

 

次に⑪(以降MasterSessionKey)を算出します。MasterSessionKeyはNTHashHash、NtResponse(16進数)、Magic3を一つの文字列に結合したものにSHA1でハッシュ化を行い16進数化したもののうち、32文字までの部分となります。

my $MasterSessionKey;
my $MasterSessionKey_hex = sha1_hex($NTHashHash.$NtResponsePack.$Magic3);
$MasterSessionKey = pack("H*",substr($MasterSessionKey_hex, 0, 32));

 

次に⑫(以降MasterSendKey)を算出します。MasterSendKeyはMasterSessionKey、SHSpad1、Magic5、SHSpad2を一つの文字列に結合したものにSHA1でハッシュ化を行い16進数化したもののうち、32文字までの部分となります。

my $MasterSendKey;
my $MasterSendKey_hex = sha1_hex($MasterSessionKey.$SHSpad1.$Magic5.$SHSpad2);
$MasterSendKey = "0x".substr($MasterSendKey_hex, 0, 32);

 

次に⑬(以降MasterReceiveKey)を算出します。MasterReceiveKeyはMasterSessionKey、SHSpad1、Magic4、SHSpad2を一つの文字列に結合したものにSHA1でハッシュ化を行い16進数化したもののうち、32文字までの部分となります。

my $MasterReceiveKey;
my $MasterReceiveKey_hex = sha1_hex($MasterSessionKey.$SHSpad1.$Magic4.$SHSpad2);
$MasterReceiveKey = "0x".substr($MasterReceiveKey_hex, 0, 32);

 

最後にClient側に返すRadius属性値に算出した値を返すことで認証が完了します。

※今回、MS-MPPE-Encryption-Policyは2(暗号化必須)、MS-MPPE-Encryption-Typesは4(128bitキー)を指定しています。

$RAD_REPLY{'MS-CHAP2-Success'} = $AuthResponse;
$RAD_REPLY{'MS-MPPE-Encryption-Types'} = "4";
$RAD_REPLY{'MS-MPPE-Recv-Key'} = $MasterReceiveKey;
$RAD_REPLY{'MS-MPPE-Send-Key'} = $MasterSendKey;
$RAD_REPLY{'MS-MPPE-Encryption-Policy'} = "2";

 

どうでしたでしょうか・・・かなり長く複雑な内容になってしまったと思いますので

もっと細かく知りたい場合はRFCを読むことをお勧めいたします。

 

以上でPerl上で行うMSCHAPV2認証の解説を終わります。

最後までご覧いただきありがとうございました!

最新記事

カテゴリ

アーカイブ

%d人のブロガーが「いいね」をつけました。