0

現在、多くの TOTP クラスを実装していますが、それらはすべて間違った出力を生成します。以下に、最も単純なものに使用したコードを投稿しました。

実装して Google Authenticator と同じように動作させたいと思います - たとえば、コードhttps://gauth.apps.gbraad.nl/#mainのようにします。

私が実現したいのは、アプリケーションのフロントエンドで、ユーザーが秘密の「BANANAKEY123」を入力し、これが「IJAU4QKOIFFUKWJRGIZQ====」の base32 文字列に変換されることです。

キーの下のコンストラクターでは、「BANANAKEY123」になります。しかし、何らかの理由で、このコードでは GAuth OTP ツールと同じ OTP キーが生成されません。

合理的な間違いは次の 2 つだけです。

var secretKeyBytes = Base32Encode(secretKey);

が間違っているか、タイミング関数が間違っています。両方をチェックしましたが、どちらにも欠陥は見つかりませんでした。誰かが私を正しい方向に助けてくれますか?ありがとうございました!

    public class Totp
{
    private readonly int digits = 6;
    private readonly HMACSHA1 hmac;
    private readonly HMACSHA256 hmac256;
    private readonly Int32 t1 = 30;
    internal int mode;

    private string secret;

    private const string allowedCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";

    public Totp(string key, int mode)
    {
        secret = key;
        this.mode = mode;
    }

    // defaults to SHA-1
    public Totp(string key)
    {
        secret = key;
        this.mode = 1;
    }


    public Totp(string base32string, Int32 t1, int digits) : this(base32string)
    {
        this.t1 = t1;
        this.digits = digits;
    }

    public Totp(string base32string, Int32 t1, int digits, int mode) : this(base32string, mode)
    {
        this.t1 = t1;
        this.digits = digits;
    }

    public String getCodeString()
    {
        return GetCode(this.secret, GetInterval(DateTime.UtcNow));
    }

    private static long GetInterval(DateTime dateTime)
    {
        TimeSpan elapsedTime = dateTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        return (long)elapsedTime.TotalSeconds / 30;
    }

    private static string GetCode(string secretKey, long timeIndex)
    {
        var secretKeyBytes = Base32Encode(secretKey);
        HMACSHA1 hmac = new HMACSHA1(secretKeyBytes);
        byte[] challenge = BitConverter.GetBytes(timeIndex);
        if (BitConverter.IsLittleEndian) Array.Reverse(challenge);
        byte[] hash = hmac.ComputeHash(challenge);
        int offset = hash[19] & 0xf;
        int truncatedHash = hash[offset] & 0x7f;
        for (int i = 1; i < 4; i++)
        {
            truncatedHash <<= 8;
            truncatedHash |= hash[offset + i] & 0xff;
        }
        truncatedHash %= 1000000;
        return truncatedHash.ToString("D6");
    }

    private static byte[] Base32Encode(string source)
    {
        var bits = source.ToUpper().ToCharArray().Select(c =>
            Convert.ToString(allowedCharacters.IndexOf(c), 2).PadLeft(5, '0')).Aggregate((a, b) => a + b);
        return Enumerable.Range(0, bits.Length / 8).Select(i => Convert.ToByte(bits.Substring(i * 8, 8), 2)).ToArray();
    }
}
4

1 に答える 1