谷歌身份验证器作为公共服务可用?

是否有公共应用程式编程接口可供自行运行(例如 LAMP 协议栈)的 web 应用程式使用 谷歌身份验证器(双因素认证) ?

119381 次浏览

项目是开源的。我还没用过。但是它使用了一个文档化的算法(在开放源码项目页面上列出的 RFC 中注明) ,并且身份验证器实现支持多个帐户。

实际过程很简单。这个时间码本质上是一个伪随机数发生器。随机数生成器是一个公式,一旦给定一个种子,或起始数字,继续创建一个随机数流。给定一个种子,虽然这些数字可能是随机的,但序列本身是确定性的。因此,一旦你让你的设备和服务器“同步”,那么设备创建的随机数,每次你点击“下一个数字按钮”,将是相同的,随机的,数字服务器期望。

一个安全的一次性密码系统比随机数生成器更复杂,但概念是相似的。还有其他一些细节可以帮助保持设备和服务器的同步。

因此,没有必要让其他人来主持身份验证,比如说 OAuth。相反,你需要实现与谷歌为移动设备提供的应用程序兼容的算法。那个软件(应该)可以在开源项目中使用。

根据您的复杂程度,您应该具备实现此过程的服务器端所需的一切,包括 OSS 项目和 RFC。我不知道是否有一个特定的实现为您的服务器软件(PHP,Java,。等)

但是,具体来说,您不需要离线服务来处理这个问题。

有各种各样的 PHP 库(LAMP 栈)

PHP

Https://code.google.com/p/ga4php/

Http://www.idontplaydarts.com/2011/07/google-totp-two-factor-authentication-for-php/

在实现双因素身份验证时,您应该小心,您需要确保服务器和客户机上的时钟是同步的,有针对令牌的暴力攻击的保护措施,并且所使用的初始种子适当大。

这个算法在 RFC6238中有记载,有点像这样:

  • 您的服务器为用户提供了一个安装到 Google Authenticator 的秘密。谷歌这样做作为一个二维码文档 给你
  • Google Authenticator 通过一个关于 Unix 时间和秘密的 SHA1-HMAC 生成一个6位代码(RFC 中有更多关于这方面的详细信息)
  • 服务器还知道验证6位代码的 secret/unix 时间。

我在这里用 javascript 实现了一个算法: http://blog.tinisles.com/2011/10/google-authenticator-one-time-password-algorithm-in-javascript/

你可以 用我的方法,张贴作为我的问题的答案(有 完整的 Python 代码解释) :

用 Python 实现 Google Authenticator

我认为在 PHP 或 Perl 中实现它相当容易。如果你对此有任何问题,请让我知道。

我还将 在 GitHub 上发布了我的代码作为 Python 模块。

有: https://www.gauthify.com,提供它作为一种服务

我发现了这个: https://github.com/PHPGangsta/GoogleAuthenticator。我测试了它,对我来说工作得很好。

是的,不需要网络服务,因为谷歌认证应用程序不会与谷歌服务器通信,它只是保持同步的初始秘密,您的服务器生成(输入到您的手机从二维码) ,而时间的流逝。

对于那些使用 Laravel 的人来说,这个 https://github.com/sitepoint-editors/google-laravel-2FA是解决这个问题的好方法。

不是 LAMP,但如果你使用 C # ,这是我使用的代码:

代码原文来自:

Https://github.com/kspearrin/otp.net

Base32Encoding 类来自这个答案:

Https://stackoverflow.com/a/7135008/3850405

示例程序:

class Program
{
static void Main(string[] args)
{
var bytes = Base32Encoding.ToBytes("JBSWY3DPEHPK3PXP");


var totp = new Totp(bytes);


var result = totp.ComputeTotp();
var remainingTime = totp.RemainingSeconds();
}
}

上图:

