You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用cryptonite对接Google Authenticator生成TOTP遇问题求助

Fixing TOTP Mismatch & Base32 Padding Issues with Cryptonite

Let's break down and fix your two main issues step by step:

1. You're Using the Wrong Key for totp

The biggest mistake in your code is that you're passing Base32-encoded bytes to totp, but the function expects the raw original secret bytes. Google Authenticator takes the Base32 string as input, decodes it back to the raw bytes, then generates the TOTP—your code was doing the exact opposite, which is why the codes never matched.

For context:

  • Your raw secret is "12345678901234567890"
  • Google Authenticator uses the Base32 version of this secret (without padding) to decode back to the raw bytes
  • Your code was converting the raw secret to Base32, then using that Base32 data as the secret for totp—this is like using a translated version of your password instead of the password itself.

2. Stop Using unpad for Base32 Padding

The unpad function from Crypto.Data.Padding is designed for block cipher padding schemes like PKCS7, not Base32's = padding. These are completely unrelated systems! To remove Base32 padding, just filter out the = characters from the encoded string—no fancy crypto functions needed.

Fixed Code

Here's your corrected code with inline explanations:

{-# LANGUAGE OverloadedStrings #-}
module TOTP ( genTOTP ) where
import qualified Basement.Imports as Base (String)
import Crypto.OTP (defaultTOTPParams, totp)
import Data.ByteArray
import Data.ByteArray.Encoding
import Data.Time.Clock.POSIX
import Data.ByteString.Char8 (filter)
import Prelude hiding (filter) -- Avoid conflict with Prelude's built-in filter

genTOTP :: IO ()
genTOTP = do
 time <- getPOSIXTime
 let secret = "12345678901234567890" :: Base.String
     -- Raw secret bytes (this is what totp actually requires)
     rawKey = convert secret :: Bytes
     -- Generate unpadded Base32 string for Google Authenticator input
     base32Key = filter (/= '=') $ convertToBase Base32 secret :: String
     -- Generate TOTP using the raw secret, not Base32-encoded data
     code = totp defaultTOTPParams rawKey (round time)
 print "Generated TOTP:"
 print code
 print "Unpadded Base32 key for Google Authenticator:"
 print base32Key

Why This Works

  • rawKey is the direct byte conversion of your original secret—this matches exactly what Google Authenticator decodes from the Base32 string.
  • base32Key filters out the = padding characters, which aligns with Google Authenticator's requirements (it ignores padding anyway, but removing it is good practice).
  • Now, when you input GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ into Google Authenticator, it will decode it back to the same rawKey your code uses, so the TOTP codes will match perfectly.

Quick Validation Test

If you test this at timestamp 1111111111 (a standard test value for this secret), both your code and Google Authenticator should return the code 050471—this confirms everything is working as expected.

内容的提问来源于stack exchange,提问作者James Burton

火山引擎 最新活动