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 来实现整个用户流程。我用作依赖项:
org.jboss.aerogear:aerogear-otp-java
, aerogear OTP 方便地根据 GA 的 TOTP 验证用户密钥
org.grails.plugins:qrcode
,qrcode Grails plugin生成二维码
我的问题是关于在 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
在 Google 身份验证器应用程序中,您可以扫描二维码或手动输入发卡机构提供的密钥。
在下面的屏幕截图中,您可以看到 Google 安全设置中的 2FA 设置,显示如何按照第二种方法获取 TOTP。
我的问题是:这个密钥是如何生成的?
我正在尝试通过 Google 身份验证器为我的网站支持 2FA,我找到了很多关于如何生成 QR 码的参考资料和文档,但 none 甚至提到了替代方法。
编辑:
为了更清楚一点,我在 Grails 3 webapp 中使用 Google Authenticator 支持 2FA。我已经通过为每个用户生成一个密钥(Base32
字符串)、提供一个 QR 码供用户扫描并在登录时验证 TOTP 来实现整个用户流程。我用作依赖项:
org.jboss.aerogear:aerogear-otp-java
, aerogear OTP 方便地根据 GA 的 TOTP 验证用户密钥
org.grails.plugins:qrcode
,qrcode Grails plugin生成二维码
我的问题是关于在 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