背景

最近在为 流苏云计算 的计算节点写一个 Nova 计算组件、Ceilometer 监控组件的辅助系统,本身没有对外提供 Api 服务,仅仅通过消息队列通信,但是就是硬想整活,便将 Ward 用 Solon 重写整合进了 Leaf 组件。

Ward for Solon

但是这样就有了个大问题,这界面可不能随便就让陌生人访问到,应该怎么保护呢?密码?不行,太 Low 了,咱还是要整活,于是便采用了 用户名 + TOTP 的方式。

实现??

懒~

所以我才不会选择用网上的代码片段来拼 TOTP 实现,咱直接用现成的:# jchambers/java-otp,实现 TOTP 不是轻轻松松的么!

final TimeBasedOneTimePasswordGenerator totp = new TimeBasedOneTimePasswordGenerator();
final KeyGenerator keyGenerator = KeyGenerator.getInstance(totp.getAlgorithm());

// Key length should match the length of the HMAC output (160 bits for SHA-1, 256 bits
// for SHA-256, and 512 bits for SHA-512). Note that while Mac#getMacLength() returns a
// length in _bytes,_ KeyGenerator#init(int) takes a key length in _bits._
final int macLengthInBytes = Mac.getInstance(totp.getAlgorithm()).getMacLength();
keyGenerator.init(macLengthInBytes * 8);

final SecretKeySpec secretKey = keyGenerator.generateKey();

只要拿着生成的密钥生成二维码用 Google Authenticator 扫一下不就能用了!哎,等等,SecretKeySpec 是个啥,我咋从来没见过?toString() 一下?别说运行了,IDEA 都提示这样不行了,这是咋回事?

实现?

网上查过资料以后发现,SecretKeySpec 可以通过 getEncoded() 获取到内部的密钥,是个 byte[] 形式的,这不好说嘛 new String(secretKey.getEncoded()),运行试试:

 L3Û5¯0Ûæù?Þ›®zKå

???

这啥?算了管它呢,套进 URL 里面生成二维码扫一下试试:

otpauth://totp/Ying?secret= L3Û5¯0Ûæù?Þ›®zKå&issuer=SakuraImpression&period=30&algorithm=HmacSHA1&digits=6

进不去 ,怎么想都进不去吧

那那那,咱用 Base64 加密下试试?

进不去 ,怎么想都进不去吧

实现

果然,图快是快不了的,还是看看别人的代码怎么实现的吧:

/**
 * 生成唯一密钥
 *
 * @return
 */
public static String generateSecretKey() {
    // UUID + 4位随机字符生成唯一标识
    String uniqueId = UUID.randomUUID() + RandomStringUtils.randomAlphabetic(4);
    return new String(new Base32().encode(uniqueId.getBytes()));
}

Base32?好家伙原来是 Base32 加密。真是未曾设想的道路,那么这样我们就能或得到真正的 Secret 了:

new Base32().encodeToString(secretKey.getEncoded());

撒花~

Base32 为 Apache Common Codec 包中提供的类,需要 Maven 导入哦!

最后修改:2022 年 02 月 19 日
如果觉得我的文章对你有用,请随意赞赏