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 description for GoogleAuth /// 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; } } /// /// 驗證碼是否正確 /// /// public bool ValidateGoogleAuthCode(string ValidateCode) { string k = Password + SecretKey; //要存到資料庫, 才能重用 var r = false; TwoFactorAuthenticator tfA = new TwoFactorAuthenticator(); r = tfA.ValidateTwoFactorPIN(k, ValidateCode); return r; } /// /// 產生Secret當前的驗證碼 /// public List GeneratorCurrentCode() { var resultArray = new TwoFactorAuthenticator().GetCurrentPINs(SecretKey); var resultList = new List(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 */