Two-factor 使用 Google 身份验证器进行身份验证 - 手动输入密钥而不是扫描二维码

Two-factor authentication with Google Authenticator - manually type key instead of scanning QR code

在 Google 身份验证器应用程序中,您可以扫描二维码或手动输入发卡机构提供的密钥。

在下面的屏幕截图中,您可以看到 Google 安全设置中的 2FA 设置,显示如何按照第二种方法获取 TOTP。

我的问题是:这个密钥是如何生成的?

我正在尝试通过 Google 身份验证器为我的网站支持 2FA,我找到了很多关于如何生成 QR 码的参考资料和文档,但 none 甚至提到了替代方法。

编辑:

为了更清楚一点,我在 Grails 3 webapp 中使用 Google Authenticator 支持 2FA。我已经通过为每个用户生成一个密钥(Base32 字符串)、提供一个 QR 码供用户扫描并在登录时验证 TOTP 来实现整个用户流程。我用作依赖项:

我的问题是关于在 Google 身份验证器应用程序中添加新条目的两种方法: 1.扫描二维码(我这边一切正常) 2. 手动输入帐户名和字母代码(在我的第一个屏幕截图中,代码在 Google 安全设置中提供)

您可以在 GA 中查看 Android 的解释性屏幕截图:

如何生成并向用户提供这样的代码(在第一个屏幕截图中以 fzee 开头,在第二个屏幕截图中命名为 "provided key")?我确定它是同样的数据字符串的编码,也编码在 QR 码中,但我不知道是哪个(不仅仅是 Base32)。

Google Authenticator Setup QR 代码是基于一些东西生成的,其中之一是 "secret key",因此根据您用来将其构建到您的站点的代码库,"secret key" 通常先生成,然后使用该密钥生成二维码。

如果你看一下这个 node.js 模块你就会明白我在说什么

// generate base32 secret
var secret = GA.encode('base 32 encoded user secret') || GA.secret();

// get QRCode in SVG format
var qrCode = GA.qrCode('akanass', 'otp.js', secret);

https://github.com/njl07/otp.js/#qrcode-generation

这是另一个示例站点,您可以在其中手动生成 QR 码, 您可以提供一个Label, User, Key and URL,然后生成二维码。

https://dan.hersam.com/tools/gen-qr-code.html

让我知道您尝试使用什么代码库将此实现到您的网站,然后我可以帮助您追踪生成密钥的位置

public class HomeController : Controller
    {
        private const string key = "key"; // any 10-12 char string for use as private key in google authenticator
        public ActionResult Login()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Login(LoginModel login)
        {
            string message = "";
            bool status = false;

            //check username and password form our database here
            //for demo I am going to use Admin as Username and Password1 as Password static value
            if (login.Username == "sanket" && login.Password == "123")
            {
                status = true; // show 2FA form
                message = "2FA Verification";
                Session["Username"] = login.Username;

                //2FA Setup
                TwoFactorAuthenticator tfa = new TwoFactorAuthenticator();
                string UserUniqueKey = login.Username + key; //as Its a demo, I have done this way. But you should use any encrypted value here which will be unique value per user 
                Session["UserUniqueKey"] = UserUniqueKey;
                var setupInfo = tfa.GenerateSetupCode("Sanket Security", login.Username, UserUniqueKey, 300, 300);
                ViewBag.BarcodeImageUrl = setupInfo.QrCodeSetupImageUrl;
                ViewBag.SetupCode = setupInfo.ManualEntryKey;
            }
            else
            {
                message = "Invalid credential";
            }
            ViewBag.Message = message;
            ViewBag.Status = status;
            return View();
        }

        public ActionResult MyProfile()
        {
            if (Session["Username"] == null || Session["IsValid2FA"] == null || !(bool)Session["IsValid2FA"])
            {
                return RedirectToAction("Login");
            }
            ViewBag.Message = "Welcome " + Session["Username"].ToString();
            return View();
        }
        public ActionResult Verify2FA()
        {
            var token = Request["passcode"];
            TwoFactorAuthenticator tfa = new TwoFactorAuthenticator();
            string UserUniqueKey = Session["UserUniqueKey"].ToString();
            bool isValid = tfa.ValidateTwoFactorPIN(UserUniqueKey, token);
            if (isValid)
            {
                Session["IsValid2FA"] = true;
                return RedirectToAction("MyProfile", "Home");
                token=string.Empty;
            }
            return RedirectToAction("Login", "Home");
        }
    }
}
And View

@model _2FAGoogleAuthenticator.ViewModel.LoginModel

@{
    ViewBag.Title = "Login";
}

<h2>Login</h2>

@* Here we will add 2 form, 1 for Login and another one for 2FA token verification form *@
@if (ViewBag.Status == null || !ViewBag.Status)
{
    <!--Show login form here, Viewbag.Status is used for check is already veirfied from our database or not-->
    <div>@ViewBag.Message</div>
    <div>
        @using (Html.BeginForm())
        {
            <div class="form-group">
                <label for="Username">Username : </label>
                @Html.TextBoxFor(a => a.Username, new { @class = "form-control"})
            </div>
            <div class="form-group">
                <label for="Password">Password : </label>
                @Html.TextBoxFor(a => a.Password, new { @class="form-control", type="password"})
            </div>
            <input type="submit" value="Login" class="btn btn-default" />
        }
    </div>
}
else
{
    <!--Show 2FA verification form here-->
    <div>@ViewBag.Message</div>
    <div>
        <img src="@ViewBag.BarcodeImageUrl"/>
    </div>
    <div>
        **Manual Setup Code : @ViewBag.SetupCode**
    </div>
    <div>
        @using (Html.BeginForm("Verify2FA","Home", FormMethod.Post))
        {
            <input type="text" name="passcode" />
            <input type="submit" class="btn btn-success" /> 
        }
    </div>
}

密钥=秘密

密钥应与您生成的密钥相同。只需打开 google 身份验证器并手动将其添加为密钥即可对其进行测试。

查看下面 link 中的文档: https://support.google.com/accounts/answer/1066447?co=GENIE.Platform%3DiOS&hl=en