diff --git a/README.md b/README.md
index 42ff396be..f2e9cfe60 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,7 @@ These are the source of the GeneXus Standard Classes for Java, valid since GeneX
| gxexternalproviders | Implements service provider for IBM Cloud, Google, Azure, Amazon
| gxgeospatial | Geography data type implementation
| gxodata | OData access
+| gamutils | GAM external object with utilities
The dependencies between the projects are specified in each pom.xml within their directory.
diff --git a/gamutils/pom.xml b/gamutils/pom.xml
new file mode 100644
index 000000000..76c226126
--- /dev/null
+++ b/gamutils/pom.xml
@@ -0,0 +1,83 @@
+
+
+ 4.0.0
+
+
+ com.genexus
+ parent
+ ${revision}${changelist}
+
+
+ gamutils
+ GAM Utils EO
+
+
+ UTF-8
+
+
+
+ com.nimbusds
+ nimbus-jose-jwt
+ 9.37.3
+
+
+ org.bouncycastle
+ bcprov-jdk18on
+ 1.78.1
+
+
+ org.bouncycastle
+ bcpkix-jdk18on
+ 1.78.1
+
+
+ org.apache.logging.log4j
+ log4j-core
+ ${log4j.version}
+
+
+ commons-io
+ commons-io
+ 2.11.0
+
+
+ ${project.groupId}
+ gxcommon
+ ${project.version}
+
+
+ commons-codec
+ commons-codec
+ 1.15
+ test
+
+
+ com.google.code.gson
+ gson
+ 2.12.1
+
+
+ com.genexus
+ gxclassR
+ ${project.version}
+ compile
+
+
+
+
+ gamutils
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.0
+
+ 1.8
+ 1.8
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gamutils/src/main/java/com/genexus/gam/GamUtilsEO.java b/gamutils/src/main/java/com/genexus/gam/GamUtilsEO.java
new file mode 100644
index 000000000..bbe199f09
--- /dev/null
+++ b/gamutils/src/main/java/com/genexus/gam/GamUtilsEO.java
@@ -0,0 +1,106 @@
+package com.genexus.gam;
+
+import com.genexus.gam.utils.Encoding;
+import com.genexus.gam.utils.Pkce;
+import com.genexus.gam.utils.Random;
+import com.genexus.gam.utils.cryptography.Encryption;
+import com.genexus.gam.utils.cryptography.Hash;
+import com.genexus.gam.utils.json.Jwk;
+import com.genexus.gam.utils.json.Jwt;
+import com.genexus.gam.utils.json.UnixTimestamp;
+
+import java.util.Date;
+
+public class GamUtilsEO {
+
+ /********EXTERNAL OBJECT PUBLIC METHODS - BEGIN ********/
+
+ //**HASH**//
+ public static String sha512(String plainText) {
+ return Hash.hash(plainText, Hash.SHA512);
+ }
+
+ public static String sha256(String plainText) {
+ return Hash.hash(plainText, Hash.SHA256);
+ }
+
+ //**ENCRYPTION**//
+
+ public static String AesGcm(String input, String key, String nonce, int macSize, boolean toEncrypt) {
+ return Encryption.AesGcm(input, key, nonce, macSize, toEncrypt);
+ }
+
+ //**RANDOM**//
+ public static String randomAlphanumeric(int length) {
+ return Random.alphanumeric(length);
+ }
+
+ public static String randomNumeric(int length) {
+ return Random.numeric(length);
+ }
+
+ public static String randomHexaBits(int bits) {
+ return Random.hexaBits(bits);
+ }
+
+ //**JWK**//
+
+ public static String generateKeyPair() {
+ return Jwk.generateKeyPair();
+ }
+
+ public static String getPublicJwk(String jwkString) {
+ return Jwk.getPublic(jwkString);
+ }
+
+ public static String getJwkAlgorithm(String jwkString) {
+ return Jwk.getAlgorithm(jwkString);
+ }
+
+ //**JWT**//
+ public static boolean verifyJwt(String path, String alias, String password, String token) {
+ return Jwt.verify(path, alias, password, token);
+ }
+
+ public static String createJwt(String path, String alias, String password, String payload, String header) {
+ return Jwt.create(path, alias, password, payload, header);
+ }
+
+ public static boolean verifyAlgorithm(String expectedAlgorithm, String token)
+ {
+ return Jwt.verifyAlgorithm(expectedAlgorithm, token);
+ }
+
+ public static long createUnixTimestamp(Date date) {
+ return UnixTimestamp.create(date);
+ }
+
+ public static String getJwtHeader(String token) {
+ return Jwt.getHeader(token);
+ }
+
+ public static String getJwtPayload(String token) {
+ return Jwt.getPayload(token);
+ }
+
+ //**ENCODING**//
+ public static String base64ToBase64Url(String base64) {
+ return Encoding.b64ToB64Url(base64);
+ }
+
+ public static String hexaToBase64(String hexa) { return Encoding.hexaToBase64(hexa); }
+
+ public static String toBase64Url(String input) { return Encoding.toBase64Url(input); }
+
+ public static String fromBase64Url(String base64) { return Encoding.fromBase64Url(base64); }
+
+ public static String base64ToHexa(String base64) { return Encoding.base64ToHexa(base64); }
+
+ //**PKCE**//
+
+ public static String pkce_create(int len, String option) { return Pkce.create(len, option); }
+
+ public static boolean pkce_verify(String code_verifier, String code_challenge, String option) { return Pkce.verify(code_verifier, code_challenge, option); }
+
+ /********EXTERNAL OBJECT PUBLIC METHODS - END ********/
+}
diff --git a/gamutils/src/main/java/com/genexus/gam/utils/Dictionary.java b/gamutils/src/main/java/com/genexus/gam/utils/Dictionary.java
new file mode 100644
index 000000000..3338aa35f
--- /dev/null
+++ b/gamutils/src/main/java/com/genexus/gam/utils/Dictionary.java
@@ -0,0 +1,93 @@
+package com.genexus.gam.utils;
+
+import com.genexus.GxUserType;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.reflect.TypeToken;
+
+import java.lang.reflect.Type;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Dictionary {
+
+ private final Map userMap;
+ private final Gson gson;
+
+ /********EXTERNAL OBJECT PUBLIC METHODS - BEGIN ********/
+
+ public Dictionary() {
+ this.userMap = new LinkedHashMap<>();
+ this.gson = new GsonBuilder().serializeNulls().create();
+ }
+
+ public Object get(String key) {
+ return this.userMap.get(key);
+ }
+
+ public void set(String key, Object value) {
+ objectToMap(key, value);
+ }
+
+ public void remove(String key) {
+ this.userMap.remove(key);
+ }
+
+ public void clear() {
+ this.userMap.clear();
+ }
+
+ public String toJsonString() {
+ return this.gson.toJson(this.userMap);
+ }
+
+ /********EXTERNAL OBJECT PUBLIC METHODS - END ********/
+
+ // Convert a JSON String to Map
+ private Map jsonStringToMap(String jsonString) {
+ Type type = new TypeToken