166 lines
5.3 KiB
C#
166 lines
5.3 KiB
C#
using Google.Authenticator;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Configuration;
|
|
using System.Drawing;
|
|
using System.Drawing.Imaging;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Web;
|
|
|
|
/// <summary>
|
|
/// Summary description for GoogleAuth
|
|
/// </summary>
|
|
public class GoogleAuth
|
|
{
|
|
public string User { get; set; }
|
|
public string Password { get; set; }
|
|
public string WebTitle { get; set; }
|
|
public string WebKey { get; set; }
|
|
public string SecretKey { get; set; }
|
|
public SetupCode setupCode { get; set; }
|
|
public GoogleAuth()
|
|
{
|
|
//
|
|
// TODO: Add constructor logic here
|
|
//
|
|
WebKey = ConfigurationManager.AppSettings["SC"].ToString();
|
|
WebTitle = WebKey;//可為中文?
|
|
}
|
|
public Image CreateSecretKeyAndQrCode()
|
|
{
|
|
Image ret;
|
|
TwoFactorAuthenticator tfA = new TwoFactorAuthenticator();
|
|
string issuer = WebKey + ":" + User;//發行者:用戶+網站
|
|
string accountTitle = WebKey + "::" + User;//標題(可為中文字?)
|
|
string accountSecKey = Password + SecretKey;//密鑰:密碼+隨機
|
|
setupCode = tfA.GenerateSetupCode(
|
|
issuer, accountTitle, accountSecKey, false, 3);
|
|
|
|
//1. QRCode圖片從記憶體轉到畫面上
|
|
using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(
|
|
setupCode.QrCodeSetupImageUrl.Replace("data:image/png;base64,", ""))))
|
|
{
|
|
ret = Image.FromStream(ms);
|
|
}
|
|
|
|
//2. 產生的金鑰與資訊
|
|
//this.textBox_Message.Text =
|
|
// "結合密鑰的文字 Account: " + textBox_account.Text + System.Environment.NewLine +
|
|
// "自已加密的密鑰 Secret Key: " + textBox_SecretKey.Text + System.Environment.NewLine +
|
|
// "手動輸入的密鑰 Encoded Key: " + setupCode.ManualEntryKey;
|
|
|
|
return ret;
|
|
}
|
|
public string ImageToBase64(Image image)
|
|
{
|
|
System.Drawing.Imaging.ImageFormat format = ImageFormat.Png;
|
|
string mime = "data:image/png;base64,";
|
|
using (MemoryStream ms = new MemoryStream())
|
|
{
|
|
// Convert Image to byte[]
|
|
image.Save(ms, format);
|
|
byte[] imageBytes = ms.ToArray();
|
|
|
|
// Convert byte[] to base 64 string
|
|
string base64String = mime+Convert.ToBase64String(imageBytes);
|
|
return base64String;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 驗證碼是否正確
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public bool ValidateGoogleAuthCode(string ValidateCode)
|
|
{
|
|
string k = Password + SecretKey; //要存到資料庫, 才能重用
|
|
var r = false;
|
|
TwoFactorAuthenticator tfA = new TwoFactorAuthenticator();
|
|
r = tfA.ValidateTwoFactorPIN(k, ValidateCode);
|
|
return r;
|
|
}
|
|
/// <summary>
|
|
/// 產生Secret當前的驗證碼
|
|
/// </summary>
|
|
public List<string> GeneratorCurrentCode()
|
|
{
|
|
var resultArray = new TwoFactorAuthenticator().GetCurrentPINs(SecretKey);
|
|
var resultList = new List<string>(resultArray);
|
|
return resultList;
|
|
}
|
|
}
|
|
/*
|
|
# 例:
|
|
|
|
綁定資訊:
|
|
使用者帳號: user1
|
|
網站代號: erp17168
|
|
使用者密碼: G0t0r5hPel4EJnAfFkmhAI=
|
|
動態密鑰: aJn7TSc3Nl2UPUTWtiBE
|
|
setupCode: I4YHIMDSGVUFAZLMGRCUU3SBMZDGW3LIIFET2YKKNY3VIU3DGNHGYMSVKBKVIV3UNFBEK
|
|
|
|
---
|
|
# 傳回QRCODE
|
|
otpauth://totp/erp17168%3Auser1:erp17168:user1?secret=I4YHIMDSGVUFAZLMGRCUU3SBMZDGW3LIIFET2YKKNY3VIU3DGNHGYMSVKBKVIV3UNFBEK&issuer=erp17168%3Auser1
|
|
|
|
otpauth://totp/
|
|
erp17168:user1:erp17168:user1
|
|
?
|
|
secret=I4YHIMDSGVUFAZLMGRCUU3SBMZDGW3LIIFET2YKKNY3VIU3DGNHGYMSVKBKVIV3UNFBEK
|
|
&
|
|
issuer=erp17168%3Auser1
|
|
|
|
|
|
# 建立:
|
|
動態=亂數()
|
|
U="網站:用戶"
|
|
K="密碼+動態"
|
|
|
|
QRCODE= "otpauth://" + U + SecretKey(回傳的)
|
|
SecretKey:不用存
|
|
用戶拍QRCODE, 記在Google Anth. App中
|
|
每30秒自動更新一次
|
|
|
|
# 驗證:
|
|
(先檢查帳密:通過)
|
|
再傳出:
|
|
K="密碼+動態" (從資料庫抓:用戶)
|
|
V=(用戶即時輸入的驗證碼, 來自APP)
|
|
傳回:true / false
|
|
|
|
---
|
|
參考
|
|
https://dotblogs.com.tw/milkgreenteaprograme_c_sharp/2020/10/28/135725
|
|
https://github.com/gotoa1234/GoogleAuthenticatorExample
|
|
|
|
---
|
|
# 未實作功能
|
|
# button : 取得當前SecretKey的密碼
|
|
private void button_GeneratorCode_Click(object sender, EventArgs e)
|
|
{
|
|
DataGridViewRow row = (DataGridViewRow)dataGridView_KeyCode.RowTemplate.Clone();
|
|
var currentCodeList = GeneratorCurrentCode().Take(1);
|
|
var takeSingle = currentCodeList.Take(1).First();//取一筆做為比較
|
|
if (_lastCurrentCode != takeSingle)
|
|
{
|
|
_lastCurrentCode = takeSingle;
|
|
var currentDateTimeNow = DateTime.Now;
|
|
foreach (var code in currentCodeList)
|
|
{
|
|
dataGridView_KeyCode.Rows.Add(
|
|
currentDateTimeNow.ToString("yyyy/MM/d HH:mm:ss"),
|
|
textBox_account.Text,
|
|
textBox_SecretKey.Text,
|
|
code);
|
|
}
|
|
//排序
|
|
dataGridView_KeyCode.Sort(dataGridView_KeyCode.Columns["GeneratorDateTime"], System.ComponentModel.ListSortDirection.Descending);
|
|
}
|
|
}
|
|
# grid
|
|
產生時間:GeneratorDateTime
|
|
手產字串:Account
|
|
手產金鑰:SecretKey
|
|
當前驗證金鑰:CurrentCode
|
|
*/ |