【问题标题】:Google Authenticator implementation in PerlPerl 中的 Google 身份验证器实现
【发布时间】:2014-10-21 10:53:12
【问题描述】:

我正在寻找一个简单的 Perl 实现来验证使用服务器端密码创建的 Google 身份验证器令牌。例如,

以下 Google URL 允许您将服务器密码以 base32 格式(在以下情况下密码为 e4ytonjeim4hcsrhja5fe5kqfu)编码为可从 Google 身份验证器应用程序读取的 QR 码(见下图):
https://chart.googleapis.com/chart?cht=qr&chs=100x100&chl=otpauth%3A%2F%2Ftotp%2Fmysite%3A29%3Fsecret%3De4ytonjeim4hcsrhja5fe5kqfu%26issuer%3Dmysite

一旦 QR 码被扫描到身份验证器应用程序中,它会生成类似于:716340 的令牌。如何验证令牌的正确性?

这个问题是这个 Python 问题的 Perl 等价物: Google Authenticator implementation in Python

【问题讨论】:

  • 这不是一个等价的问题。另一个问题的 OP 提供了代码,并提出了一个关于它的主题问题。这个问题离题了,您要求的是完整的解决方案(甚至没有尝试翻译 Python 代码)或外部资源。请考虑尝试并解释您遇到的问题
  • 很公平。如果我首先提出一个完整的解决方案,我肯定会发布它,但我问的是因为其他人可能已经跳过了这个环节。我认为一个答案(即使我要求一个完整的答案)对于 StackOverflow 上的其他人来说是一个非常有用的资源。我也不是 Python 用户,所以可能会浪费大量时间试图找出语法(我最终可能会这样做),而其他人可能会立即弄清楚。

标签: perl two-factor-authentication google-authenticator


【解决方案1】:

这是另一个解决方案,您可以验证它是否与this example 中生成的令牌匹配

use Authen::OATH;
use Convert::Base32;
my $oath = Authen::OATH->new();
my $secret = "JBSWY3DPEHPK3PXP";
my $otp = $oath->totp(  decode_base32( $secret ) );
print $otp."\n";

【讨论】:

    【解决方案2】:

    好吧,这花了一点时间,但我有一个 Perl 解决方案(希望这可以弥补这个稍微懒惰的问题 :) 感谢 Borodin 在这方面的帮助 (Taking the SHA1 HMAC of hex strings in Perl)

    #!/usr/bin/perl -w
    
    use strict;
    use warnings;
    
    use Convert::Base32;
    use Digest::HMAC_SHA1 qw/ hmac_sha1_hex /;
    
    my $base_32_secret = "JBSWY3DPEHPK3PXP";
    print "".totp_token($base_32_secret)."\n";
    
    sub totp_token {
        my $secret = shift;
    
        my $key = unpack("H*", decode_base32($secret));
        my $lpad_time = sprintf("%016x", int(time()/30));
        my $hmac = hmac_sha1_hex_string($lpad_time, $key);
    
        my $offset = sprintf("%d", hex(substr($hmac, -1)));
    
        my $part1 = 0 + sprintf("%d", hex(substr($hmac, $offset*2, 8)));
        my $part2 = 0 + sprintf("%d", hex("7fffffff"));
    
        my $token = substr("".($part1 & $part2), -6);
        return $token;
    }
    
    sub  hmac_sha1_hex_string {
       my ($data, $key) = map pack('H*', $_), @_;
       hmac_sha1_hex($data, $key);
    }
    

    【讨论】:

      【解决方案3】:

      Auth::GoogleAuthenticator 是否适合您的目的?

      编辑:确实如此;这验证了 JS 生成的 OTP。当计数器不再及时时,它返回一个空字符串;即假的。并且使用 URL 会导致应用程序同步到 JS:

      use Data::Printer;
      use Auth::GoogleAuthenticator;
      
      my $auth = Auth::GoogleAuthenticator->new(secret_base32 => q/e4ytonjeim4hcsrhja5fe5kqfu/);
      say $auth->registration_url;
      p($auth->verify('252499'));
      

      输出:

      otpauth://totp/?secret=e4ytonjeim4hcsrhja5fe5kqfu
      1
      

      【讨论】:

      • 这看起来行不通。我在这个 javascript oauth 示例中使用了秘密:blog.tinisles.com/2011/10/… 在 Google 身份验证器中,它们都产生相同的令牌。但是,当我对 Auth::GoogleAuthenticator 使用相同的秘密时,它不会产生匹配的令牌
      【解决方案4】:

      为了后代,我从@Vijay 的回答中获取了脚本(谢谢老兄),稍微简化了算法,添加了 TOTP 定义的文档,并添加了一些示例代码。

      我精简的数字生成代码只是@Vijay 答案的简化:

      use Digest::HMAC_SHA1 qw/ hmac_sha1_hex /;
      
      my $paddedTime = sprintf("%016x", int(time() / $TIME_STEP));
      my $data = pack('H*', $paddedTime);
      my $key = decode_base32($secret);
      
      # encrypt the data with the key and return the SHA1 of it in hex
      my $hmac = hmac_sha1_hex($data, $key);
      
      # take the 4 least significant bits (1 hex char) from the encrypted string as an offset
      my $offset = hex(substr($hmac, -1));
      # take the 4 bytes (8 hex chars) at the offset (* 2 for hex), and drop the high bit
      my $encrypted = hex(substr($hmac, $offset * 2, 8)) & 0x7fffffff;
      
      # the token is then the last 6 digits in the number
      my $token = $encrypted % 1000000;
      # make sure it is 0 prefixed
      return sprintf("%06d", $token);
      

      完整的TOTP 2 Factor Auth Perl script 可以从 Github 下载。

      【讨论】:

        猜你喜欢
        • 2012-01-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-19
        • 2015-01-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多