很抱歉,篇幅较长,但我认为最好以有序方式提供尽可能多的信息。
我检查了我在Stack Overflow中发现的与此问题有关的所有问题,但是我仍然没有找到解决方法。
我正在编写自己的类以使用OAuth2连接到Microsoft Graph,但是我找不到一种方法来验证访问令牌签名(根据RFC 7519 S.7.2)。
背景:
我能够从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';
public function base64_url_decode( $input )
{
return base64_decode( str_replace(['-', '_'], ['+', '/'], $input ) );
}
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];
}
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尝试验证签名。
// 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-----