JWT-使用公钥和纯PHP

时间:2019-12-05 21:48:02

标签: php validation jwt microsoft-graph signature

很抱歉,篇幅较长,但我认为最好以有序方式提供尽可能多的信息。

我检查了我在Stack Overflow中发现的与此问题有关的所有问题,但是我仍然没有找到解决方法。

我正在编写自己的类以使用OAuth2连接到Microsoft Graph,但是我找不到一种方法来验证访问令牌签名(根据RFC 7519 S.7.2)。


注意:之所以尝试使用纯PHP 7实现此目的,是因为我尝试了所有可用的库,但它们对我不起作用。它们只是不运行,并且包含过多的膨胀软件。甚至是Microsoft的GitHub存储库。

背景:

我能够从Microsoft检索代码答复。我也可以解码令牌的内容。

我正在使用以下功能:

我的配置文件包含:

// This is the Application ID from the Azure App Registration
$CLIENT_ID              = '<id goes here>';
// This is the Application Secret from the Azure App Registration
$CLIENT_SECRET          = '<secret goes here>';
// This is the Azure Active Directory tenant ID
$DIRECTORYID            = '<AAD tenant id goes here>';
// We are requesting that the response be an authorization code appended to the URL
$RESPONSE_TYPE          = 'code';
// User.Read        - to be able to get the user profile details
// offline_access   - to be able to get a refresh token
$SCOPE                  = 'user.read offline_access';
// We are asking for an authorization code which we will use to get an access token
$GRANT_TYPE             = 'authorization_code';
// The place in our app that will handle all of the OAuth2 process.
$REDIRECT_URL           = 'https://localhost/index.php';
// This is where we request a token from Microsoft
// MAKE SURE THAT IS IS USING VERSION 2.0 or the audience will not match
$TOKEN_URL              = 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
// This is where we request authorization from Microsoft
$AUTH_URL               = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize';
// We will be pulling the user data from the Microsoft Graph v1.0
$USERDATAURL            = 'https://graph.microsoft.com/api/v1.0/me';
// This is where we pull the information for the URLs in this file
$OPENIDCONFIG_URL       = 'https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration';
// This is where Microsoft publishes the public keys used to validate the tokens
$KEYS_URL               = 'https://login.microsoftonline.com/common/discovery/keys';
  1. 要解码base64 url​​内容,请执行以下操作:
public function base64_url_decode( $input )
{
    return base64_decode( str_replace(['-', '_'], ['+', '/'], $input ) );
}
  1. 要解码收到的访问令牌,请执行以下操作:

    这将返回一个包含
    的数组
    1. 3个数组
      • 令牌有效性信息
      • 标题信息
      • 身体信息
    2. 1个字符串
      • base64_url解码的签名
public function decode_AccessToken( $token )
{
    $info = [ 'token_type' => $token['token_type'], 
              'scope' => $token['scope'], 
              'expires_in' => $token['expires_in'], 
              'ext_expires_in' => $token['ext_expires_in'] ];

    // Explode the token to get each of the three parts inside it (Header, Body, Signature)
    $parts = explode(".", $token['access_token']);

    // Decode the header as an Array
    $header = (array)json_decode( $this->base64_url_decode( $parts[0] ) );

    // Decode the Body as an Array
    $body = (array)json_decode( $this->base64_url_decode( $parts[1] ) );

    // Decode the Signature
    $signature = $this->base64_url_decode( $parts[2] );

    // Return value
    return ['Info' => $info,'Header' => $header, 'Body' => $body, 'Signature' => $signature];
}
  1. 我从Microsoft密钥端点提供的证书链[x5c]中检索公共密钥:
public function get_SigningKeys()
{
    $ch = curl_init();
    // Specify the URL to retrieve
    curl_setopt( $ch, CURLOPT_URL, $this->keys_url);
    // Receive a JSON response
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER, True );
    // Do not validate SSL Certificates, by default SSL doesn't work with CURL.
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    // execute request
    $result = curl_exec( $ch );

    curl_close($ch);

    return (array)json_decode( $result );
}

