11package com .genexus .JWT ;
22
3+ import java .util .ArrayList ;
4+ import java .util .HashMap ;
35import java .util .List ;
46import java .util .Map ;
57
1113import com .auth0 .jwt .interfaces .DecodedJWT ;
1214import com .auth0 .jwt .interfaces .JWTVerifier ;
1315import com .auth0 .jwt .interfaces .Verification ;
16+ import com .fasterxml .jackson .core .type .TypeReference ;
17+ import com .fasterxml .jackson .databind .ObjectMapper ;
1418import com .genexus .JWT .claims .Claim ;
1519import com .genexus .JWT .claims .PrivateClaims ;
1620import com .genexus .JWT .claims .PublicClaims ;
2529import com .genexus .securityapicommons .keys .PrivateKeyManager ;
2630import com .genexus .securityapicommons .utils .SecurityUtils ;
2731
28-
29-
3032public class JWTCreator extends JWTObject {
3133
34+ private int counter ;
35+
3236 public JWTCreator () {
3337 super ();
3438 EncodingUtil eu = new EncodingUtil ();
3539 eu .setEncoding ("UTF8" );
36-
40+ this .counter = 0 ;
41+
3742 }
38-
43+
3944 /******** EXTERNAL OBJECT PUBLIC METHODS - BEGIN ********/
4045 public String doCreate (String algorithm , PrivateClaims privateClaims , JWTOptions options ) {
4146 if (options .hasError ()) {
@@ -98,7 +103,7 @@ public boolean doVerify(String token, String expectedAlgorithm, PrivateClaims pr
98103 this .error .setError ("JW005" , e .getMessage ());
99104 return false ;
100105 }
101- if (isRevoqued (decodedJWT , options ) || !verifyPrivateClaims (decodedJWT , privateClaims )) {
106+ if (isRevoqued (decodedJWT , options ) || !verifyPrivateClaims (decodedJWT , privateClaims , options )) {
102107 return false ;
103108 }
104109 String algorithm = decodedJWT .getAlgorithm ();
@@ -107,12 +112,11 @@ public boolean doVerify(String token, String expectedAlgorithm, PrivateClaims pr
107112 return false ;
108113 }
109114 JWTAlgorithm expectedJWTAlgorithm = JWTAlgorithm .getJWTAlgorithm (expectedAlgorithm , this .error );
110- if (alg .compareTo (expectedJWTAlgorithm ) != 0 || this .hasError ())
111- {
112- this .error .setError ("JW008" , "Expected algorithm does not match token algorithm" );
113- return false ;
114- }
115-
115+ if (alg .compareTo (expectedJWTAlgorithm ) != 0 || this .hasError ()) {
116+ this .error .setError ("JW008" , "Expected algorithm does not match token algorithm" );
117+ return false ;
118+ }
119+
116120 Algorithm algorithmType = null ;
117121 if (JWTAlgorithm .isPrivate (alg )) {
118122 CertificateX509 cert = options .getCertificate ();
@@ -150,7 +154,7 @@ public boolean doVerify(String token, String expectedAlgorithm, PrivateClaims pr
150154 error .setError ("JW006" , e .getMessage ());
151155 return false ;
152156 }
153-
157+
154158 return true ;
155159
156160 }
@@ -236,7 +240,11 @@ private Builder doBuildPayload(Builder tokenBuilder, PrivateClaims privateClaims
236240 List <Claim > privateC = privateClaims .getAllClaims ();
237241 for (int i = 0 ; i < privateC .size (); i ++) {
238242 try {
239- tokenBuilder .withClaim (privateC .get (i ).getKey (), privateC .get (i ).getValue ());
243+ if (privateC .get (i ).getNestedClaims () != null ) {
244+ tokenBuilder .withClaim (privateC .get (i ).getKey (), privateC .get (i ).getNestedClaims ().getNestedMap ());
245+ } else {
246+ tokenBuilder .withClaim (privateC .get (i ).getKey (), privateC .get (i ).getValue ());
247+ }
240248 } catch (Exception e ) {
241249 this .error .setError ("JW004" , e .getMessage ());
242250 return null ;
@@ -275,32 +283,107 @@ private Builder doBuildPayload(Builder tokenBuilder, PrivateClaims privateClaims
275283 // ****END BUILD PAYLOAD****//
276284 return tokenBuilder ;
277285 }
278-
279- private boolean verifyPrivateClaims (DecodedJWT decodedJWT , PrivateClaims privateClaims )
280- {
281- if ( privateClaims == null || privateClaims . isEmpty ())
282- {
286+
287+ private boolean verifyPrivateClaims (DecodedJWT decodedJWT , PrivateClaims privateClaims , JWTOptions options ) {
288+ RegisteredClaims registeredClaims = options . getAllRegisteredClaims ();
289+ PublicClaims publicClaims = options . getAllPublicClaims ();
290+ if ( privateClaims == null || privateClaims . isEmpty ()) {
283291 return true ;
284292 }
285- Map <String , com .auth0 .jwt .interfaces .Claim > map = decodedJWT .getClaims ();
286-
287- List <Claim > claims = privateClaims .getAllClaims ();
288- for (int i = 0 ; i < claims .size (); i ++)
289- {
290- Claim c = claims .get (i );
291- if (!map .containsKey (c .getKey ()))
292- {
293- return false ;
294- }
295- com .auth0 .jwt .interfaces .Claim claim = map .get (c .getKey ());
296- if (!SecurityUtils .compareStrings (claim .asString ().trim (), c .getValue ().trim ()))
297- {
298- return false ;
293+ String base64Part = decodedJWT .getPayload ();
294+ byte [] base64Bytes = Base64 .decodeBase64 (base64Part );
295+ EncodingUtil eu = new EncodingUtil ();
296+ String plainTextPart = eu .getString (base64Bytes );
297+ HashMap <String , Object > map = new HashMap <String , Object >();
298+ ObjectMapper mapper = new ObjectMapper ();
299+
300+ try {
301+ map = mapper .readValue (plainTextPart , new TypeReference <Map <String , Object >>() {
302+ });
303+ } catch (Exception e ) {
304+ this .error .setError ("JW009" , "Cannot parse JWT payload" );
305+ return false ;
306+ }
307+ this .counter = 0 ;
308+ boolean validation = verifyNestedClaims (privateClaims .getNestedMap (), map , registeredClaims , publicClaims );
309+ int pClaimsCount = countingPrivateClaims (privateClaims .getNestedMap (), 0 );
310+ if (validation && !(this .counter == pClaimsCount )) {
311+ return false ;
312+ }
313+ return validation ;
314+ }
315+
316+ private boolean verifyNestedClaims (Map <String , Object > pclaimMap , Map <String , Object > map ,
317+ RegisteredClaims registeredClaims , PublicClaims publicClaims ) {
318+ List <String > mapClaimKeyList = new ArrayList <String >(map .keySet ());
319+ List <String > pClaimKeyList = new ArrayList <String >(pclaimMap .keySet ());
320+ if (pClaimKeyList .size () > pClaimKeyList .size ()) {
321+ return false ;
322+ }
323+ for (String mapKey : mapClaimKeyList ) {
324+
325+ if (!isRegistered (mapKey , registeredClaims ) && !isPublic (mapKey , publicClaims )) {
326+
327+ this .counter ++;
328+ if (!pclaimMap .containsKey (mapKey )) {
329+ return false ;
330+ }
331+
332+ Object op = pclaimMap .get (mapKey );
333+ Object ot = map .get (mapKey );
334+
335+ if (op instanceof String && ot instanceof String ) {
336+
337+ if (!SecurityUtils .compareStrings (((String ) op ).trim (), ((String ) ot ).trim ())) {
338+ return false ;
339+ }
340+ } else if (op instanceof HashMap && ot instanceof HashMap ) {
341+ @ SuppressWarnings ("unchecked" )
342+ boolean flag = verifyNestedClaims ((HashMap <String , Object >) op , (HashMap <String , Object >) ot ,
343+ registeredClaims , publicClaims );
344+ if (!flag ) {
345+ return false ;
346+ }
347+ } else {
348+ return false ;
349+ }
299350 }
300351 }
301352 return true ;
302353 }
303-
304354
355+ private boolean isRegistered (String claimKey , RegisteredClaims registeredClaims ) {
356+
357+ List <Claim > registeredClaimsList = registeredClaims .getAllClaims ();
358+ for (Claim s : registeredClaimsList ) {
359+ if (SecurityUtils .compareStrings (s .getKey ().trim (), claimKey .trim ())) {
360+ return true ;
361+ }
362+ }
363+ return false ;
364+ }
365+
366+ private boolean isPublic (String claimKey , PublicClaims publicClaims ) {
367+ List <Claim > publicClaimsList = publicClaims .getAllClaims ();
368+ for (Claim s : publicClaimsList ) {
369+ if (SecurityUtils .compareStrings (s .getKey ().trim (), claimKey .trim ())) {
370+ return true ;
371+ }
372+ }
373+ return false ;
374+ }
375+
376+ @ SuppressWarnings ("unchecked" )
377+ private int countingPrivateClaims (Map <String , Object > map , int counter ) {
378+ List <String > list = new ArrayList <String >(map .keySet ());
379+ for (String s : list ) {
380+ counter ++;
381+ Object obj = map .get (s );
382+ if (obj instanceof HashMap ) {
383+ counter = countingPrivateClaims ((HashMap <String , Object >) obj , counter );
384+ }
385+ }
386+ return counter ;
387+ }
305388
306389}
0 commit comments