使用aerogear生成totp
2017-12-14 00:00
471 查看
序
本文主要讲述一下如何使用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进行验证了。实例
生成客户端密钥的二维码
String secret = Base32.random(); Totp totp = new Totp(secret); String uri = totp.uri(account);
将这个uri作为二维码的信息,即可。
/** * 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或阿里云的身份宝均支持这种格式的识别。
校验
boolean isValid = totp.verify(code);
其源码如下
/** * 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); 7fe0 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.javaprivate 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
相关文章推荐
- 常用代码块:使用时间生成数据库文件名
- 使用AutoMake轻松生成Makefile
- 使用Python生成Shell命令,批量执行程序代码解析
- Linux crash dump文件生成与使用
- Android桌面快捷方式图标生成与删除,使用Intent与launcher交互
- 使用HTML5画布(canvas)生成阴影效果
- 如何使用CodeSmith批量生成代码
- lib 和 dll 的区别、生成以及使用详解
- 通过 Bitmap Font Generator 生成 fnt 与 png 文件供 cocos2d-x 中 LabelBMFont 使用达到以图片表现数字
- [PHP学习]TP5开篇学习-TP5中的build配置目录及自动生成使用问题
- 怎样使用powerdesigner类图生成代码及反向
- 使用POI分段落生成纯Word动态模板并导入数据
- 使用Git Bash生成新的ssh key
- 使用php实现下载生成某链接快捷方式的解决方法
- 若不想使用编译器自动生成的函数,应该明确拒绝
- 使用Collections.emptyList()生成的List不支持add方法
- Hibernate单独使用 (二)表操作深入、主键、自动生成映射文件
- 使用vscode,根据vue基础模板文件生成代码
- 在VS2010上使用C#调用非托管C++生成的DLL文件
- hibernate中使用schemaExport生成数据表报错解决方法