我尝试在Google Authenticator应用程序中直接插入默认secret:'thiswasmysecretkeyused'
和secret的base64.b32encode()
编码版本'ORUGS43XMFZW26LTMVRXEZLUNNSXS5LTMVSA===='
,但是它们都生成与服务器不同的代码。
我读到,====
从密钥开始的尾随可能会导致它不起作用,因此我也尝试添加不带这些字符的密钥。仍然没有良好的结果(它们生成相同的代码)
我尝试使用另一种算法生成TOTP代码,因为在极少数情况下,我正在使用的算法(django-otp)不正确。所用的不同算法,我从拍摄这个答案。当使用相同的密钥时,两种算法都生成相同的代码。
我检查了系统时间。我看到操作系统显示的15:03
就像我的智能手机一样。在将时间与python一起转储之后time.time()
,datetime.datetime.now()
我看到返回的时间比操作系统时间晚了一个小时;显示14:03
。我尝试3600
在用于代码生成的时间戳中增加几秒钟,但无济于事。
我尝试了其他几件事,但还不太记得它们是什么。
我在Google Authenticator中查找了接受密钥的代码,并验证了它是否需要base32字符串。据我所知,我对密钥的编码是正确的。从代码(EnterKeyActivity.java,第78行)中:
验证输入字段包含有效的base32字符串
def generate_shared_key(self):
# create hash etc.
return base64.b32encode(hasher.hexdigest())
生成QR码;
key = authenticator.generate_shared_key()
qrcode = pyqrcode.create('otpauth://totp/someurl.nl?secret=' + key)
生成TOTP代码;
def generate_code(self, drift_steps=0, creation_interval=30, digits=6, t0=0):
code = str(totp(self.generate_shared_key(), creation_interval, timestamp, digits, drift_steps))
return code.zfill(digits)
如果您还需要其他代码,例如django-otp实际的totp生成代码,请告诉我。
我还注意到,在我使用的其他算法中,那里的秘密首先被解码。
key = base64.b32decode(secret, True)
我的原始密钥(SHA512哈希)不正确吗?我应该还是不应该使用base64.b32encode()
?如果我尝试扫描生成的QR码而不对哈希值进行编码,则Google Authenticator表示无法将其识别为(有效)密钥。
好吧,在研究了Google Authenticator的代码之后,我终于发现我做错了。
显而易见,Google Authenticator确实希望将base32
编码后的字符串作为秘密。因此,无论您是手动输入还是通过QR码输入,base32
将其提供给Google Authenticator时,都必须确保您的机密是经过编码的字符串。
/*
* Verify that the input field contains a valid base32 string,
* and meets minimum key requirements.
*/
private boolean validateKeyAndUpdateStatus(boolean submitting) {
//...
}
Google身份验证器会将您提供的密钥原样存储在数据库中。因此,这意味着它将base32
您的机密字符串直接存储在数据库中。
private String getEnteredKey() {
String enteredKey = mKeyEntryField.getText().toString();
return enteredKey.replace('1', 'I').replace('0', 'O');
}
protected void onRightButtonPressed() {
//...
if (validateKeyAndUpdateStatus(true)) {
AuthenticatorActivity.saveSecret(this, mAccountName.getText().toString(), getEnteredKey(), null, mode, AccountDb.DEFAULT_HOTP_COUNTER);
exitWizard();
}
//...
}
static boolean saveSecret(Context context, String user, String secret, String originalUser, OtpType type, Integer counter) {
//...
if (secret != null) {
AccountDb accountDb = DependencyInjector.getAccountDb();
accountDb.update(user, secret, originalUser, type, counter);
//...
}
}
当Google Authenticator从数据库中检索到机密时,它将对base32
字符串进行解码,以便可以使用真正的机密。
private String computePin(String secret, long otp_state, byte[] challenge) throws OtpSourceException {
//...
try {
Signer signer = AccountDb.getSigningOracle(secret);
//...
}
}
static Signer getSigningOracle(String secret) {
try {
byte[] keyBytes = decodeKey(secret);
//...
}
}
private static byte[] decodeKey(String secret) throws DecodingException {
return Base32String.decode(secret);
}
我的错误是,在服务器端,我使用了base32
编码密钥来生成TOTP代码,因为我认为Google Authenticator也使用了该密钥。事后看来,这当然是很合逻辑的,但是我找不到太多有关此的信息。希望这会在将来帮助更多的人。
确保您传递给Google Authenticator的秘密/密钥是base32
编码字符串。确保在服务器端您没有使用base32
编码的字符串,而是解码的字符串。在Python中,您可以按以下方式对秘密/密钥进行编码和解码:
import base64
base64.b32encode(self.key)
base64.b32decode(self.key)
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句