前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用aerogear生成totp

使用aerogear生成totp

作者头像
code4it
发布2018-09-17 15:30:35
1.8K0
发布2018-09-17 15:30:35
举报
文章被收录于专栏:码匠的流水账

本文主要讲述一下如何使用aerogear-otp生成otp,以及相关源码分析

otp分类

全称是one time password,通常用来支持双因素认证。主要可以分两类

  • HMAC-Based One-time Password (HOTP) 在RFC 4226规范中
  • Time-based One-time Password (TOTP) 在RFC 6238规范中

这里主要讲TOTP

  • 客户端 其常见的手机客户端有Google Authenticator APP以及阿里云的身份宝。由于google的软件在国内被墙,因此可以使用阿里云的身份宝
  • 服务端 服务端的话,google官方有c的代码,java的话很多第三方都有实现,这里选择jboss提供的aerogear-otp-java,其maven如下 <dependency> <groupId>org.jboss.aerogear</groupId> <artifactId>aerogear-otp-java</artifactId> <version>1.0.0</version> </dependency>

步骤

主要的步骤如下:

绑定密钥

  • 服务端为每个账户生成一个secret并保存下来
  • 服务端提供该密钥的二维码扫描功能,方便客户端扫描绑定账号
  • 用户手机安装Google Authenticator APP或阿里云的身份宝,扫描二维码绑定该账号的secret

使用otp验证

绑定secret之后,就可以使用one time password进行验证了。

实例

生成客户端密钥的二维码

代码语言:javascript
复制
String secret = Base32.random();
Totp totp = new Totp(secret);
String uri = totp.uri(account);

将这个uri作为二维码的信息,即可。

代码语言:javascript
复制
    /**
     * Prover - To be used only on the client side
     * Retrieves the encoded URI to generated the QRCode required by Google Authenticator
     *
     * @param name Account name
     * @return Encoded URI
     */
    public String uri(String name) {
        try {
            return String.format("otpauth://totp/%s?secret=%s", URLEncoder.encode(name, "UTF-8"), secret);
        } catch (UnsupportedEncodingException e) {
            throw new IllegalArgumentException(e.getMessage(), e);
        }
    }

它的格式otpauth://totp/%s?secret=%s,Google Authenticator APP或阿里云的身份宝均支持这种格式的识别。

校验

代码语言:javascript
复制
 boolean isValid = totp.verify(code);

其源码如下

代码语言:javascript
复制
    /**
     * Verifier - To be used only on the server side
     * <p/>
     * Taken from Google Authenticator with small modifications from
     * {@see <a href="http://code.google.com/p/google-authenticator/source/browse/src/com/google/android/apps/authenticator/PasscodeGenerator.java?repo=android#212">PasscodeGenerator.java</a>}
     * <p/>
     * Verify a timeout code. The timeout code will be valid for a time
     * determined by the interval period and the number of adjacent intervals
     * checked.
     *
     * @param otp Timeout code
     * @return True if the timeout code is valid
     *         <p/>
     *         Author: sweis@google.com (Steve Weis)
     */
    public boolean verify(String otp) {

        long code = Long.parseLong(otp);
        long currentInterval = clock.getCurrentInterval();

        int pastResponse = Math.max(DELAY_WINDOW, 0);

        for (int i = pastResponse; i >= 0; --i) {
            int candidate = generate(this.secret, currentInterval - i);
            if (candidate == code) {
                return true;
            }
        }
        return false;
    }

这里有个DELAY_WINDOW参数,是为了防止手机客户端与服务器端的时差引入的。默认值是1,即允许那个code在手机端过期30秒之内到服务端验证还有效。

  • clock aerogear-otp-java-1.0.0-sources.jar!/org/jboss/aerogear/security/otp/api/Clock.java public class Clock { private final int interval; private Calendar calendar; public Clock() { interval = 30; } public Clock(int interval) { this.interval = interval; } public long getCurrentInterval() { calendar = GregorianCalendar.getInstance(TimeZone.getTimeZone("UTC")); long currentTimeSeconds = calendar.getTimeInMillis() / 1000; return currentTimeSeconds / interval; } } 这个interval默认是30,当然你也可以改为1分钟那就是60. 另外这里先把毫秒转为秒,然后再除去interval,由于是使用/,因此是直接取整数部分,因而上面的DELAY_WINDOW的值=N,其实相当于允许过去的N个interval的code还能校验成功

code生成源码

aerogear-otp-java-1.0.0-sources.jar!/org/jboss/aerogear/security/otp/Totp.java

代码语言:javascript
复制
    private int generate(String secret, long interval) {
        return hash(secret, interval);
    }

    private int hash(String secret, long interval) {
        byte[] hash = new byte[0];
        try {
            //Base32 encoding is just a requirement for google authenticator. We can remove it on the next releases.
            hash = new Hmac(Hash.SHA1, Base32.decode(secret), interval).digest();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (Base32.DecodingException e) {
            e.printStackTrace();
        }
        return bytesToInt(hash);
    }

    private int bytesToInt(byte[] hash) {
        // put selected bytes into result int
        int offset = hash[hash.length - 1] & 0xf;

        int binary = ((hash[offset] & 0x7f) << 24) |
                ((hash[offset + 1] & 0xff) << 16) |
                ((hash[offset + 2] & 0xff) << 8) |
                (hash[offset + 3] & 0xff);

        return binary % Digits.SIX.getValue();
    }

    /**
     * Retrieves the current OTP
     *
     * @return OTP
     */
    public String now() {
        return leftPadding(hash(secret, clock.getCurrentInterval()));
    }

    private String leftPadding(int otp) {
        return String.format("%06d", otp);
    }

小结

  • interval 默认值为30,在Clock里头可以通过构造器修改interval。不过由于Google Authenticator APP或阿里云的身份宝均为30秒更换一次,因此这个参数可以按默认的来。
  • DELAY_WINDOW aerogear-otp-java本身不提供DELAY_WINDOW的修改,不过你可以继承Totp,自己扩展一下。

doc

  • 身份宝
  • google-authenticator
  • Java 接入 Google Authenticator
  • 使用OTP动态口令(每分钟变一次)进行登录认证
  • GoogleAuth
  • aerogear-otp-java
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-12-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码匠的流水账 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • otp分类
  • 步骤
    • 绑定密钥
      • 使用otp验证
      • 实例
        • 生成客户端密钥的二维码
          • 校验
          • code生成源码
          • 小结
          • doc
          相关产品与服务
          多因子身份认证
          多因子身份认证(Multi-factor Authentication Service,MFAS)的目的是建立一个多层次的防御体系,通过结合两种或三种认证因子(基于记忆的/基于持有物的/基于生物特征的认证因子)验证访问者的身份,使系统或资源更加安全。攻击者即使破解单一因子(如口令、人脸),应用的安全依然可以得到保障。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档