9

Amazon предоставляет IOS, Android и Javascript Cognito SDK, которые предлагают высокоуровневую аутентификацию пользователя., реализующий USER_SRP_AUTH с python boto3 для AWS Cognito

Например, см Use Case 4 здесь:

https://github.com/aws/amazon-cognito-identity-js

Однако, если вы используете Python/boto3, все вы получите являются парой примитивов: cognito.initiate_auth и cognito.respond_to_auth_challenge.

Я пытаюсь использовать эти примитивы вместе с идентификатором pysrp с помощью потока USER_SRP_AUTH, но то, что у меня есть, не работает.

Он всегда терпит неудачу с «Произошла ошибка (NotAuthorizedException) при вызове операции RespondToAuthChallenge: неправильное имя пользователя или пароль». (Поиск пары имени пользователя/пароля находится в JS SDK.)

Мое подозрение, что я строю ответ на вызов неправильно (шаг 3) и/или передавая шестнадцатеричные строки Congito, когда он хочет base64 или наоборот.

Кто-нибудь получил эту работу? Кто-нибудь видит, что я делаю неправильно?

Я пытаюсь скопировать поведение authenticateUser вызова, найденного в Javascript SDK:

https://github.com/aws/amazon-cognito-identity-js/blob/master/src/CognitoUser.js#L138

но я делаю что-то неправильно, и не могу понять, что.

#!/usr/bin/env python 
import base64 
import binascii 
import boto3 
import datetime as dt 
import hashlib 
import hmac 

# http://pythonhosted.org/srp/ 
# https://github.com/cocagne/pysrp 
import srp 

bytes_to_hex = lambda x: "".join("{:02x}".format(ord(c)) for c in x) 

cognito = boto3.client('cognito-idp', region_name="us-east-1") 

username = "[email protected]" 
password = "123456" 

user_pool_id = u"us-east-1_XXXXXXXXX" 
client_id = u"XXXXXXXXXXXXXXXXXXXXXXXXXX" 

# Step 1: 
# Use SRP lib to construct a SRP_A value. 

srp_user = srp.User(username, password) 
_, srp_a_bytes = srp_user.start_authentication() 

srp_a_hex = bytes_to_hex(srp_a_bytes) 

# Step 2: 
# Submit USERNAME & SRP_A to Cognito, get challenge. 

response = cognito.initiate_auth(
    AuthFlow='USER_SRP_AUTH', 
    AuthParameters={ 'USERNAME': username, 'SRP_A': srp_a_hex }, 
    ClientId=client_id, 
    ClientMetadata={ 'UserPoolId': user_pool_id }) 

# Step 3: 
# Use challenge parameters from Cognito to construct 
# challenge response. 

salt_hex   = response['ChallengeParameters']['SALT'] 
srp_b_hex  = response['ChallengeParameters']['SRP_B'] 
secret_block_b64 = response['ChallengeParameters']['SECRET_BLOCK'] 

secret_block_bytes = base64.standard_b64decode(secret_block_b64) 
secret_block_hex = bytes_to_hex(secret_block_bytes) 

salt_bytes = binascii.unhexlify(salt_hex) 
srp_b_bytes = binascii.unhexlify(srp_b_hex) 

process_challenge_bytes = srp_user.process_challenge(salt_bytes,       
                srp_b_bytes) 

timestamp = unicode(dt.datetime.utcnow().strftime("%a %b %d %H:%m:%S +0000 %Y")) 

hmac_obj = hmac.new(process_challenge_bytes, digestmod=hashlib.sha256) 
hmac_obj.update(user_pool_id.split('_')[1].encode('utf-8')) 
hmac_obj.update(username.encode('utf-8')) 
hmac_obj.update(secret_block_bytes) 
hmac_obj.update(timestamp.encode('utf-8')) 

challenge_responses = { 
    "TIMESTAMP": timestamp.encode('utf-8'), 
    "USERNAME": username.encode('utf-8'), 
    "PASSWORD_CLAIM_SECRET_BLOCK": secret_block_hex, 
    "PASSWORD_CLAIM_SIGNATURE": hmac_obj.hexdigest() 
} 

# Step 4: 
# Submit challenge response to Cognito. 

