Php JWT/OAuth令牌签名验证失败

Php JWT/OAuth令牌签名验证失败,php,.net,oauth-2.0,openssl,jwt,Php,.net,Oauth 2.0,Openssl,Jwt,有一个基于.NET Framework的Windows桌面应用程序,它与基于PHP的后端网站进行通信。在尝试验证.NET应用程序向PHP网站提供的令牌时,我遇到了永久性签名验证失败(openssl\u verifyreturns0) .NET Framework应用程序使用(MSAL)根据Azure Active Directory(AAD)对用户进行身份验证。库以字符串形式返回标记。应用程序将此令牌作为其请求的一部分发送到基于PHP的服务器 var result=await Applicati

有一个基于.NET Framework的Windows桌面应用程序,它与基于PHP的后端网站进行通信。在尝试验证.NET应用程序向PHP网站提供的令牌时,我遇到了永久性签名验证失败(
openssl\u verify
returns
0

  • .NET Framework应用程序使用(MSAL)根据Azure Active Directory(AAD)对用户进行身份验证。库以字符串形式返回标记。应用程序将此令牌作为其请求的一部分发送到基于PHP的服务器

    var result=await Application.AcquireTokenAsync(作用域)。ConfigureAwait(false)

    var token=result.AccessToken

    添加(“X-Auth-AAD-Token”,Token)

  • 基于PHP的服务器网站接收.NET应用程序提供的令牌。然后尝试验证令牌,并在从Azure Active Directory请求用户数据时使用它。PHP网站使用库,它是泛型库的提供者,然后泛型库使用库来处理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
    endpoint

    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);
    
    urlsafeb64解码

    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\u 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);
    
    函数返回
    0
    ,这意味着签名验证失败(不匹配)

    我做错了什么?如何修复它


    编辑:除非为“我”颁发令牌,否则我不应该验证签名。由于我检查的令牌的作用域是一个Graph API,因此只有Graph API应该验证它。在我将请求的令牌的范围更改为web应用程序后,签名将按预期进行验证。

    decode
    函数中,语句
    $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='----开始证书---'.PHP\u EOL;
    $certificate.=chunk\u split($key['x5c'][0],64,PHP\u EOL);
    $certificate.='----结束证书---'.PHP\u EOL;
    
    • 从该证书获取公钥
    $publicKey=openssl\u pkey\u get\u public($certificate);
    
    • 将该公钥与
      openssl\u verify
      功能一起使用
    static::verify($headb64.$bodyb64“,$sig,$publicKey,$header->alg);
    
    你好,谢谢。我对此进行了一些研究,发现
    openssl\u-verify
    也接受证书,因此在
    openssl\u-verify
    的当前实现中(即使它没有文档记录),也不需要提取公钥。我不知道为什么,我也没有立场,认为它是最好的实践,但通过证书或公钥在我的测试没有什么区别。同时,我最近知道,我不应该验证签名,除非令牌是颁发给“我”。由于我检查的令牌的作用域是一个Graph API,因此只有Graph API应该验证它。在我将请求的令牌的范围更改为web应用程序的范围后,签名将按预期进行验证。我无法说出使用证书和提取密钥之间的最佳区别。由于参数名为
    key
    ,我更喜欢处理密钥而不是证书
    与此同时,我最近了解到,除非为“我”颁发令牌,否则我不应该验证签名。
    。当然,如果您的应用程序不使用令牌(例如,它只是一个代理或存储库),则无需对其进行解析或验证。如果您的应用程序使用声明(例如在身份验证上下文中),则必须验证标头、声明和签名。