ASP.NETでGoogle Authenticatorを用いたニ段階認証を行う

Google Authenticatorアプリケーションを用いて時間ベースのワンタイムパスワード(TOTP:Time-based One Time Password)を用いてニ段階認証します。
この認証方式ですが、名称がバラバラみたいです。AWSではMulti-Factor Authentication、Google Appsでは2-Step Verification、SlackではTwo factor authentication。意味合いはどれも一緒ですが。

で、今回使うGoogle Authenticatorはこちら。これでニ段階認証します。

iTunes

Google Authenticator

Google Authenticator

  • Google LLC
  • ユーティリティ
  • 無料
Android
https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=ja

環境

ASP.NET Web Forms
.NET Framework 4

必要なパッケージのインストール

OTP Sharp f:id:ihisa:20150703071712p:plain

OTP Sharpの依存関係であるBase32
f:id:ihisa:20150703071735p:plain

Qr Code library f:id:ihisa:20150703071750p:plain

QRコードはオンラインでもできますが今回はローカルで作成します。
https://google-developers.appspot.com/chart/infographics/docs/overview

1. ニ段階認証アプリケーション登録のための処理

やることはこれだけです。画像は直接Data URI schemeで描画します。

  1. 認証に用いる一意なキー(認証キー)を発行
  2. ニ段階認証登録用URLを作成
  3. 作成したURLを元にQRコード画像作成
using Base32;
using MessagingToolkit.QRCode.Codec;
using OtpSharp;
using System;
using System.Drawing;
using System.IO;

// 認証キーの作成とQRコードに埋め込むURL作成
byte[] secretKey = KeyGeneration.GenerateRandomKey(20);
string userName = "username";
string qrcodeUrl = KeyUrl.GetTotpUrl(secretKey, userName) + "&issuer=HogeHogeApplication";

// QRコードが読めなかった場合のキーコード表示
this.QRCodeKey.Text = Base32Encoder.Encode(secretKey);

// QRコード画像作成
QRCodeEncoder enc = new QRCodeEncoder();
enc.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.BYTE;
enc.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.M;
Image qrimage = enc.Encode(qrcodeUrl);

// 画像をBase64に変換
string qrcodestring = string.Empty;
using (var ms = new MemoryStream())
{
    qrimage.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
    qrcodestring = Convert.ToBase64String(ms.GetBuffer());
}

// Data URI schemeで描写
this.QRImage.ImageUrl = String.Format("data:{0};base64,{1}", "image/png", qrcodestring);
QRコード
<asp:Image ID="QRImage" runat="server" />                                    
<asp:Label ID="QRCodeKey" runat="server"></asp:Label>    

認証キー入力
<asp:TextBox ID="DigitCode" runat="server"></asp:TextBox>
<asp:Button ID="Register" runat="server" Text="登録" OnClick="Register_Click" />

f:id:ihisa:20150703074447p:plain

2. Google Authenticatorのインストールとキーの追加

  1. 画面に表示されたQRコードGoogle Authenticatorでスキャン(QRコードが読めない場合用に1-1で作成した認証キーを画面表示しておき、手入力可能にしておく)
  2. Google Authenticatorに表示されるワンタイムパスワードで認証させる(後述するログイン処理と同等の処理を行う)
  3. 認証成功時に認証キーを該当ユーザに紐づける(テーブル等に保存しておく)

f:id:ihisa:20150703071919p:plain

3. いざ二段階認証でログイン

  1. 入力されたワンタイムパスワード値を比較
using Base32;
using OtpSharp;

// qrcodestringは1-1で作成した認証キー文字列
byte[] secretKey = Base32Encoder.Decode(qrcodestring);

long timeStepMatched = 0;
Totp totp = new Totp(secretKey);

// 入力されたワンタイムパスワードで認証
if (totp.VerifyTotp(this.DigitCode.Text, out timeStepMatched, new VerificationWindow(1, 1)))
{
    // ニ段階認証成功!
}
else
{
    // ニ段階認証失敗!
}

VerifyTotpの第3引数のVerificationWindowは許容するワンタイムパスワードのずれ幅です。
https://bitbucket.org/devinmartin/otp-sharp/wiki/TOTP

お手軽に実装できました。