私はTOTPクラスをいくつか実装しましたが、それらはすべて間違った出力を生成します。以下では、最もシンプルなコードを使用しました。時間ベースのOTP生成で間違った鍵が生成されるC#
Google認証システムと同じように実装して動作させたいと思います(コードhttps://gauth.apps.gbraad.nl/#mainなど)。
私は、アプリケーションのフロントエンドで、 "IJAU4QKOIFFUKWJRGIZQ ===="のbase32文字列に変換する秘密の "BANANAKEY123"を入力します。
以下のコンストラクタで、キーは「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();
}
}
、あなたの 'Base32Encode'方法は非常に非効率(' TOUPPER() 'と' ToCharArray() 'の呼び出しは不要です)、代わりに' STRING'と 'Aggregate'を使用しています'StringBuilder'の新しい文字列の割り当てが過剰になり(O(n^n)時間で実行されます)。 – Dai
あなたの「秘密鍵」に文字「1」が含まれていますが、それはあなたの 'allowedCharacters'セットにはないというこの投稿のタイプミスですか? –