The ID token is signed according to the JSON Web Signature (JWS) specification; algorithms used for signing are defined in the JSON Web Algorithm (JWA) specification. PingFederate 7.1 can support the following signing algorithms:
"alg" Value | Signature Method | Signing Key |
---|---|---|
NONE | No Digital Signature | N/A |
HS256 | HMAC w/ SHA-256 hash | Uses the client secret of the OAuth2 client |
HS384 | HMAC w/ SHA-384 hash | Uses the client secret of the OAuth2 client |
HS512 | HMAC w/ SHA-512 hash | Uses the client secret of the OAuth2 client |
RS256 | RSA PKCS v1.5 w/ SHA-256 hash | Public key available from the JWKS (see below) |
RS384 | RSA PKCS v1.5 w/ SHA-384 hash | Public key available from the JWKS (see below) |
RS512 | RSA PKCS v1.5 w/ SHA-512 hash | Public key available from the JWKS (see below) |
ES256 | ECDSA w/ P-256 curve and SHA-256 hash | Public key available from the JWKS (see below) |
ES384 | ECDSA w/ P-384 curve and SHA-384 hash | Public key available from the JWKS (see below) |
ES512 | ECDSA w/ P-521 curve and SHA-512 hash | Public key available from the JWKS (see below) |
The basic steps to verify a digital signature involve retrieving the appropriate key to use for the signature verification and then performing the cryptographic action to verify the signature.
To validate the signature, take the JWT header and the JWT payload and join with a period. Validate that value against the third component of the JWT using the algorithm defined in the JWT header. Using the above ID token as an example:
Signed data (JWT Header + "." + JWT Payload):
eyJhbGciOiJSUzI1NiIsImtpZCI6Imkwd25uIn0.eyJzdWIiOiJqb2UiLCJhdWQiOiJpbV9vaWNfY2xpZW50IiwianRpIjoidWY5MFNLNHdz
Y0ZoY3RVVDZEdHZiMiIsImlzcyI6Imh0dHBzOlwvXC9sb2NhbGhvc3Q6OTAzMSIsImlhdCI6MTM5NDA2MDg1MywiZXhwIjoxMzk0MDYxMTUz
LCJub25jZSI6ImU5NTdmZmJhLTlhNzgtNGVhOS04ZWNhLWFlOGM0ZWY5Yzg1NiIsImF0X2hhc2giOiJ3Zmd2bUU5VnhqQXVkc2w5bGM2VHFB
In0
Signature value to verify:
lr4L-oT7DJi7Re0eSZDstAdOKHwSvjZfR-OpdWSOmsrw0QVeI7oaIcehyKUFpPFDXDR0-RsEzqno0yek-_U-Ui5EM-yv0PiaUOmJK1U-ws_C
-fCplUFSE7SK-TrCwaOow4_7FN5L4i-4NAa_WqgOjZPloT8o3kKyTkBL7GdITL8rEe4BDK8L6mLqHJrFX4SsEduPk0CyHJSykRqzYS2MEJln
cocBBI4up5Y5g2BNEb0aV4VZwYjmrv9oOUC_yC1Fb4Js5Ry1t6P4Q8q_2ka5OcArlo188XH7lMgPA2GnwSFGHBhccjpxhN7S46ubGPXRBNsn
rPx6RuoR2cI46d9ARQ
For symmetric key signature methods, the client secret value for the OAuth2 client is used as the shared symmetric key. For this reason the client secret defined for the OAuth2 client must be of a large enough length to accommodate the appropriate algorithm (i.e. for a SHA256 hash, the secret must be at least 256 bits "“ 32 ASCII characters).
Asymmetric signature methods require the application to know the corresponding public key. The public key can be distributed out-of-band or can be retrieved dynamically via the JSON Web Key Set (JWKS) endpoint as explained below:
1. Determine the signing algorithm (alg) and the key identifier (kid) from the JWT header. Using the sample JWT token above as an example, the following values are known:
OpenID Connect issuer | https://localhost:9031 |
Signing algorithm (alg) | RS256 |
Key reference identifier (kid) | i0wnn |
2. Query the OpenID configuration URL for the location of the JWKS:
GET https://localhost:9031/.well-known/openid-configuration HTTP/1.1
this will result in a HTTP response containing the OpenID Connect configuration for the OpenID Connect Provider (OP) :
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"version":"3.0",
"issuer":"https:\/\/localhost:9031",
"authorization_endpoint":"https:\/\/localhost:9031\/as\/authorization.oauth2",
"token_endpoint":"https:\/\/localhost:9031\/as\/token.oauth2",
"userinfo_endpoint":"https:\/\/localhost:9031\/idp\/userinfo.openid",
"jwks_uri":"https:\/\/localhost:9031\/pf\/JWKS",
"scopes_supported":["phone","address","email","admin","edit","openid","profile"],
"response_types_supported":["code","token","id_token","code token",
"code id_token","token id_token","code token id_token"],
"subject_types_supported":["public"],
"id_token_signing_alg_values_supported":["none","HS256","HS384","HS512","RS256",
"RS384","RS512","ES256","ES384","ES512"],
"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post"],
"claim_types_supported":["normal"],
"claims_parameter_supported":false,
"request_parameter_supported":false,
"request_uri_parameter_supported":false
}
3. Parse the JSON to retrieve the jwks_uri value (bolded above) and make a request to that endpoint, JSON Web Keystore (JWKS), to retrieve the public key for key identifier "i0wnn" and key type (kty) of RSA as the algorithm is RS256 that was used to sign the JWT:
GET https://localhost:9031/pf/JWKS HTTP/1.1
Which will return the JWKS for the issuer:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"keys":[
{
"kty":"EC",
"kid":"i0wng",
"use":"sig",
"x":"AXYMGFO6K_R2E3RH42_5YTeGYgYTagLM-v3iaiNlPKFFvTh17CKQL_OKH5pEkj5U8mbel-0R1YrNuraRXtBztcVO",
"y":"AaYuq27czYSrbFQUMo3jVK2hrW8KZ75KyE8dyYS-HOB9vUC4nMvoPGbu2hE_yBTLZLpuUvTOSSv150FLaBPhPLA2",
"crv":"P-521"
},
...
{
"kty":"RSA",
"kid":"i0wnn",
"use":"sig",
"n":"mdrLAp5GR8o5d5qbwWTYqNGuSXHTIE6w9HxV445oMACOWRuwlOGVZeKJQXHM9cs5Dm7iUfNVk4pJBttUxzcnhVCRf
9tr20LJB7xAAqnFtzD7jBHARWbgJYR0p0JYVOA5jVzT9Sc-j4Gs5m8b-am2hKF93kA4fM8oeg18V_xeZf11WWcxnW5YZwX
9kjGBwbK-1tkapIar8K1WrsAsDDZLS_y7Qp0S83fAPgubFGYdST71s-B4bvsjCgl30a2W-je9J6jg2bYxZeJf982dzHFqV
QF7KdF4n5UGFAvNMRZ3xVoV4JzHDg4xe_KJE-gOn-_wlao6R8xWcedZjTmDhqqvUw",
"e":"AQAB"
},
...
]
}
We now have the modulus (n) and the exponent (e) of the public key. This can be used to create the public key and validate the signature.