谷歌 OTP 生成 Java
Posted
技术标签:
【中文标题】谷歌 OTP 生成 Java【英文标题】:Google OTP Generation Java 【发布时间】:2017-03-15 02:27:45 【问题描述】:我目前正在尝试重新创建 Google 一次性密码生成器。我使用设置 Google Authenticator 时生成的共享密钥。 我尝试查看 Google Authenticator 的来源以及互联网上的所有内容,我发现与我的代码有很多相似之处,但我真的找不到我错的地方。 第一部分似乎是正确的。至于 hmac,我认为我不能在这里搞砸,但我可能错了。截断部分对我来说仍然有点模糊,我尝试了很多不同的实现,但我就是无法获得有效的 OTP。 (我正在使用 Google Authenticator 来比较结果)
private String truncateHash(byte[] hash)
int offset = hash[hash.length - 1] & 0xF;
long truncatedHash = 0;
for (int i = 0; i < 4; ++i)
truncatedHash <<= 8;
truncatedHash |= (hash[offset + i] & 0xFF);
truncatedHash &= 0x7FFFFFFF;
truncatedHash %= 1000000;
int code = (int) truncatedHash;
String result = Integer.toString(code);
for (int i = result.length(); i < 6; i++)
result = "0" + result;
return result;
private byte[] hmacSha1(byte[] value, byte[] keyBytes)
try
Mac mac = HmacUtils.getHmacSha1(keyBytes);
byte[] rawHmac = mac.doFinal(value);
return new Hex().encode(rawHmac);
catch (Exception e)
throw new RuntimeException(e);
public String GoogleAuthenticatorCode(String secret) throws UnsupportedEncodingException
Base32 base = new Base32();
byte[] key = base.decode(secret);
//Update from Andrew Rueckert's response
long value = new Date().getTime() / TimeUnit.SECONDS.toMillis(30);
byte[] data = new byte[8];
for (int i = 8; i-- > 0; value >>>= 8)
data[i] = (byte) value;
//
System.out.println("Time remaining : " + new Date().getTime() / 1000 % 30);
byte[] hash = hmacSha1(data, key);
return truncateHash(hash);
更新:
我尝试复制并粘贴来自 Andrew Rueckert 的回复链接的代码以及这个https://github.com/wstrange/GoogleAuth/blob/master/src/main/java/com/warrenstrange/googleauth/GoogleAuthenticator.java
和来自RFC 4226
的代码。这些都没有给我一个正确的OTP
谁能赐教?
【问题讨论】:
【参考方案1】:您的byte value[]
需要将时间的字节表示为 long,并且看起来它当前是该数字的字节表示为数字字符的字符串。而不是
Double time = floor(new Date().getTime() / 1000 / 30);
String message = String.valueOf(time.intValue());
byte[] value = message.getBytes("UTF-8");
byte[] hash = hmacSha1(value, key);
你会想要这样的:
// decimal truncation is free when dealing with int/long
long value = new Date().getTime() / 1000 / 30;
byte[] data = new byte[8];
for (int i = 8; i-- > 0; value >>>= 8)
data[i] = (byte) value;
byte[] hash = hmacSha1(data, key);
如果您想了解更多资源,我设法通过关注this guide 设置了 Google TOTP 实施。
【讨论】:
嘿,谢谢你的建议。但它似乎仍然不起作用。我什至尝试从您的链接以及github.com/wstrange/GoogleAuth/blob/master/src/main/java/com/… 和 RFC 4226 中的链接复制和粘贴代码。他们都没有给我正确的 OTP。 在hmacSha1
,而不是return new Hex().encode(rawHmac);
,尝试return rawHmac;
。我在实现中根本没有使用 Hex。【参考方案2】:
我解决了我的问题,所以我想我会把它贴在那里以防有人需要它。 部分原因是我使用的 Base32 类没有返回正确的密钥。截断也不正确。
它与 Google Authenticator 应用兼容。
import org.apache.commons.codec.binary.Hex;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class Authentication
Authentication() ;
private String truncateHash(byte[] hash)
String hashString = new String(hash);
int offset = Integer.parseInt(hashString.substring(hashString.length() - 1, hashString.length()), 16);
String truncatedHash = hashString.substring(offset * 2, offset * 2 + 8);
int val = Integer.parseUnsignedInt(truncatedHash, 16) & 0x7FFFFFFF;
String finalHash = String.valueOf(val);
finalHash = finalHash.substring(finalHash.length() - 6, finalHash.length());
return finalHash;
private byte[] hmacSha1(byte[] value, byte[] keyBytes)
SecretKeySpec signKey = new SecretKeySpec(keyBytes, "HmacSHA1");
try
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signKey);
byte[] rawHmac = mac.doFinal(value);
return new Hex().encode(rawHmac);
catch (Exception e)
throw new RuntimeException(e);
public String GoogleAuthenticatorCode(String secret) throws Exception
if (secret == null || secret == "")
throw new Exception("Secret key does not exist.");
long value = new Date().getTime() / TimeUnit.SECONDS.toMillis(30);
Base32 base = new Base32(Base32.Alphabet.BASE32, false, true);
byte[] key = base.fromString(secret);
byte[] data = new byte[8];
for (int i = 8; i-- > 0; value >>>= 8)
data[i] = (byte) value;
byte[] hash = hmacSha1(data, key);
return truncateHash(hash);
如果需要,我使用的 Base32 可在此处与项目的其余部分一起使用:https://github.com/Poncholay/OTPGenerator/blob/master/src/main/java/com/requireris/app/web/rest/Base32.java
【讨论】:
以上是关于谷歌 OTP 生成 Java的主要内容,如果未能解决你的问题,请参考以下文章