реализация аутентификации пользователя SRP с помощью python boto3 для AWS Cognito
Amazon предоставляет iOS, Android и Javascript Cognito SDKs, которые предлагают высокоуровневую операцию аутентификации пользователя.
например, см. Вариант использования 4 здесь:
https://github.com/aws/amazon-cognito-identity-js
однако, если вы используете python / boto3, все, что вы получаете, это пара примитивов:cognito.initiate_auth
и cognito.respond_to_auth_challenge
.
Я пытаюсь использовать эти примитивы вместе с pysrp
lib аутентифицируется с 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 = "foobar@foobar.com"
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)
2 ответов
в вашей реализации много ошибок. Например:
-
pysrp
по умолчанию используется алгоритм SHA1. Он должен быть установлен в SHA256. -
_ng_const
длина должна быть 3072 бит, и она должна быть скопирована изamazon-cognito-identity-js
- нет hkdf на
pysrp
. - ответ должен содержать
secret_block_b64
, а неsecret_block_hex
. - неправильный формат метки времени.
%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
документация.
к сожалению, это сложная проблема, так как вы не получаете никаких намеков от службы в отношении вычислений (в основном это говорит не авторизовано, как вы упомянули).
мы работаем над улучшением опыта разработчика, когда пользователи пытаются реализовать SRP самостоятельно на языках, где у нас нет SDK. Кроме того, мы пытаемся добавить больше SDK.
Как бы сложно это ни звучало, я бы предложил взять Javascript или Android SDK, исправить входы (SRP_A, SRP_B, TIMESTAMP)и добавить консоль.инструкции журнала в различных точках реализации, чтобы убедиться, что ваши вычисления похожи. Затем вы запустите эти вычисления в своей реализации и убедитесь, что получаете то же самое. Как вы предположили, подпись утверждения пароля должна быть передана в службу как строка, закодированная в base64, чтобы это могло быть одной из проблем.
некоторые из проблем с которыми я столкнулся при реализации это типа BigInteger библиотечные различия (как они делают байтовое заполнение и преобразуют отрицательные числа в байтовые массивы и наоборот).