You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#应用中基于Google Authenticator的双因素认证流程及密钥使用咨询

基于Google Authenticator的C#双因素认证实现指南

一、完整双因素认证流程(补充优化版)

  • 用户在系统中申请启用双因素认证
  • 用户输入用户名和密码完成首次登录验证
  • 后台生成双因素认证密钥并生成对应二维码,返回前端展示,提示用户扫码并输入验证码
  • 用户通过Google Authenticator获取验证码并提交
  • 后台验证验证码有效性,通过则允许进入系统;失败则提示重试

二、关于“密钥”的核心说明

1. 密钥的作用

密钥是生成**一次性时间同步验证码(TOTP)**的核心种子,客户端(Google Authenticator)与服务端基于同一密钥+当前时间戳,通过相同的HMAC算法生成一致的验证码,以此验证用户身份的合法性。

2. 使用方式

  • 配置阶段:服务端生成随机的Base32格式密钥,将其封装为符合OTP标准的URI(格式:otpauth://totp/[应用名称]:[用户名]?secret=[密钥]&issuer=[应用名称]),再转换为二维码供用户扫码。Google Authenticator扫码后会本地存储该密钥,每隔30秒自动生成新的验证码。
  • 登录验证阶段:用户提交验证码后,服务端取出存储的密钥,结合当前时间(允许前后1-2个30秒窗口,兼容时间差)生成预期验证码,与用户输入的验证码比对,一致则验证通过。

3. 存储位置

  • 服务端:必须加密存储在用户关联的数据库字段中(例如用AES或.NET DataProtection API加密),绝对禁止明文存储,防止数据泄露后被绕过双因素认证。
  • 客户端:密钥由Google Authenticator本地存储在用户设备中,用户无需手动管理。

三、步骤2到3的后台逻辑(登录后生成二维码的核心流程)

  1. 用户名密码验证通过后,后台检查该用户是否已生成双因素认证密钥:
    • 若未生成,调用工具生成16位长度的Base32随机密钥(TOTP标准推荐长度)
    • 将密钥加密后写入用户数据库的对应字段
  2. 用生成的密钥构建OTP标准URI
  3. 将URI转换为二维码图片流(或直接返回URI让前端生成二维码),返回给前端展示,同时提示用户扫码并输入验证码

四、C#示例代码

1. 依赖准备

安装NuGet包 Google.Authenticator(封装了TOTP算法与二维码生成逻辑)

2. 核心代码实现

(1)生成密钥与二维码

using Google.Authenticator;

// 初始化双因素认证工具
var tfa = new TwoFactorAuthenticator();
// 生成16位Base32随机密钥
var secretKey = tfa.GenerateRandomKey(16);
// 生成配置信息(包含二维码URL与手动输入密钥)
var setupInfo = tfa.GenerateSetupCode(
    issuer: "你的应用名称",
    accountTitle: "user@example.com", // 用户名或用户邮箱
    accountSecretKey: secretKey,
    includeQrCode: false // 若设为true会直接返回二维码图片字节流
);

// 加密密钥后存储到数据库(示例用简化加密,实际需用企业级加密方案)
string encryptedKey = Encrypt(secretKey, "你的加密密钥");
// 将encryptedKey写入用户表的TwoFactorSecret字段

(2)验证用户提交的验证码

// 从数据库获取当前用户的加密密钥并解密
string encryptedKey = GetUserTwoFactorSecret(userId);
string secretKey = Decrypt(encryptedKey, "你的加密密钥");

var tfa = new TwoFactorAuthenticator();
// 验证验证码,允许前后1个时间窗口(共90秒,兼容时间差)
bool isVerified = tfa.ValidateTwoFactorPIN(secretKey, userInputCode, timeTolerance: 1);

if (isVerified)
{
    // 验证通过,放行登录
}
else
{
    // 验证失败,提示用户重新输入
}

(3)简化版加密解密示例(生产环境建议用.NET DataProtection API)

using System.Security.Cryptography;
using System.Text;

public static string Encrypt(string plainText, string key)
{
    using var aes = Aes.Create();
    aes.Key = Encoding.UTF8.GetBytes(key.PadRight(32).Substring(0, 32));
    aes.IV = new byte[16];
    var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
    
    using var ms = new MemoryStream();
    using var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write);
    using var sw = new StreamWriter(cs);
    sw.Write(plainText);
    sw.Close();
    
    return Convert.ToBase64String(ms.ToArray());
}

public static string Decrypt(string cipherText, string key)
{
    using var aes = Aes.Create();
    aes.Key = Encoding.UTF8.GetBytes(key.PadRight(32).Substring(0, 32));
    aes.IV = new byte[16];
    var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
    
    using var ms = new MemoryStream(Convert.FromBase64String(cipherText));
    using var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read);
    using var sr = new StreamReader(cs);
    
    return sr.ReadToEnd();
}

五、关键注意事项

  • 服务端必须同步NTP时间,确保与客户端时间误差在30秒内,否则TOTP验证会失败
  • 密钥加密存储是硬性要求,绝对不能明文保存
  • 需提供手动输入密钥的选项,应对用户无法扫码的场景
  • 验证时的时间窗口不宜过大,建议控制在1-2个30秒窗口范围内,平衡兼容性与安全性

内容的提问来源于stack exchange,提问作者Tracey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 20:30:46