diff --git a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/jwks/JwksEndpoint.java b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/jwks/JwksEndpoint.java index f6cf13f6973..b0817648ed0 100644 --- a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/jwks/JwksEndpoint.java +++ b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/jwks/JwksEndpoint.java @@ -17,7 +17,9 @@ */ package org.wso2.carbon.identity.oauth.endpoint.jwks; +import com.nimbusds.jose.JOSEException; import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.jwk.JWK; import com.nimbusds.jose.jwk.KeyUse; import com.nimbusds.jose.jwk.RSAKey; import com.nimbusds.jose.util.Base64; @@ -67,6 +69,8 @@ public class JwksEndpoint { private static final String SECURITY_KEY_STORE_PW = "Security.KeyStore.Password"; private static final String KEYS = "keys"; private static final String ADD_PREVIOUS_VERSION_KID = "JWTValidatorConfigs.JWKSEndpoint.AddPreviousVersionKID"; + public static final String JWKS_IS_THUMBPRINT_HEXIFY_REQUIRED = "JWTValidatorConfigs.JWKSEndpoint" + + ".IsThumbprintHexifyRequired"; @GET @Path(value = "/jwks") @@ -111,7 +115,7 @@ public String jwks() { } private String buildResponse(List certInfoList) - throws IdentityOAuth2Exception, ParseException, CertificateEncodingException { + throws IdentityOAuth2Exception, ParseException, CertificateEncodingException, JOSEException { JSONArray jwksArray = new JSONArray(); JSONObject jwksJson = new JSONObject(); @@ -139,7 +143,7 @@ private String buildResponse(List certInfoList) private void populateJWKSArray(List certInfoList, List diffAlgorithms, JSONArray jwksArray, String hashingAlgorithm) - throws IdentityOAuth2Exception, ParseException, CertificateEncodingException { + throws IdentityOAuth2Exception, ParseException, CertificateEncodingException, JOSEException { for (CertificateInfo certInfo : certInfoList) { for (JWSAlgorithm algorithm : diffAlgorithms) { @@ -156,7 +160,7 @@ private void populateJWKSArray(List certInfoList, List encodedCertList, X509Certificate certificate, String kidAlgorithm, String alias) - throws ParseException, IdentityOAuth2Exception { + throws ParseException, IdentityOAuth2Exception, JOSEException { RSAKey.Builder jwk = new RSAKey.Builder((RSAPublicKey) certificate.getPublicKey()); if (kidAlgorithm.equals(OAuthConstants.SignatureAlgorithms.KID_HASHING_ALGORITHM)) { jwk.keyID(OAuth2Util.getKID(certificate, algorithm, getTenantDomain())); @@ -166,7 +170,12 @@ private RSAKey.Builder getJWK(JWSAlgorithm algorithm, List encodedCertLi jwk.algorithm(algorithm); jwk.keyUse(KeyUse.parse(KEY_USE)); jwk.x509CertChain(encodedCertList); - jwk.x509CertSHA256Thumbprint(new Base64URL(OAuth2Util.getThumbPrint(certificate, alias))); + if (!Boolean.parseBoolean(IdentityUtil.getProperty(JWKS_IS_THUMBPRINT_HEXIFY_REQUIRED))) { + JWK parsedJWK = JWK.parse(certificate); + jwk.x509CertSHA256Thumbprint(parsedJWK.getX509CertSHA256Thumbprint()); + } else { + jwk.x509CertSHA256Thumbprint(new Base64URL(OAuth2Util.getThumbPrint(certificate, alias))); + } return jwk; } diff --git a/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/jwks/JwksEndpointTest.java b/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/jwks/JwksEndpointTest.java index d607a1a4639..0c620a5a3ff 100644 --- a/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/jwks/JwksEndpointTest.java +++ b/components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/jwks/JwksEndpointTest.java @@ -18,7 +18,6 @@ package org.wso2.carbon.identity.oauth.endpoint.jwks; import com.nimbusds.jose.JWSAlgorithm; -import com.nimbusds.jose.util.Base64URL; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -51,6 +50,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.security.KeyStore; +import java.util.Base64; import java.util.HashMap; import java.util.Map; @@ -84,10 +84,7 @@ public class JwksEndpointTest extends PowerMockIdentityBaseTest { private static final String ALG = "RS256"; private static final String USE = "sig"; private static final JSONArray X5C_ARRAY = new JSONArray(); - private static final String X5T = "YmUwN2EzOGI3ZTI0Y2NiNTNmZWFlZjI5MmVjZjdjZTYzZjI0M2MxNDQ1YjQwNjI3NjY" + - "yZmZlYzkwNzY0YjU4NQ"; - private static final String rsa256Thumbprint = "be:07:a3:8b:7e:24:cc:b5:3f:ea:ef:29:2e:cf:7c:e6:3f:24:3c:" + - "14:45:b4:06:27:66:2f:fe:c9:07:64:b5:85"; + private static final JSONArray X5T_ARRAY = new JSONArray(); private JwksEndpoint jwksEndpoint; private Object identityUtilObj; @@ -128,6 +125,8 @@ public void setUp() throws Exception { "EJCSfsvswtLVDZ7GDvTHKojJjQvdVCzRj6XH5Truwefb4BJz9APtnlyJIvjHk1hdozqyOniVZd0QOxLAbcdt946chNdQvCm6aUOp" + "utp8Xogr0KBnEy3U8es2cAfNZaEkPU8Va5bU6Xjny8zGQnXCXxPKp7sMpgO93nPBt/liX1qfyXM7xEotWoxmm6HZx8oWQ8U5aiXj" + "Z5RKDWCCq4ZuXl6wVsUz1iE61suO5yWi8="); + X5T_ARRAY.put("vgeji34kzLU_6u8pLs985j8kPBRFtAYnZi_-yQdktYU"); + X5T_ARRAY.put("UPDtpYmK86EVwsUIGUlW5-EU_iNHQ-nSL3Ca58uAG70"); } @DataProvider(name = "provideTenantDomain") @@ -216,15 +215,18 @@ protected Map initialValue() { assertEquals(keyObject.get("alg"), ALG, "Incorrect alg value"); assertEquals(keyObject.get("use"), USE, "Incorrect use value"); assertEquals(keyObject.get("kty"), "RSA", "Incorrect kty value"); - assertEquals(keyObject.get("x5t#S256"), - Base64URL.encode(rsa256Thumbprint.replaceAll(":", "")).toString()); if ("foo.com".equals(tenantDomain)) { assertEquals(objectArray.length(), 2, "Incorrect no of keysets"); assertEquals(((JSONArray) keyObject.get("x5c")).get(0), X5C_ARRAY.get(0), "Incorrect x5c value"); + assertEquals(keyObject.get("x5t#S256"), X5T_ARRAY.get(0), "Incorrect x5t#S256 value"); } else { assertEquals(objectArray.length(), 3, "Incorrect no of keysets"); assertEquals(((JSONArray) keyObject.get("x5c")).get(0), X5C_ARRAY.get(1), "Incorrect x5c value"); + assertEquals(keyObject.get("x5t#S256"), X5T_ARRAY.get(1), "Incorrect x5t#S256 value"); } + String base64UrlEncodedString = (String) keyObject.get("x5t#S256"); + byte[] decodedBytes = Base64.getUrlDecoder().decode(base64UrlEncodedString); + assertEquals(decodedBytes.length, 32, "Incorrect x5t#S256 size"); } catch (JSONException e) { if ("invalid.com".equals(tenantDomain)) { // This is expected. We don't validate for invalid tenants.