Cognito php 密码 SRP 验证程序质询返回不正确的用户名和密码

问题描述

我有一个 PHP 网页,我在其中配置了 aws sdk 并实例化了 Cognito 客户端 客户端也配置了客户端密钥,并且启用了身份验证流 USER_PASSWORD_AUTH、USER_SRP_AUTH。 具有基本身份验证流类型 USER_PASSWORD_AUTH 的initialAuth 对我来说很好用。 以下是我尝试的示例用法 -

$result = $cognito->initiateAuth([
'AuthFlow' => 'USER_PASSWORD_AUTH','ClientId' => '<client_id>','UserPoolId' => '<pool_id>','AuthParameters' => [
'USERNAME' => '<username>','PASSWORD' => "<password>",'SECRET_HASH' => base64_encode(hash_hmac('sha256','<username>' . '<client_id>','<client_secret>',true))
]
]);

有了这个,我们得到了一个带有访问和 id 令牌的成功响应, 但是当我们像这样使用 USER_SRP_AUTH 方法尝试使用相同的参数时 -

$srp = new srp();
$a = $srp->getRandomSeed();
$A = $srp->generateA($a);
$result = $cognito->initiateAuth([
'AuthFlow' => 'USER_SRP_AUTH','SRP_A' => $A,true))
]
]);
$date = date('D M d H:i:s')." UTC 2021";
$challengeParameters = $result->get("ChallengeParameters");
$s = $srp->getRandomSeed();
$x = $srp->generateX($s,$challengeParameters['USER_ID_FOR_SRP'],'<password>');
$S = $srp->generateS_Client($A,$challengeParameters['SRP_B'],$a,$x);
$K = $srp->generateK($S);
$response = $cognito->respondToAuthChallenge([
"ChallengeName" => "PASSWORD_VERIFIER","ClientId" => '<client_id>',"ChallengeResponses" => [
"TIMESTAMP" => $date,"USERNAME" => $challengeParameters['USER_ID_FOR_SRP'],"PASSWORD_CLaim_SECRET_BLOCK" => $challengeParameters['SECRET_BLOCK'],"PASSWORD_CLaim_SIGNATURE" => hash_hmac('sha256',$K,$challengeParameters['SALT']),$challengeParameters['USER_ID_FOR_SRP'] . '<client_id>',true))
]
]);

但是用这种方法我总是得到这个错误 -

NotAuthorizedException Error executing "RespondToAuthChallenge" on "https://cognito-idp.us-east-1.amazonaws.com"; AWS HTTP error: Client error: `POST https://cognito-idp.us-east-1.amazonaws.com` resulted in a `400 Bad Request` response: {"__type":"NotAuthorizedException","message":"Incorrect username or password."} NotAuthorizedException (client): Incorrect username or password. - {"__type":"NotAuthorizedException","message":"Incorrect username or password."}

现在我也尝试将 challengeParams['USER_ID_FOR_SRP'] 替换为 <username>,但仍然出现相同的错误

那么,这里的任何人都可以帮我弄清楚问题是什么,并可能也尝试帮助解决它吗?

解决方法

Cognito 中 SRP 的问题在于它不是根据 RFC5054 的 SRP。

如果您使用任何标准库来处理用于 Cognito 的 SRP,则它不会起作用。另外,PHP 的 SDK 不支持 SRP。

以下是根据 RFC5054 的 SRP 的工作原理(非常简化):

客户:

  • 计算 SRP A
  • 将用户名和 SRP_A 发送到服务器

服务器:

  • 计算 SRP_B
  • 向客户端发送 SRP_B 和盐

客户:

  • 计算会话密钥
  • 向服务器发送会话密钥证明

服务器:

  • 计算会话密钥
  • 比较服务器会话密钥和客户端会话密钥的证明
  • 将服务器的会话密钥证明发送给客户端

客户:

  • 验证服务器的会话密钥证明

当双方都能够确认他们拥有的会话密钥有效时,就假定用户通过了身份验证。

这是在 Cognito 中的样子:

客户:

  • 计算 SRP_A
  • 将用户名和 SRP_A 发送到服务器

服务器:

  • 计算 SRP_B
  • 向客户端发送 SRP_B、salt 和 SECRET_BLOCK

此时我们已经可以看到这与 RFC 不同。这个秘密块是特定于 Cognito 而不是 RFC 的。因此,接下来的任何计算都需要以不同的方式进行。

实现过程相当冗长,所以我将粘贴一个指向 Gist 的链接。它是由 Lynh 来自 Python 的端口,而后者又基于 AWS SDK sample for Java

https://gist.github.com/jenky/a4465f73adf90206b3e98c3d36a3be4f