What part of the key object do I pass to `openssl_verify` to validate a signed Google JWT?

I am trying to implement a federated login system with Google and OpenID Connect, and I delay checking and parsing the JWT identifier that I am returning from Google. I follow the google documentation here.

Accepting document recommendations, I am trying to use the existing JWT library. The most popular version of PHP on GitHub is PHP_JWT . The problem is the JWK key format.

The Google docs linked above are said to get the keys from the jwks_uri , as shown in their docs document. This endpoint returns the following:

 { "keys": [ { "kty": "RSA", "alg": "RS256", "use": "sig", "kid": "1771931eb0eb64eb97733e857685be153e079bb9", "n": "AMNFQMNJw/EVwrYsyPTnEHWkaPinPb4ngc/SqD701aisFhbU9/wWoKADeFtwfBcWl1qjzIqhPorQElB+2mtiqUh3Qtaazt1x5wA9XnJDe6kjtMGm9nNLMilSVNBilAE8GIdbciMycISfOfL0WRaJrqpNxewNEVZjuYiGzOWahiDP", "e": "AQAB" }, { "kty": "RSA", "alg": "RS256", "use": "sig", "kid": "7b3bc600209875d3c42ae277a0d018d1d21986ec", "n": "AN2UvG5+hNEMIPIbnpPm+JQi6LFWXBPzg3Ltb3xkVmSTjVaCFWppw/ZYRBgpToGKZP9XJstlOE88SDUFSMZIkIqtLpnUqmZax2Zc2gjEB9PhmHSH3/tTmtZ1U0X6V+crqitZ2uc3NV78vCn9/s+WuPwk/gfKBG8Cirb0fgLmsPd9", "e": "AQAB" } ] } / SqD701aisFhbU9 / wWoKADeFtwfBcWl1qjzIqhPorQElB + 2mtiqUh3Qtaazt1x5wA9XnJDe6kjtMGm9nNLMilSVNBilAE8GIdbciMycISfOfL0WRaJrqpNxewNEVZjuYiGzOWahiDP", { "keys": [ { "kty": "RSA", "alg": "RS256", "use": "sig", "kid": "1771931eb0eb64eb97733e857685be153e079bb9", "n": "AMNFQMNJw/EVwrYsyPTnEHWkaPinPb4ngc/SqD701aisFhbU9/wWoKADeFtwfBcWl1qjzIqhPorQElB+2mtiqUh3Qtaazt1x5wA9XnJDe6kjtMGm9nNLMilSVNBilAE8GIdbciMycISfOfL0WRaJrqpNxewNEVZjuYiGzOWahiDP", "e": "AQAB" }, { "kty": "RSA", "alg": "RS256", "use": "sig", "kid": "7b3bc600209875d3c42ae277a0d018d1d21986ec", "n": "AN2UvG5+hNEMIPIbnpPm+JQi6LFWXBPzg3Ltb3xkVmSTjVaCFWppw/ZYRBgpToGKZP9XJstlOE88SDUFSMZIkIqtLpnUqmZax2Zc2gjEB9PhmHSH3/tTmtZ1U0X6V+crqitZ2uc3NV78vCn9/s+WuPwk/gfKBG8Cirb0fgLmsPd9", "e": "AQAB" } ] } + JQi6LFWXBPzg3Ltb3xkVmSTjVaCFWppw / ZYRBgpToGKZP9XJstlOE88SDUFSMZIkIqtLpnUqmZax2Zc2gjEB9PhmHSH3 / tTmtZ1U0X6V + crqitZ2uc3NV78vCn9 / s + WuPwk / gfKBG8Cirb0fgLmsPd9", { "keys": [ { "kty": "RSA", "alg": "RS256", "use": "sig", "kid": "1771931eb0eb64eb97733e857685be153e079bb9", "n": "AMNFQMNJw/EVwrYsyPTnEHWkaPinPb4ngc/SqD701aisFhbU9/wWoKADeFtwfBcWl1qjzIqhPorQElB+2mtiqUh3Qtaazt1x5wA9XnJDe6kjtMGm9nNLMilSVNBilAE8GIdbciMycISfOfL0WRaJrqpNxewNEVZjuYiGzOWahiDP", "e": "AQAB" }, { "kty": "RSA", "alg": "RS256", "use": "sig", "kid": "7b3bc600209875d3c42ae277a0d018d1d21986ec", "n": "AN2UvG5+hNEMIPIbnpPm+JQi6LFWXBPzg3Ltb3xkVmSTjVaCFWppw/ZYRBgpToGKZP9XJstlOE88SDUFSMZIkIqtLpnUqmZax2Zc2gjEB9PhmHSH3/tTmtZ1U0X6V+crqitZ2uc3NV78vCn9/s+WuPwk/gfKBG8Cirb0fgLmsPd9", "e": "AQAB" } ] } 

Considering the source for the JWT decode and verify class methods, it seems that the $keys parameter can be an array, but they expect the array keys to be kid and the array values โ€‹โ€‹will be like this: @param string|resource $key for HS*, a string key works. for RS*, must be a resource of an openssl public key @param string|resource $key for HS*, a string key works. for RS*, must be a resource of an openssl public key . It's simple enough to pull out kid properties and use them as array keys, but what should be used as array values?

From the Google JWK document, it seems we are using RS* , but I have no idea which part of the key object is the resource of an openssl public key . I tried using the whole stdClass object and only the string n , but both are executed with the openssl_verify step. This function issues a notification: "Warning: openssl_verify (): the specified key parameter cannot be forced into the public key."

So itโ€™s obvious that I am going to the wrong key, but what is the correct key?

The Google library for this seems to use a different endpoint to get the keys. This endpoint seems to return an array of certificates. Do I need to use something like this? If so, why should documents indicate the jwks_uri ?

+6
source share
3 answers

Both endpoints contain the same information, but in a different format.

The jwks_uri provides the value of the module and the RSA exponential public key. You can use these two values โ€‹โ€‹to generate the PEM file that you get at https://www.googleapis.com/oauth2/v1/certs .

+4
source

For people trying to do this in the future, I would like to provide a complete answer;

Parts n and e in the jwks_uri JSON keys give a module and an exponent that can be used to extract the public key (which is all that is required to verify the signature). How this is done in pure PHP is described in detail in this post:

openssl: how can I get the public key from a module

However , you should be aware that the JSON document provided by Google jwks_uri uses secure base64 encoding URLs (i.e. normal base64 with replaced characters + and /). If you do not take this into account, you can still get an invalid certificate. Since you mention the use of php-jwt , the working code to get the public key from the module and exponent using phpseclib is:

 $modulus = 'someencodedmodulusvalue'; $exponent = 'someencodedexponentvalue'; $rsa = new Crypt_RSA(); $modulus = new Math_BigInteger(JWT::urlsafeB64Decode($modulus), 256); $exponent = new Math_BigInteger(JWT::urlsafeB64Decode($exponent), 256); $rsa->loadKey(array('n' => $modulus, 'e' => $exponent)); $rsa->setPublicKey(); $pubKey = $rsa->getPublicKey(); 
+6
source

I am developing the Jose library in PHP. My library is not full (especially the documentation), but it can already check whether the signature is valid and return verified data. You will find my work at https://github.com/Spomky-Labs/JOSE

Feel free to contact me if you have any questions or to introduce a new method that can help you.

0
source

Source: https://habr.com/ru/post/973238/


All Articles