public class Totp
{
const long unixEpochTicks = 621355968000000000L;


const long ticksToSeconds = 10000000L;


private const int step = 30;


private const int totpSize = 6;


private byte[] key;


public Totp(byte[] secretKey)
{
key = secretKey;
}


public string ComputeTotp()
{
var window = CalculateTimeStepFromTimestamp(DateTime.UtcNow);


var data = GetBigEndianBytes(window);


var hmac = new HMACSHA1();
hmac.Key = key;
var hmacComputedHash = hmac.ComputeHash(data);


int offset = hmacComputedHash[hmacComputedHash.Length - 1] & 0x0F;
var otp = (hmacComputedHash[offset] & 0x7f) << 24
| (hmacComputedHash[offset + 1] & 0xff) << 16
| (hmacComputedHash[offset + 2] & 0xff) << 8
| (hmacComputedHash[offset + 3] & 0xff) % 1000000;


var result = Digits(otp, totpSize);


return result;
}


public int RemainingSeconds()
{
return step - (int)(((DateTime.UtcNow.Ticks - unixEpochTicks) / ticksToSeconds) % step);
}


private byte[] GetBigEndianBytes(long input)
{
// Since .net uses little endian numbers, we need to reverse the byte order to get big endian.
var data = BitConverter.GetBytes(input);
Array.Reverse(data);
return data;
}


private long CalculateTimeStepFromTimestamp(DateTime timestamp)
{
var unixTimestamp = (timestamp.Ticks - unixEpochTicks) / ticksToSeconds;
var window = unixTimestamp / (long)step;
return window;
}


private string Digits(long input, int digitCount)
{
var truncatedValue = ((int)input % (int)Math.Pow(10, digitCount));
return truncatedValue.ToString().PadLeft(digitCount, '0');
}


}

Base32编码:

public static class Base32Encoding
{
public static byte[] ToBytes(string input)
{
if (string.IsNullOrEmpty(input))
{
throw new ArgumentNullException("input");
}


input = input.TrimEnd('='); //remove padding characters
int byteCount = input.Length * 5 / 8; //this must be TRUNCATED
byte[] returnArray = new byte[byteCount];


byte curByte = 0, bitsRemaining = 8;
int mask = 0, arrayIndex = 0;


foreach (char c in input)
{
int cValue = CharToValue(c);


if (bitsRemaining > 5)
{
mask = cValue << (bitsRemaining - 5);
curByte = (byte)(curByte | mask);
bitsRemaining -= 5;
}
else
{
mask = cValue >> (5 - bitsRemaining);
curByte = (byte)(curByte | mask);
returnArray[arrayIndex++] = curByte;
curByte = (byte)(cValue << (3 + bitsRemaining));
bitsRemaining += 3;
}
}


//if we didn't end with a full byte
if (arrayIndex != byteCount)
{
returnArray[arrayIndex] = curByte;
}


return returnArray;
}


public static string ToString(byte[] input)
{
if (input == null || input.Length == 0)
{
throw new ArgumentNullException("input");
}


int charCount = (int)Math.Ceiling(input.Length / 5d) * 8;
char[] returnArray = new char[charCount];


byte nextChar = 0, bitsRemaining = 5;
int arrayIndex = 0;


foreach (byte b in input)
{
nextChar = (byte)(nextChar | (b >> (8 - bitsRemaining)));
returnArray[arrayIndex++] = ValueToChar(nextChar);


if (bitsRemaining < 4)
{
nextChar = (byte)((b >> (3 - bitsRemaining)) & 31);
returnArray[arrayIndex++] = ValueToChar(nextChar);
bitsRemaining += 5;
}


bitsRemaining -= 3;
nextChar = (byte)((b << bitsRemaining) & 31);
}


//if we didn't end with a full char
if (arrayIndex != charCount)
{
returnArray[arrayIndex++] = ValueToChar(nextChar);
while (arrayIndex != charCount) returnArray[arrayIndex++] = '='; //padding
}


return new string(returnArray);
}


private static int CharToValue(char c)
{
int value = (int)c;


//65-90 == uppercase letters
if (value < 91 && value > 64)
{
return value - 65;
}
//50-55 == numbers 2-7
if (value < 56 && value > 49)
{
return value - 24;
}
//97-122 == lowercase letters
if (value < 123 && value > 96)
{
return value - 97;
}


throw new ArgumentException("Character is not a Base32 character.", "c");
}


private static char ValueToChar(byte b)
{
if (b < 26)
{
return (char)(b + 65);
}


if (b < 32)
{
return (char)(b + 24);
}


throw new ArgumentException("Byte is not a value Base32 value.", "b");
}


}

对于 C # 用户,运行这个简单的控制台应用程序来理解如何验证一次性令牌代码。请注意,我们需要首先从 Nuget 软件包安装程序库 Otp. Net

static string secretKey = "JBSWY3DPEHPK3PXP"; //add this key to your Google Authenticator app


private static void Main(string[] args)
{
var bytes = Base32Encoding.ToBytes(secretKey);


var totp = new Totp(bytes);


while (true)
{
Console.Write("Enter your code from Google Authenticator app: ");
string userCode = Console.ReadLine();


//Generate one time token code
string tokenInApp = totp.ComputeTotp();
int remainingSeconds = totp.RemainingSeconds();


if (userCode.Equals(tokenInApp)
&& remainingSeconds > 0)
{
Console.WriteLine("Success!");
}
else
{
Console.WriteLine("Failed. Try again!");
}
}
}