response = cognito.respond_to_auth_challenge(
    ClientId=client_id, 
    ChallengeName='PASSWORD_VERIFIER', 
    ChallengeResponses=challenge_responses) 
+0

Вы когда-нибудь получали эту работу? Я работаю над тем же самым в своем проекте. – man2xxl

+0

Нет, я ударил по нему немного дальше, но пока не повезло. Во время работы я создал настраиваемый лямбда ('DefineAuthChallenge'), который всегда всегда аутентифицирует пользователя в:' export.handler = function (event, context) {event.response.issueTokens = true; event.response.failAuthentication = false; context.done (null, event);} '. Это все, что мне нужно сейчас, так как я просто создаю прототип. Но я рассчитываю на то, что смогу в конечном итоге добиться этой работы. – billc

+0

man2xxl, ответ от руки armicron ниже. – billc

ответ

5

В вашей реализации много ошибок. Например:

  1. pysrp по умолчанию использует алгоритм SHA1. Он должен быть установлен на SHA256.
  2. _ng_const длина должна быть 3072 битами, и он должен быть скопирован из amazon-cognito-identity-js
  3. Там нет hkdf функции в pysrp.
  4. Ответ должен содержать secret_block_b64, а не secret_block_hex.
  5. Неверный формат временной отметки. %H:%m:%S означает «час: месяц: второй» и +0000 следует заменить на UTC.

Кто-нибудь получил эту работу?

Да. Он реализован в модуле warrant.aws_srp. https://github.com/capless/warrant/blob/develop/warrant/aws_srp.py

from warrant.aws_srp import AWSSRP 


USERNAME='xxx' 
PASSWORD='yyy' 
POOL_ID='us-east-1_zzzzz' 
CLIENT_ID = '12xxxxxxxxxxxxxxxxxxxxxxx' 

aws = AWSSRP(username=USERNAME, password=PASSWORD, pool_id=POOL_ID, 
      client_id=CLIENT_ID) 
tokens = aws.authenticate_user() 
id_token = tokens['AuthenticationResult']['IdToken'] 
refresh_token = tokens['AuthenticationResult']['RefreshToken'] 
access_token = tokens['AuthenticationResult']['AccessToken'] 
token_type = tokens['AuthenticationResult']['TokenType'] 

Обратите внимание, что aws_srp модуль не был слит в master отрасли еще.

authenticate_user метод поддерживает только PASSWORD_VERIFIER вызов. Если вы хотите ответить на другие проблемы, просто просмотрите документацию authenticate_user и boto3.

+0

спасибо armicron! – billc

2

К сожалению, это сложная проблема, так как вы не получите никаких намеков от службы в отношении вычислений (она в основном говорит, что не имеет права, как вы упомянули).

Мы работаем над улучшением опыта разработчиков, когда пользователи пытаются реализовать SRP самостоятельно на языках, где у нас нет SDK. Кроме того, мы пытаемся добавить больше SDK.

Как бы это ни звучало, я бы предложил использовать Javascript или Android SDK, исправить входы (SRP_A, SRP_B, TIMESTAMP) и добавить инструкции console.log в разных точках реализации, чтобы убедиться, что ваш вычисления аналогичны. Затем вы будете запускать эти вычисления в своей реализации и убедитесь, что вы получаете то же самое. Как вы предположили, подпись запроса пароля должна быть передана в качестве кодировки с кодировкой base64 в службу, так что это может быть одна из проблем.

Некоторые из проблем, с которыми я столкнулся при реализации этого метода, были связаны с различиями библиотек BigInteger (так, как они выполняют байтовое заполнение и преобразовывают отрицательные числа в байтовые массивы и обратно).

+0

Спасибо Ionut. Я надеялся, что кто-то увидит, что я сделал какой-то шаг, очевидно, неправильно, и я мог бы избежать болезненного процесса, который вы предлагаете, но я думаю, что я застрял. Спасибо за отзыв на PASSWORD_CLAIM_SIGNATURE. Я предполагаю, что вы имеете в виду, что base64 кодирует * raw bytes *, возвращенный из операции HMAC, правильно? (И не сказать, base64 * hexstring * этих байтов?) – billc

+1

Да, это то, что я имел в виду, base64 кодирует необработанные байты, возвращенные из операции hmac. –