4

фона

Google Authenticator код не совпадает с сервером сгенерированного кода


Я в настоящее время работаю в системе двухфакторной аутентификации, где пользователь в состоянии проверить подлинность с помощью своего смартфона. Прежде чем пользователь сможет использовать свое устройство, ему необходимо сначала его проверить. Для этого им нужно отсканировать QR-код, который я им даю, и ввести код, который впоследствии будет показан.

Проблема


Сканирование с QR-код работает отлично, и он получает правильно читать приложение Google Authenticator. Однако сгенерированные коды не соответствуют тем, которые я генерирую на сервере.

Что есть я пытался


Я попробовал несколько вещей, в надежде найти мою проблему.

  1. Я попытался непосредственно вставив как секрет по умолчанию: 'thiswasmysecretkeyused' и base64.b32encode() кодируются версия секрета: 'ORUGS43XMFZW26LTMVRXEZLUNNSXS5LTMVSA====' в приложении Google Authenticator, но оба эти сгенерированные коды, отличные от сервера.

  2. Я прочитал, что конечный ==== с ключа может привести к его неработоспособности, поэтому я попытался добавить его без них. По-прежнему нет хороших результатов (они генерируют одни и те же коды)

  3. Я попытался использовать другой алгоритм для генерации кодов TOTP, поскольку в маловероятном случае, если алгоритм, который я использую (django-otp), неверен. Другой использованный мною алгоритм был взят из ответа this. Оба алгоритма генерировали одни и те же коды при использовании одного и того же ключа.

  4. Я проверил, какое время на моей системе было. Я видел, что операционная система показывала 15:03, как и мой смартфон. После сброса времени в python с time.time() и datetime.datetime.now() я увидел, что время возврата было на один час ниже времени операционной системы; показывая 14:03. Я попытался добавить 3600 секунд в метку времени, используемую для генерации кода, но безрезультатно.

  5. Я пробовал несколько других вещей, но не могу вспомнить, кем они были.

  6. Я искал код, который принимает ключи в 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 actual totp, сообщите мне.

Ошибки


Нет ошибок.

Догадки


Подозреваю, что я должен идти куда-то не так с генерации ключа или с переходом ключ к Google Authenticator. Так как даже ручная установка ключа в Google Authenticator не позволяет сгенерировать правильные коды. Google Authenticator делает что-то еще с ключом после его сохранения, например, с добавлением пользователя?

Я также заметил, что в другом алгоритме я использовал, что секрет там сначала декодируется;

key = base64.b32decode(secret, True) 

Является ли мой оригинальный ключ (хэш SHA512) неправильным? Должен ли я или не кодировать его с помощью base64.b32encode()? Если я попытаюсь отсканировать QR-код, сгенерированный без кодирования хеша, Google Authenticator говорит, что он не признает его как (действительный) ключ.

+0

Параметр 'base64.base32encode()' строка является правильным, поэтому, возможно, выкладываю тот, который не соответствует также. –

+0

Я предполагаю, что вы попытались реализовать, как показано в [этих примерах] (https://pythonhosted.org/django-otp/auth.html#authenticating-users)? –

+0

@ l'L'l Не могли бы вы рассказать о том, что вы имеете в виду? Я знаю, что ключи правильны (насколько они одинаковы), но я должен отправлять строчную кодировку 'base64.b32encode() в Google Authenticator? Что вы подразумеваете под «той, которая не соответствует»? – Bono

ответ

6

Хорошо, после копания через the code of Google Authenticator Я, наконец, нашел то, что я делал неправильно.

Ключ кодирования

Просто так ясно: Google Authenticator делает ожидать base32 закодированную строку в секрете. Поэтому, вводите ли вы его вручную или с помощью QR-кода, вы должны убедиться, что ваш секрет является строкой, кодированной base32, когда вы передаете ее Google Authenticator.

От EnterKeyActivity:

/* 
* Verify that the input field contains a valid base32 string, 
* and meets minimum key requirements. 
*/ 
private boolean validateKeyAndUpdateStatus(boolean submitting) { 
    //... 
} 

Хранение

Google Authenticator хранит ключ вы даете его в базе данных, как есть. Таким образом, это означает, что он хранит строку вашего тайна непосредственно в базе данных.

От EnterKeyActivity:

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(); 
    } 
    //... 
} 

От AuthenticatorActivity:

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 строку, поэтому он может использовать подлинный секрет.

От OtpProvider:

private String computePin(String secret, long otp_state, byte[] challenge) throws OtpSourceException { 
    //... 

    try { 
     Signer signer = AccountDb.getSigningOracle(secret); 
     //... 
    } 
} 

От AccountDb:

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 также использовал это. Оглядываясь назад, это, конечно, очень логично, но я не мог найти слишком много информации об этом. Надеюсь, это поможет еще нескольким людям в будущем.

TL; DR

Убедитесь, что секрет/ключ вы передаете Google Authenticator является base32 строка в кодировке. Убедитесь, что на стороне сервера вы не используете кодированную строку base32, но декодированную строку. В Python вы можете кодировать и декодировать вам секретный ключ/следующим образом:

import base64 

base64.b32encode(self.key) 
base64.b32decode(self.key) 
Смежные вопросы