public function get_PublicKey( $decodedToken )
{
    $keys = $this->get_SigningKeys();

    // The ID of the key that was used
    $keyID = $decodedToken['Header']['kid'];

    foreach( $keys['keys'] as $key )
    {
        if( $key->kid === $keyID )
        {
            // Create the Plain Text Certificate Chain
            $plainTextKeyCert  = "-----BEGIN CERTIFICATE-----\r\n";
            $plainTextKeyCert .= chunk_split( str_replace( " ", "", $key->x5c[0] ), 64, "\r\n" );
            $plainTextKeyCert .= "-----END CERTIFICATE-----";
            // Read the Certificate Chain
            $cert              = openssl_x509_read( $plainTextKeyCert );
            // Extract the Public Key from the Certificate Chain
            $pubkey            = openssl_pkey_get_public( $cert );
            // Return the Public Key
            return openssl_pkey_get_details( $pubkey )['key'];
        }
    }

    return False;
}
  1. 我尝试使用openssl_verify验证签名:

    先决条件:
    1. 有效负载:
      • 由base64网址解码的“标头。正文”组成。
    2. 签名:
      • base64_url解码的签名
    3. 公钥
      • 在上一步中获得
    4. 确认Microsoft使用RS256对令牌进行签名。

采取的措施:
  1.使用openssl_verify尝试验证签名。

// Do the actual signature verification
// Get the Public Key
$theKey = $this->get_PublicKey( $decoded );

list( $header, $body, $signature ) = explode( '.', $accessToken['access_token'] );

$payload = utf8_decode( $this->base64_url_decode( $header ) . '.' . $this->base64_url_decode( $body ) );

$verified = openssl_verify( $payload, $decoded['Signature'], $theKey, 'RSA-SHA256' );

echo '<br><br>Verified: ' . $verified;

此步骤失败。

我不确定是否:
  1.我没有正确检索公共密钥。
  2.我没有在openssl_verify函数中使用正确的算法。
     [OPENSSL_ALGO_SHA256,RSA-SHA256,sha256WithRSAEncryption]
  3.其他?


检索到的证书如下:

-----BEGIN CERTIFICATE-----
MIIDBTCCAe2gAwIBAgIQbiJkXaenk61AKixVocnLRTANBgkqhkiG9w0BAQsFADAt
MSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4X
DTE5MTAwNTAwMDAwMFoXDTI0MTAwNDAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3Vu
dHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAJ2H9Y6Z+3BXUCtlbmXr6H5owYy15XTl3vdpOZLUkk4OV9LM
sB1phjNp+wgl28eAgrNNfu4BTVlHdR9x6NTrSiIapsYjzzEz4mOmRh1Bw5tJxit0
VEGn00/ZENniTjgeEFYgDHYwjrfZQ6dERBFiw1OQb2IG5f3KLtx92lUXeIZ7ZvTa
PkUpc4Qd6wQZmWgzPqWFocRsJATGyZzXiiXQUrc9cVqm1bws3P0lFBcqNtv+AKDY
KT5IRYLsyCkueQC9R6LUCsZVD7bVIkeQuA3iehJKIEAlk/e3j5E4VaCRs642ajb/
z9kByTl2xL2k0AeZGc8/Rcy7SQn0LBcJNZGp/SMCAwEAAaMhMB8wHQYDVR0OBBYE
FOLhl3BDPLNVYDe38Dp9JbUmd4kKMA0GCSqGSIb3DQEBCwUAA4IBAQAN4XwyqYfV
dMl0xEbBMa/OzSfIbuI4pQWWpl3isKRAyhXezAX1t/0532LsIcYkwubLifnjHHqo
4x1jnVqkvkFjcPZ12kjs/q5d1L0LxlQST/Uqwm/9/AeTzRZXtUKNBWBOWy9gmw9D
EH593sNYytGAEerbWhCR3agUxsnQSYTTwg4K9cSqLWzHX5Kcz0NLCGwLx015/Jc7
HwPJnp7q5Bo0O0VfhomDiEctIFfzqE5x9T9ZTUSWUDn3J7DYzs2L1pDrOQaNs/YE
kXsKDP1j4tOFyxic6OvjQ10Yugjo5jg1uWoxeU8pI0BxY6sj2GZt3Ynzev2bZqmj
68y0I9Z+NTZo
-----END CERTIFICATE-----

检索到的公共密钥如下:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnYf1jpn7cFdQK2VuZevo
fmjBjLXldOXe92k5ktSSTg5X0sywHWmGM2n7CCXbx4CCs01+7gFNWUd1H3Ho1OtK
IhqmxiPPMTPiY6ZGHUHDm0nGK3RUQafTT9kQ2eJOOB4QViAMdjCOt9lDp0REEWLD
U5BvYgbl/cou3H3aVRd4hntm9No+RSlzhB3rBBmZaDM+pYWhxGwkBMbJnNeKJdBS
tz1xWqbVvCzc/SUUFyo22/4AoNgpPkhFguzIKS55AL1HotQKxlUPttUiR5C4DeJ6
EkogQCWT97ePkThVoJGzrjZqNv/P2QHJOXbEvaTQB5kZzz9FzLtJCfQsFwk1kan9
IwIDAQAB
-----END PUBLIC KEY-----

0 个答案:

没有答案