Skip to content

Commit 8556bc4

Browse files
GamCryptography EO migration to GamUtils EO on Github (#885)
* GamUtils + tests * Fix test * Fix quality issues and Log * New features, tests and some needed refactoring * Load encrypted private keys + tests * GamUtilsEO simplification and module refactor + tests * Fix cast type error for jwk keys on gamutils * Add DynamicCall EO to GamUtils * Add sha256 and encoding features for PKCE implementation * Fix hash class * Refactor Encoding class name * Adding missing functions * Delete usless function * Adding Base64Url encoding functions and fixing symmetric and asymmetric * Adding Base64Url encoding functions and fixing symmetric and asymmetric JWT creation and verification * Fix JWTAlgorithm bug with RSA header * Add Base64ToHexa function * GamUtilsEO add functions for PKCE * Fix pkce functions * Change Base64Url functions * Fix key extension detection for azure OIDC discovery * GAM: Add a generic dictionary (for logging) * Add randomUrlSafeCharacters function + tests * Bump nimbus-jose-jwt from 9.39.2 to 10.4 --------- Co-authored-by: mrodbratschi <[email protected]>
1 parent fef68cb commit 8556bc4

37 files changed

+2106
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ These are the source of the GeneXus Standard Classes for Java, valid since GeneX
3131
| gxxmlsignature | SecurityAPI's GeneXusXmlSignature module
3232
| gxftps | SecurityAPI's GeneXusFTPS module
3333
| gxsftp | SecurityAPI's GeneXusSFTP module
34+
| gamutils | GAM external object with utilities
3435

3536
The dependencies between the projects are specified in each pom.xml within their directory.
3637

gamutils/pom.xml

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<parent>
7+
<groupId>com.genexus</groupId>
8+
<artifactId>parent</artifactId>
9+
<version>${revision}${changelist}</version>
10+
</parent>
11+
12+
<artifactId>gamutils</artifactId>
13+
<name>GAM Utils EO</name>
14+
15+
<properties>
16+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
17+
</properties>
18+
<dependencies>
19+
<dependency>
20+
<groupId>com.nimbusds</groupId>
21+
<artifactId>nimbus-jose-jwt</artifactId>
22+
<version>10.4</version>
23+
</dependency>
24+
<dependency>
25+
<groupId>org.bouncycastle</groupId>
26+
<artifactId>bcprov-jdk18on</artifactId>
27+
<version>1.78.1</version>
28+
</dependency>
29+
<dependency>
30+
<groupId>org.bouncycastle</groupId>
31+
<artifactId>bcpkix-jdk18on</artifactId>
32+
<version>1.78.1</version>
33+
</dependency>
34+
<dependency>
35+
<groupId>org.apache.logging.log4j</groupId>
36+
<artifactId>log4j-core</artifactId>
37+
<version>${log4j.version}</version>
38+
</dependency>
39+
<dependency>
40+
<groupId>commons-io</groupId>
41+
<artifactId>commons-io</artifactId>
42+
<version>2.11.0</version>
43+
</dependency>
44+
<dependency>
45+
<groupId>${project.groupId}</groupId>
46+
<artifactId>gxcommon</artifactId>
47+
<version>${project.version}</version>
48+
</dependency>
49+
<dependency>
50+
<groupId>commons-codec</groupId>
51+
<artifactId>commons-codec</artifactId>
52+
<version>1.15</version>
53+
<scope>test</scope>
54+
</dependency>
55+
<dependency>
56+
<groupId>com.google.code.gson</groupId>
57+
<artifactId>gson</artifactId>
58+
<version>2.12.1</version>
59+
</dependency>
60+
<dependency>
61+
<groupId>com.genexus</groupId>
62+
<artifactId>gxclassR</artifactId>
63+
<version>${project.version}</version>
64+
<scope>compile</scope>
65+
</dependency>
66+
</dependencies>
67+
68+
<build>
69+
<finalName>gamutils</finalName>
70+
<plugins>
71+
<plugin>
72+
<groupId>org.apache.maven.plugins</groupId>
73+
<artifactId>maven-compiler-plugin</artifactId>
74+
<version>3.8.0</version>
75+
<configuration>
76+
<source>1.8</source>
77+
<target>1.8</target>
78+
</configuration>
79+
</plugin>
80+
</plugins>
81+
</build>
82+
83+
</project>
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package com.genexus.gam;
2+
3+
import com.genexus.gam.utils.Encoding;
4+
import com.genexus.gam.utils.Pkce;
5+
import com.genexus.gam.utils.Random;
6+
import com.genexus.gam.utils.cryptography.Encryption;
7+
import com.genexus.gam.utils.cryptography.Hash;
8+
import com.genexus.gam.utils.json.Jwk;
9+
import com.genexus.gam.utils.json.Jwt;
10+
import com.genexus.gam.utils.json.UnixTimestamp;
11+
12+
import java.util.Date;
13+
14+
public class GamUtilsEO {
15+
16+
/********EXTERNAL OBJECT PUBLIC METHODS - BEGIN ********/
17+
18+
//**HASH**//
19+
public static String sha512(String plainText) {
20+
return Hash.hash(plainText, Hash.SHA512);
21+
}
22+
23+
public static String sha256(String plainText) {
24+
return Hash.hash(plainText, Hash.SHA256);
25+
}
26+
27+
//**ENCRYPTION**//
28+
29+
public static String AesGcm(String input, String key, String nonce, int macSize, boolean toEncrypt) {
30+
return Encryption.AesGcm(input, key, nonce, macSize, toEncrypt);
31+
}
32+
33+
//**RANDOM**//
34+
public static String randomAlphanumeric(int length) {
35+
return Random.alphanumeric(length);
36+
}
37+
38+
public static String randomNumeric(int length) {
39+
return Random.numeric(length);
40+
}
41+
42+
public static String randomHexaBits(int bits) {
43+
return Random.hexaBits(bits);
44+
}
45+
46+
public static String randomUrlSafeCharacters(int length) { return Random.urlSafe(length); }
47+
48+
//**JWK**//
49+
50+
public static String generateKeyPair() {
51+
return Jwk.generateKeyPair();
52+
}
53+
54+
public static String getPublicJwk(String jwkString) {
55+
return Jwk.getPublic(jwkString);
56+
}
57+
58+
public static String getJwkAlgorithm(String jwkString) {
59+
return Jwk.getAlgorithm(jwkString);
60+
}
61+
62+
//**JWT**//
63+
public static boolean verifyJwt(String path, String alias, String password, String token) {
64+
return Jwt.verify(path, alias, password, token);
65+
}
66+
67+
public static String createJwt(String path, String alias, String password, String payload, String header) {
68+
return Jwt.create(path, alias, password, payload, header);
69+
}
70+
71+
public static boolean verifyAlgorithm(String expectedAlgorithm, String token)
72+
{
73+
return Jwt.verifyAlgorithm(expectedAlgorithm, token);
74+
}
75+
76+
public static long createUnixTimestamp(Date date) {
77+
return UnixTimestamp.create(date);
78+
}
79+
80+
public static String getJwtHeader(String token) {
81+
return Jwt.getHeader(token);
82+
}
83+
84+
public static String getJwtPayload(String token) {
85+
return Jwt.getPayload(token);
86+
}
87+
88+
//**ENCODING**//
89+
public static String base64ToBase64Url(String base64) {
90+
return Encoding.b64ToB64Url(base64);
91+
}
92+
93+
public static String hexaToBase64(String hexa) { return Encoding.hexaToBase64(hexa); }
94+
95+
public static String toBase64Url(String input) { return Encoding.toBase64Url(input); }
96+
97+
public static String fromBase64Url(String base64) { return Encoding.fromBase64Url(base64); }
98+
99+
public static String base64ToHexa(String base64) { return Encoding.base64ToHexa(base64); }
100+
101+
//**PKCE**//
102+
103+
public static String pkce_create(int len, String option) { return Pkce.create(len, option); }
104+
105+
public static boolean pkce_verify(String code_verifier, String code_challenge, String option) { return Pkce.verify(code_verifier, code_challenge, option); }
106+
107+
/********EXTERNAL OBJECT PUBLIC METHODS - END ********/
108+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package com.genexus.gam.utils;
2+
3+
import com.genexus.GxUserType;
4+
import com.google.gson.Gson;
5+
import com.google.gson.GsonBuilder;
6+
import com.google.gson.JsonParser;
7+
import com.google.gson.JsonElement;
8+
import com.google.gson.JsonPrimitive;
9+
import com.google.gson.JsonSyntaxException;
10+
import com.google.gson.reflect.TypeToken;
11+
12+
import java.lang.reflect.Type;
13+
import java.util.LinkedHashMap;
14+
import java.util.List;
15+
import java.util.Map;
16+
17+
public class Dictionary {
18+
19+
private final Map<String, Object> userMap;
20+
private final Gson gson;
21+
22+
/********EXTERNAL OBJECT PUBLIC METHODS - BEGIN ********/
23+
24+
public Dictionary() {
25+
this.userMap = new LinkedHashMap<>();
26+
this.gson = new GsonBuilder().serializeNulls().create();
27+
}
28+
29+
public Object get(String key) {
30+
return this.userMap.get(key);
31+
}
32+
33+
public void set(String key, Object value) {
34+
objectToMap(key, value);
35+
}
36+
37+
public void remove(String key) {
38+
this.userMap.remove(key);
39+
}
40+
41+
public void clear() {
42+
this.userMap.clear();
43+
}
44+
45+
public String toJsonString() {
46+
return this.gson.toJson(this.userMap);
47+
}
48+
49+
/********EXTERNAL OBJECT PUBLIC METHODS - END ********/
50+
51+
// Convert a JSON String to Map<String, Object>
52+
private Map<String, Object> jsonStringToMap(String jsonString) {
53+
Type type = new TypeToken<Map<String, Object>>() {}.getType();
54+
return this.gson.fromJson(jsonString, type);
55+
}
56+
57+
private void objectToMap(String key, Object value) {
58+
if (value == null) {
59+
this.userMap.put(key, null);
60+
} else if (value instanceof Number || value instanceof Boolean || value instanceof Map || value instanceof List) {
61+
this.userMap.put(key, value);
62+
} else if (value instanceof GxUserType) {
63+
this.userMap.put(key, jsonStringToMap(((GxUserType) value).toJSonString()));
64+
} else if (value instanceof String) {
65+
String str = (String) value;
66+
67+
// Try to parse as JSON
68+
try {
69+
JsonElement parsed = JsonParser.parseString(str);
70+
if (parsed.isJsonObject()) {
71+
this.userMap.put(key, this.gson.fromJson(parsed, Map.class));
72+
} else if (parsed.isJsonArray()) {
73+
this.userMap.put(key, this.gson.fromJson(parsed, List.class));
74+
} else if (parsed.isJsonPrimitive()) {
75+
JsonPrimitive primitive = parsed.getAsJsonPrimitive();
76+
if (primitive.isBoolean()) {
77+
this.userMap.put(key, primitive.getAsBoolean());
78+
} else if (primitive.isNumber()) {
79+
this.userMap.put(key, primitive.getAsNumber());
80+
} else if (primitive.isString()) {
81+
this.userMap.put(key, primitive.getAsString());
82+
}
83+
}
84+
} catch (JsonSyntaxException e) {
85+
// Invalid JSON: it is left as string
86+
this.userMap.put(key, str);
87+
}
88+
} else {
89+
// Any other object: it is converted to string
90+
this.userMap.put(key, value.toString());
91+
}
92+
}
93+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package com.genexus.gam.utils;
2+
3+
import com.genexus.ModelContext;
4+
import org.apache.logging.log4j.LogManager;
5+
import org.apache.logging.log4j.Logger;
6+
7+
import java.lang.reflect.Constructor;
8+
import java.lang.reflect.InvocationTargetException;
9+
10+
@SuppressWarnings("unused")
11+
public class DynamicCall {
12+
private final ModelContext mContext;
13+
private final Integer mRemoteHandle;
14+
private static final Logger logger = LogManager.getLogger(DynamicCall.class);
15+
16+
/********EXTERNAL OBJECT PUBLIC METHODS - BEGIN ********/
17+
18+
@SuppressWarnings("unused")
19+
public DynamicCall(ModelContext context, Integer remoteHandle) {
20+
mContext = context;
21+
mRemoteHandle = remoteHandle;
22+
}
23+
24+
@SuppressWarnings("unused")
25+
public boolean execute(String assembly, String typeName, boolean useContext, String method, String jsonParms, String[] jsonOutput) {
26+
logger.debug("execute");
27+
return doCall(assembly, typeName, useContext, method, false, "", jsonParms, jsonOutput);
28+
}
29+
30+
@SuppressWarnings("unused")
31+
public boolean executeEventHandler(String assembly, String typeName, boolean useContext, String method, String eventType, String jsonInput, String[] jsonOutput) {
32+
logger.debug("executeEventHandler");
33+
return doCall(assembly, typeName, useContext, method, true, eventType, jsonInput, jsonOutput);
34+
}
35+
36+
/********EXTERNAL OBJECT PUBLIC METHODS - END ********/
37+
38+
39+
private boolean doCall(String assembly, String typeName, boolean useContext, String method, boolean isEventHandler, String parm1, String parm2, String[] jsonOutput) {
40+
logger.debug("doCall");
41+
Object[] parms;
42+
Class<?>[] parmTypes;
43+
if (isEventHandler) {
44+
parms = new Object[]{parm1, parm2, new String[]{jsonOutput[0]}};
45+
parmTypes = new Class[]{String.class, String.class, String[].class};
46+
} else {
47+
parms = new Object[]{parm2, new String[]{jsonOutput[0]}};
48+
parmTypes = new Class[]{String.class, String[].class};
49+
}
50+
51+
try {
52+
Class<?> myClass = Class.forName(typeName);
53+
Class<?>[] constructorParms;
54+
Constructor<?> constructor;
55+
Object instance = null;
56+
57+
if (useContext && (mContext != null)) {
58+
try {
59+
constructorParms = new Class[]{int.class, ModelContext.class};
60+
constructor = myClass.getConstructor(constructorParms);
61+
instance = constructor.newInstance(new Object[]{mRemoteHandle, mContext});
62+
} catch (NoSuchMethodException e) {
63+
logger.error("doCall", e);
64+
}
65+
}
66+
67+
if (instance == null) {
68+
constructorParms = new Class[]{int.class};
69+
constructor = myClass.getConstructor(constructorParms);
70+
instance = constructor.newInstance(new Object[]{-2});
71+
}
72+
73+
myClass.getMethod(method, parmTypes).invoke(instance, parms);
74+
} catch (ClassNotFoundException e) {
75+
logger.error("doCall", e);
76+
jsonOutput[0] = "{\"error\":\"" + " class " + typeName + " not found" + "\"}";
77+
return false;
78+
} catch (NoSuchMethodException e) {
79+
logger.error("doCall", e);
80+
jsonOutput[0] = "{\"error\":\"" + " method " + method + " not found" + "\"}";
81+
return false;
82+
} catch (InstantiationException e) {
83+
logger.error("doCall", e);
84+
jsonOutput[0] = "{\"error\":\"" + " cannot instantiate type " + typeName + "\"}";
85+
return false;
86+
} catch (IllegalAccessException e) {
87+
logger.error("doCall", e);
88+
jsonOutput[0] = "{\"error\":\"" + " cannot access method " + method + "\"}";
89+
return false;
90+
} catch (InvocationTargetException e) {
91+
logger.error("doCall", e);
92+
jsonOutput[0] = "{\"error\":\"" + " InvocationTargetException in class " + typeName + "\"}";
93+
return false;
94+
}
95+
String[] result = (String[]) parms[parms.length - 1];
96+
jsonOutput[0] = result[0];
97+
logger.debug("doCall result {}", result[0]);
98+
return true;
99+
}
100+
}

0 commit comments

Comments
 (0)