Как мне генерировать одноразовые пароли (OTP/HOTP)?

Мы решили начать работу над многофакторной аутентификацией, выпустив приложение для iPhone, Android и Blackberry для наших клиентов.

Думайте Google Authenticator для разовой системы паролей.

Я могу получить способ создания уникальной строки с помощью хэширования с использованием SALT на основе секретного ключа учетной записи и серийного номера устройства (или другого уникального идентификатора).

Но кто-нибудь знает, как вы могли бы создать уникальный, короткий номер, как это делает google? И/или у кого-нибудь есть хорошие ссылки на статьи о достижении такого рода вещей?

Большое спасибо

Ответы

Ответ 1

В конце концов я обнаружил, что это было хорошо задокументировано в RFC 4226 и в отношении целочисленного преобразования это можно сделать с помощью побитового операция показанная на стр. 7, по существу, такая же, как в приведенном ниже ответе.

Был fooobar.com/questions/395964/... относительно этого в контексте С#, который, возможно, стоит прочитать, если вы находитесь в аналогичной позиции.

В С# я в основном, hashed идентификатор времени (т.е. текущее время в секундах, деленное на 30, чтобы получить длинный, который действителен для текущего 30-секундного интервала). Затем хэшировал это, используя мой секретный ключ как СОЛЬ.

И затем...

// Use a bitwise operation to get a representative binary code from the hash
// Refer section 5.4 at http://tools.ietf.org/html/rfc4226#page-7            
int offset = hashBytes[19] & 0xf;
int binaryCode = (hashBytes[offset] & 0x7f) << 24
    | (hashBytes[offset + 1] & 0xff) << 16
    | (hashBytes[offset + 2] & 0xff) << 8
    | (hashBytes[offset + 3] & 0xff);

// Generate the OTP using the binary code. As per RFC 4426 [link above] "Implementations MUST extract a 6-digit code at a minimum 
// and possibly 7 and 8-digit code"
int otp = binaryCode % (int)Math.Pow(10, 6); // where 6 is the password length

return otp.ToString().PadLeft(6, '0');

Для тех из вас, кто не знал, Google Authenticator - проект с открытым исходным кодом - вы можете просмотреть исходный код здесь.

Ответ 2

Ну, это не , чтобы быть уникальным. Он просто должен иметь справедливую часть энтропии. Это означает, что шансы получить одну и ту же строку довольно низки.

Один из способов сделать это - взять ваш хэш и отрезать определенное количество целых чисел:

var hash = sha1(salt + device + secretKey);
var numbers = base_convert(hash, 16, 10); // Convert hex string to a integer
var key = numbers % 100000; // Limit to 5 digits (you can change this on need)

Просто запомните, чтобы выставить номер, чтобы он начинался с буквального 0, если он слишком короткий.