Bài viết hôm nay mình tiếp tục hướng dẫn các bạn, cách bảo mật ứng dụng đăng nhập 2 lớp 2FA (Multi-factor Authentication) trên C# winform.
Dưới đây, là hình ảnh demo ứng dụng:

Tiếp đến các bạn scan mã qrcode trên.
Nó sẽ hiển thị cho các bạn mã OTP gồm 6 số để các bạn thực hiện đăng nhập 2FA.
Nó sẽ hiện thị số như hình bên dưới.

Source code c# đầy đủ:
using OtpNet;
using QRCoder;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Media.Imaging;
namespace OTP_Microsoft_Auth
{
public partial class Form1 : Form
{
readonly QRCodeGenerator qrGenerator = new QRCodeGenerator();
const int DEFAULT_STEP = 30;
public Form1()
{
InitializeComponent();
txtAppName.Focus();
txtAppName.Text = "Demo2FA Auth";
txtIssuer.Text = "Thảo meo - Laptrinhvb.net";
txtSecret.Text = Base32Encoding.ToString(KeyGeneration.GenerateRandomKey(20));
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void txtSecret_TextChanged(object sender, EventArgs e)
{
if (txtAppName.Text != "" && txtIssuer.Text != "" && txtSecret.Text != "")
{
var qrCodeUri = $"otpauth://totp/{Uri.EscapeDataString(txtAppName.Text)}?secret={txtSecret.Text}&issuer={Uri.EscapeDataString(txtIssuer.Text)}";
using (var qrCodeData = qrGenerator.CreateQrCode(qrCodeUri, QRCodeGenerator.ECCLevel.Q))
{
using (var qrCode = new QRCode(qrCodeData))
{
var qrCodeImage = qrCode.GetGraphic(20);
using (var memory = new MemoryStream())
{
qrCodeImage.Save(memory, ImageFormat.Bmp);
memory.Position = 0;
var bitmapImage = new Bitmap(memory);
pictureBox1.Image = bitmapImage;
}
}
}
}
}
private void txtVerify_Click(object sender, EventArgs e)
{
var totp = new Totp(Base32Encoding.ToBytes(txtSecret.Text));
var isValidTotpCode = totp.VerifyTotp(txtCodeOTP.Text, out long timeStepMatched, new VerificationWindow(previous: 1, future: 1));
var now = DateTime.UtcNow;
var step = Math.Floor((now - new DateTime(1970, 1, 1)).TotalSeconds / DEFAULT_STEP);
lblCode1.Text = $"{totp.ComputeTotp(now.AddSeconds(-DEFAULT_STEP))}{Environment.NewLine}({step - 1})";
lblCode2.Text = $"{totp.ComputeTotp(now)}{Environment.NewLine}({step})";
lblCode3.Text = $"{totp.ComputeTotp(now.AddSeconds(DEFAULT_STEP))}{Environment.NewLine}({step + 1})";
if (isValidTotpCode)
{
MessageBox.Show($"Valid TOTP Code (step: {timeStepMatched})!");
}
else
{
MessageBox.Show("Invalid TOTP Code");
}
}
}
}