Skip to content
This repository was archived by the owner on Apr 10, 2025. It is now read-only.

Commit f5c75ed

Browse files
authored
V16u11 (#29)
* Change version to 16.11.0 (#18) * Change java-jwt to version 3.10.3 (#19) * Nested PrivateClaims implementation (#21) * Header parameters implementation (#22) * Change jackson libraries version to 2.11.0 (#23) * Fix JWT DateUtil methods (#24) * Add JWT DateUtil functions (#25) * DateUtil & GUID deprecation (#26) * Add .key & .pkcs12 extensions (#27) * Fix possible null dereference (#28)
1 parent d1d5c2a commit f5c75ed

File tree

19 files changed

+329
-64
lines changed

19 files changed

+329
-64
lines changed

GeneXusCryptography/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<parent>
88
<groupId>com.genexus</groupId>
99
<artifactId>SecurityAPIParent</artifactId>
10-
<version>1.0.0.3</version>
10+
<version>16.11.0</version>
1111
</parent>
1212

1313

GeneXusFtps/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<parent>
99
<groupId>com.genexus</groupId>
1010
<artifactId>SecurityAPIParent</artifactId>
11-
<version>1.0.0.3</version>
11+
<version>16.11.0</version>
1212
</parent>
1313

1414

GeneXusJWT/pom.xml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<parent>
88
<groupId>com.genexus</groupId>
99
<artifactId>SecurityAPIParent</artifactId>
10-
<version>1.0.0.3</version>
10+
<version>16.11.0</version>
1111
</parent>
1212

1313

@@ -26,25 +26,25 @@
2626
<dependency>
2727
<groupId>com.auth0</groupId>
2828
<artifactId>java-jwt</artifactId>
29-
<version>3.9.0</version>
29+
<version>3.10.3</version>
3030
</dependency>
3131
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
3232
<dependency>
3333
<groupId>com.fasterxml.jackson.core</groupId>
3434
<artifactId>jackson-databind</artifactId>
35-
<version>2.9.10.4</version>
35+
<version>2.11.0</version>
3636
</dependency>
3737
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
3838
<dependency>
3939
<groupId>com.fasterxml.jackson.core</groupId>
4040
<artifactId>jackson-annotations</artifactId>
41-
<version>2.9.9</version>
41+
<version>2.11.0</version>
4242
</dependency>
4343
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
4444
<dependency>
4545
<groupId>com.fasterxml.jackson.core</groupId>
4646
<artifactId>jackson-core</artifactId>
47-
<version>2.9.9</version>
47+
<version>2.11.0</version>
4848
</dependency>
4949
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on -->
5050
<dependency>

GeneXusJWT/src/main/java/com/genexus/JWT/JWTCreator.java

Lines changed: 169 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.genexus.JWT;
22

3+
import java.util.ArrayList;
4+
import java.util.HashMap;
35
import java.util.List;
46
import java.util.Map;
57

@@ -11,7 +13,10 @@
1113
import com.auth0.jwt.interfaces.DecodedJWT;
1214
import com.auth0.jwt.interfaces.JWTVerifier;
1315
import com.auth0.jwt.interfaces.Verification;
16+
import com.fasterxml.jackson.core.type.TypeReference;
17+
import com.fasterxml.jackson.databind.ObjectMapper;
1418
import com.genexus.JWT.claims.Claim;
19+
import com.genexus.JWT.claims.HeaderParameters;
1520
import com.genexus.JWT.claims.PrivateClaims;
1621
import com.genexus.JWT.claims.PublicClaims;
1722
import com.genexus.JWT.claims.RegisteredClaim;
@@ -25,17 +30,18 @@
2530
import com.genexus.securityapicommons.keys.PrivateKeyManager;
2631
import com.genexus.securityapicommons.utils.SecurityUtils;
2732

28-
29-
3033
public class JWTCreator extends JWTObject {
3134

35+
private int counter;
36+
3237
public JWTCreator() {
3338
super();
3439
EncodingUtil eu = new EncodingUtil();
3540
eu.setEncoding("UTF8");
36-
41+
this.counter = 0;
42+
3743
}
38-
44+
3945
/******** EXTERNAL OBJECT PUBLIC METHODS - BEGIN ********/
4046
public String doCreate(String algorithm, PrivateClaims privateClaims, JWTOptions options) {
4147
if (options.hasError()) {
@@ -47,6 +53,10 @@ public String doCreate(String algorithm, PrivateClaims privateClaims, JWTOptions
4753
return "";
4854
}
4955
Builder tokenBuilder = JWT.create();
56+
if (!options.getHeaderParameters().isEmpty()) {
57+
HeaderParameters parameters = options.getHeaderParameters();
58+
tokenBuilder.withHeader(parameters.getMap());
59+
}
5060
tokenBuilder = doBuildPayload(tokenBuilder, privateClaims, options);
5161
if (this.hasError()) {
5262
return "";
@@ -98,7 +108,8 @@ public boolean doVerify(String token, String expectedAlgorithm, PrivateClaims pr
98108
this.error.setError("JW005", e.getMessage());
99109
return false;
100110
}
101-
if (isRevoqued(decodedJWT, options) || !verifyPrivateClaims(decodedJWT, privateClaims)) {
111+
if (isRevoqued(decodedJWT, options) || !verifyPrivateClaims(decodedJWT, privateClaims, options)
112+
|| !verifyHeader(decodedJWT, options)) {
102113
return false;
103114
}
104115
String algorithm = decodedJWT.getAlgorithm();
@@ -107,12 +118,11 @@ public boolean doVerify(String token, String expectedAlgorithm, PrivateClaims pr
107118
return false;
108119
}
109120
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-
121+
if (alg.compareTo(expectedJWTAlgorithm) != 0 || this.hasError()) {
122+
this.error.setError("JW008", "Expected algorithm does not match token algorithm");
123+
return false;
124+
}
125+
116126
Algorithm algorithmType = null;
117127
if (JWTAlgorithm.isPrivate(alg)) {
118128
CertificateX509 cert = options.getCertificate();
@@ -150,7 +160,7 @@ public boolean doVerify(String token, String expectedAlgorithm, PrivateClaims pr
150160
error.setError("JW006", e.getMessage());
151161
return false;
152162
}
153-
163+
154164
return true;
155165

156166
}
@@ -236,7 +246,11 @@ private Builder doBuildPayload(Builder tokenBuilder, PrivateClaims privateClaims
236246
List<Claim> privateC = privateClaims.getAllClaims();
237247
for (int i = 0; i < privateC.size(); i++) {
238248
try {
239-
tokenBuilder.withClaim(privateC.get(i).getKey(), privateC.get(i).getValue());
249+
if (privateC.get(i).getNestedClaims() != null) {
250+
tokenBuilder.withClaim(privateC.get(i).getKey(), privateC.get(i).getNestedClaims().getNestedMap());
251+
} else {
252+
tokenBuilder.withClaim(privateC.get(i).getKey(), privateC.get(i).getValue());
253+
}
240254
} catch (Exception e) {
241255
this.error.setError("JW004", e.getMessage());
242256
return null;
@@ -275,32 +289,157 @@ private Builder doBuildPayload(Builder tokenBuilder, PrivateClaims privateClaims
275289
// ****END BUILD PAYLOAD****//
276290
return tokenBuilder;
277291
}
278-
279-
private boolean verifyPrivateClaims(DecodedJWT decodedJWT, PrivateClaims privateClaims)
280-
{
281-
if(privateClaims == null || privateClaims.isEmpty())
282-
{
292+
293+
private boolean verifyPrivateClaims(DecodedJWT decodedJWT, PrivateClaims privateClaims, JWTOptions options) {
294+
RegisteredClaims registeredClaims = options.getAllRegisteredClaims();
295+
PublicClaims publicClaims = options.getAllPublicClaims();
296+
if (privateClaims == null || privateClaims.isEmpty()) {
297+
return true;
298+
}
299+
String base64Part = decodedJWT.getPayload();
300+
byte[] base64Bytes = Base64.decodeBase64(base64Part);
301+
EncodingUtil eu = new EncodingUtil();
302+
String plainTextPart = eu.getString(base64Bytes);
303+
HashMap<String, Object> map = new HashMap<String, Object>();
304+
ObjectMapper mapper = new ObjectMapper();
305+
306+
try {
307+
map = (HashMap<String, Object>) mapper.readValue(plainTextPart, new TypeReference<Map<String, Object>>() {
308+
});
309+
} catch (Exception e) {
310+
this.error.setError("JW009", "Cannot parse JWT payload");
311+
return false;
312+
}
313+
this.counter = 0;
314+
boolean validation = verifyNestedClaims(privateClaims.getNestedMap(), map, registeredClaims, publicClaims);
315+
int pClaimsCount = countingPrivateClaims(privateClaims.getNestedMap(), 0);
316+
if (validation && !(this.counter == pClaimsCount)) {
317+
return false;
318+
}
319+
return validation;
320+
}
321+
322+
private boolean verifyNestedClaims(Map<String, Object> pclaimMap, Map<String, Object> map,
323+
RegisteredClaims registeredClaims, PublicClaims publicClaims) {
324+
List<String> mapClaimKeyList = new ArrayList<String>(map.keySet());
325+
List<String> pClaimKeyList = new ArrayList<String>(pclaimMap.keySet());
326+
if (pClaimKeyList.size() > pClaimKeyList.size()) {
327+
return false;
328+
}
329+
for (String mapKey : mapClaimKeyList) {
330+
331+
if (!isRegistered(mapKey, registeredClaims) && !isPublic(mapKey, publicClaims)) {
332+
333+
this.counter++;
334+
if (!pclaimMap.containsKey(mapKey)) {
335+
return false;
336+
}
337+
338+
Object op = pclaimMap.get(mapKey);
339+
Object ot = map.get(mapKey);
340+
341+
if (op instanceof String && ot instanceof String) {
342+
343+
if (!SecurityUtils.compareStrings(((String) op).trim(), ((String) ot).trim())) {
344+
return false;
345+
}
346+
} else if (op instanceof HashMap && ot instanceof HashMap) {
347+
@SuppressWarnings("unchecked")
348+
boolean flag = verifyNestedClaims((HashMap<String, Object>) op, (HashMap<String, Object>) ot,
349+
registeredClaims, publicClaims);
350+
if (!flag) {
351+
return false;
352+
}
353+
} else {
354+
return false;
355+
}
356+
}
357+
}
358+
return true;
359+
}
360+
361+
private boolean isRegistered(String claimKey, RegisteredClaims registeredClaims) {
362+
363+
List<Claim> registeredClaimsList = registeredClaims.getAllClaims();
364+
for (Claim s : registeredClaimsList) {
365+
if (SecurityUtils.compareStrings(s.getKey().trim(), claimKey.trim())) {
366+
return true;
367+
}
368+
}
369+
return false;
370+
}
371+
372+
private boolean isPublic(String claimKey, PublicClaims publicClaims) {
373+
List<Claim> publicClaimsList = publicClaims.getAllClaims();
374+
for (Claim s : publicClaimsList) {
375+
if (SecurityUtils.compareStrings(s.getKey().trim(), claimKey.trim())) {
376+
return true;
377+
}
378+
}
379+
return false;
380+
}
381+
382+
@SuppressWarnings("unchecked")
383+
private int countingPrivateClaims(Map<String, Object> map, int counter) {
384+
List<String> list = new ArrayList<String>(map.keySet());
385+
for (String s : list) {
386+
counter++;
387+
Object obj = map.get(s);
388+
if (obj instanceof HashMap) {
389+
counter = countingPrivateClaims((HashMap<String, Object>) obj, counter);
390+
}
391+
}
392+
return counter;
393+
}
394+
395+
private boolean verifyHeader(DecodedJWT decodedJWT, JWTOptions options) {
396+
HeaderParameters parameters = options.getHeaderParameters();
397+
if (parameters.isEmpty()) {
283398
return true;
284399
}
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-
{
400+
401+
List<String> allParms = parameters.getAll();
402+
if (allParms.size() + 2 != getHeaderClaimsNumber(decodedJWT)) {
403+
return false;
404+
}
405+
Map<String, Object> map = parameters.getMap();
406+
for (String s : allParms) {
407+
408+
if (decodedJWT.getHeaderClaim(s) == null) {
293409
return false;
294410
}
295-
com.auth0.jwt.interfaces.Claim claim = map.get(c.getKey());
296-
if(!SecurityUtils.compareStrings(claim.asString().trim(), c.getValue().trim()))
297-
{
411+
com.auth0.jwt.interfaces.Claim c = decodedJWT.getHeaderClaim(s);
412+
String claimValue = null;
413+
try {
414+
claimValue = c.asString().trim();
415+
} catch (NullPointerException e) {
416+
return false;
417+
}
418+
String optionsValue = ((String) map.get(s)).trim();
419+
if (!SecurityUtils.compareStrings(claimValue, optionsValue)) {
298420
return false;
299421
}
300422
}
301423
return true;
424+
302425
}
303-
304426

427+
private int getHeaderClaimsNumber(DecodedJWT decodedJWT) {
428+
String base64Part = decodedJWT.getHeader();
429+
byte[] base64Bytes = Base64.decodeBase64(base64Part);
430+
EncodingUtil eu = new EncodingUtil();
431+
String plainTextPart = eu.getString(base64Bytes);
432+
HashMap<String, Object> map = new HashMap<String, Object>();
433+
ObjectMapper mapper = new ObjectMapper();
434+
435+
try {
436+
map = (HashMap<String, Object>) mapper.readValue(plainTextPart, new TypeReference<Map<String, Object>>() {
437+
});
438+
} catch (Exception e) {
439+
return 0;
440+
}
441+
return map.size();
442+
443+
}
305444

306445
}

GeneXusJWT/src/main/java/com/genexus/JWT/claims/Claim.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,27 @@
33
public class Claim {
44

55
private String key;
6-
private String value;
6+
private Object value;
77

8-
public Claim(String valueKey, String valueOfValue) {
8+
public Claim(String valueKey, Object valueOfValue) {
99
key = valueKey;
1010
value = valueOfValue;
11-
}
11+
}
1212

1313
public String getValue() {
14-
return value;
14+
if (value instanceof String) {
15+
return (String) value;
16+
} else {
17+
return null;
18+
}
19+
}
20+
21+
public PrivateClaims getNestedClaims() {
22+
if (value instanceof PrivateClaims) {
23+
return (PrivateClaims) value;
24+
} else {
25+
return null;
26+
}
1527
}
1628

1729
public String getKey() {

GeneXusJWT/src/main/java/com/genexus/JWT/claims/Claims.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import java.util.ArrayList;
44
import java.util.List;
55

6-
import com.genexus.JWT.utils.JWTUtils;
76
import com.genexus.securityapicommons.commons.Error;
87
import com.genexus.securityapicommons.utils.SecurityUtils;
98

@@ -13,9 +12,9 @@ public class Claims {
1312

1413
public Claims() {
1514
claims = new ArrayList<Claim>();
16-
}
15+
}
1716

18-
public boolean setClaim(String key, String value, Error error) {
17+
public boolean setClaim(String key, Object value, Error error) {
1918
Claim claim = new Claim(key, value);
2019
claims.add(claim);
2120
return true;

0 commit comments

Comments
 (0)