php - JWT/OAuth 令牌签名验证失败
问题描述
有一个基于 .NET Framework 的 Windows 桌面应用程序,它与基于 PHP 的后端网站进行通信。在尝试验证 .NET 应用程序向 PHP 网站提供的令牌时,我遇到了永久签名验证失败(openssl_verify
返回0
)。
.NET Framework 应用程序使用Microsoft 身份验证库(MSAL) 针对 Azure Active Directory (AAD) 对用户进行身份验证。该库将令牌作为字符串返回。应用程序将此令牌作为其请求的一部分发送到基于 PHP 的服务器。
var 结果 = 等待 Application.AcquireTokenAsync(范围).ConfigureAwait(false);
var token = result.AccessToken;
headers.Add("X-Auth-AAD-Token", token);
基于 PHP 的服务器网站接收 .NET 应用程序提供的令牌。然后,它会尝试验证令牌并在从 Azure Active Directory 请求用户数据时使用它。PHP 网站使用Networg/oauth2-azure库,它是通用thephpleage/oauth2-client库的提供者,然后使用firebase /php-jwt库来处理 JWT 令牌。
PHP 应用程序实例化一个Azure
提供者并调用
$provider->validateAccessToken($token);
$token
从 .NET 应用程序接收到的字符串在哪里。该方法调用
$keys = $this->getJwtVerificationKeys();
(array)JWT::decode($accessToken, $keys, ['RS256'])
从端点$keys
检索到的公钥数组在哪里。https://login.windows.net/common/discovery/keys
然后JWT::decode
将令牌拆分为标头、有效负载和签名,对其进行解码,选择正确的公钥并验证签名:
public static function decode($jwt, $key, array $allowed_algs = array())
$tks = explode('.', $jwt);
list($headb64, $bodyb64, $cryptob64) = $tks;
$header = static::jsonDecode(static::urlsafeB64Decode($headb64))
$sig = static::urlsafeB64Decode($cryptob64);
$key = $key[$header->kid];
static::verify("$headb64.$bodyb64", $sig, $key, $header->alg);
在哪里jsonDecode
打电话
$obj = json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
看起来urlsafeB64Decode
像
public static function urlsafeB64Decode($input)
$remainder = strlen($input) % 4;
if ($remainder) {
$padlen = 4 - $remainder;
$input .= str_repeat('=', $padlen);
}
return base64_decode(strtr($input, '-_', '+/'));
然后该verify
方法尝试通过调用来验证签名openssl_verify
。
private static function verify($msg, $signature, $key, $alg)
list($function, $algorithm) = static::$supported_algs[$alg]; // list('openssl', 'SHA256')
openssl_verify($msg, $signature, $key, $algorithm);
openssl_verify函数返回,表示0
签名验证失败(不匹配)。
我究竟做错了什么?如何解决?
编辑:除非为“我”颁发令牌,否则我不应该验证签名。由于我检查的令牌的范围是 Graph API,因此应该只有 Graph API 来验证它。在我将请求的令牌范围更改为 Web 应用程序后,签名会按预期进行验证。
解决方案
在decode
函数中,该语句返回与您从https://login.windows.net/common/discovery/keys$key = $key[$header->kid];
获得的标头参数对应的密钥。kid
由于上述 url 提供的密钥位于 JWK 格式下,因此该语句将返回如下内容:
{
"kty": "RSA",
"use": "sig",
"kid": "-sxMJMLCIDWMTPvZyJ6tx-CDxw0",
"x5t": "-sxMJMLCIDWMTPvZyJ6tx-CDxw0",
"n": "rxlPnqW6fNuCbdrhDEzwGJVux3iPvtt_8r-uHHIKa7C_b_ux5hewNMS91SgUPZOrsqb54uHj_7INWKqKEtFc4YP83Fhss_uO_mT97czENs4zWaSN9Eww_Fz36xq_uZ65750lHKwXQJ1A_pe-VOgNlPg8ECi7meQDJ05r838eu1jpKFjxkQrdRFTLgYtRQ7TxX-zzRyoRR8iqJc6Rvnijh19-YfWtBsCI1r127SFakUBrY_ZKsKyE9KNWUL7H65EyFRNgK80XfYvhQlGw3-Ajf28fi71wW-BypK1bTCArzwX7zgF3H6P1u8PKosSOSN_Q9-Qc9X-R_Y-3bOpOIiLOvw",
"e": "AQAB",
"x5c": [
"MIIDBTCCAe2gAwIBAgIQKOfEJNDyDplBSXKYcM6UcjANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE4MTIyMjAwMDAwMFoXDTIwMTIyMjAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK8ZT56lunzbgm3a4QxM8BiVbsd4j77bf/K/rhxyCmuwv2/7seYXsDTEvdUoFD2Tq7Km+eLh4/+yDViqihLRXOGD/NxYbLP7jv5k/e3MxDbOM1mkjfRMMPxc9+sav7meue+dJRysF0CdQP6XvlToDZT4PBAou5nkAydOa/N/HrtY6ShY8ZEK3URUy4GLUUO08V/s80cqEUfIqiXOkb54o4dffmH1rQbAiNa9du0hWpFAa2P2SrCshPSjVlC+x+uRMhUTYCvNF32L4UJRsN/gI39vH4u9cFvgcqStW0wgK88F+84Bdx+j9bvDyqLEjkjf0PfkHPV/kf2Pt2zqTiIizr8CAwEAAaMhMB8wHQYDVR0OBBYEFC//HOy7pEIKtnpMj4bEMA3oJ39uMA0GCSqGSIb3DQEBCwUAA4IBAQAIYxZXIpwUX8HjSKWUMiyQEn0gRizAyqQhC5wdWOFCBIZPJs8efOkGTsBg/hA+X1fvN6htcBbJRfFfDlP/LkLIVNv2zX4clGM20YhY8FQQh9FWs5qchlnP4lSk7UmScxgT3a6FG3OcLToukNoK722Om2yQ1ayWtn9K82hvZl5L3P8zYaG1gbHPGW5VlNXds60jIpcSWLdU2hacYmwz4pPQyvNOW68aK/Y/tWrJ3DKrf1feDbmm7O5kpWVYWRpah+i6ePjELNkc2Jr+2DchBQTIh9Fxe8sz+9iOyLh9tubMJ+7RTs/ksK0sQ1NVScGFxK+o5hFOOMK7y/F5r467jHez"
]
}
OpenSSl 不能直接使用 JWK。您必须将其转换为 PEM 或 DER 公钥文件。希望这种密钥格式已经包含在 JWK 本身中:它包含在x5c
参数中的第一个 X.509 证书中。
你只需要
- 稍微修改一下以获得有效的 X.509 证书。
$certificate = '-----BEGIN CERTIFICATE----'.PHP_EOL;
$certificate .= chunk_split($key['x5c'][0], 64, PHP_EOL);
$certificate .= '-----END CERTIFICATE-----'.PHP_EOL;
- 从此证书中获取公钥
$publicKey = openssl_pkey_get_public($certificate);
- 将该公钥与
openssl_verify
函数一起使用
static::verify("$headb64.$bodyb64", $sig, $publicKey, $header->alg);
推荐阅读
- android - Android如何让这种“波浪”像自定义设计的布局背景?
- javascript - 我的 smtp 密码包含特殊字符给出错误
- image - 图像宽高对迁移学习模型精度的影响
- android - 特定用户的 Firebase 数据库集合
- c# - EF Core ChangeTracker() 修改实体的当前值和原始值相同
- postgresql - docker compose后Postgresql连接失败
- mysql - 获取表的所有列并用 CASE 语句中的值替换一列
- authorization - 如何在 Cognito 中使用自定义权限
- css - 基于剪辑路径计算的 100% 半径是多少
- assembly - 我想知道如何计算计算机系统中给定虚拟机命令的跳转指令