Ошибка проверки подписи токена JWT / OAuth - PullRequest
0 голосов
/ 21 января 2019

Существует настольное приложение Windows на основе .NET Framework, которое взаимодействует с внутренним веб-сайтом на основе PHP.Я испытываю постоянные сбои проверки подписи при попытке проверить токены, предоставленные приложением .NET на веб-сайт PHP (openssl_verify возвращает 0).

  1. .NET Frameworkиспользует Microsoft Authentication Library (MSAL) для аутентификации пользователя в Azure Active Directory (AAD).Библиотека возвращает токен в виде строки.Приложение отправляет этот токен как часть своего запроса на сервер на основе PHP.

    var result = await Application.AcquireTokenAsync (scopes) .ConfigureAwait (false);

    var token = result.AccessToken;

    headers.Add ("X-Auth-AAD-Token", токен);

  2. Веб-сайт сервера на основе 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 должен его проверять.После того как я изменил область действия токена, запрошенного для веб-приложения, подпись проверяется, как и ожидалось.

1 Ответ

0 голосов
/ 23 января 2019

В функции decode оператор $key = $key[$header->kid]; возвращает ключ, соответствующий параметру заголовка kid, который вы получили от https://login.windows.net/common/discovery/keys.

Поскольку ключи, указанные в 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: он включен в первый сертификат X.509 в параметре x5c.

Вам просто нужно

  • Немного измените его, чтобы получить действительный сертификат 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);
...