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..90e75af79 --- /dev/null +++ b/gamutils/pom.xml @@ -0,0 +1,72 @@ + + + 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 + + + + + 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/DynamicCall.java b/gamutils/src/main/java/com/genexus/gam/utils/DynamicCall.java new file mode 100644 index 000000000..45b09a73c --- /dev/null +++ b/gamutils/src/main/java/com/genexus/gam/utils/DynamicCall.java @@ -0,0 +1,100 @@ +package com.genexus.gam.utils; + +import com.genexus.ModelContext; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +@SuppressWarnings("unused") +public class DynamicCall { + private final ModelContext mContext; + private final Integer mRemoteHandle; + private static final Logger logger = LogManager.getLogger(DynamicCall.class); + + /********EXTERNAL OBJECT PUBLIC METHODS - BEGIN ********/ + + @SuppressWarnings("unused") + public DynamicCall(ModelContext context, Integer remoteHandle) { + mContext = context; + mRemoteHandle = remoteHandle; + } + + @SuppressWarnings("unused") + public boolean execute(String assembly, String typeName, boolean useContext, String method, String jsonParms, String[] jsonOutput) { + logger.debug("execute"); + return doCall(assembly, typeName, useContext, method, false, "", jsonParms, jsonOutput); + } + + @SuppressWarnings("unused") + public boolean executeEventHandler(String assembly, String typeName, boolean useContext, String method, String eventType, String jsonInput, String[] jsonOutput) { + logger.debug("executeEventHandler"); + return doCall(assembly, typeName, useContext, method, true, eventType, jsonInput, jsonOutput); + } + + /********EXTERNAL OBJECT PUBLIC METHODS - END ********/ + + + private boolean doCall(String assembly, String typeName, boolean useContext, String method, boolean isEventHandler, String parm1, String parm2, String[] jsonOutput) { + logger.debug("doCall"); + Object[] parms; + Class[] parmTypes; + if (isEventHandler) { + parms = new Object[]{parm1, parm2, new String[]{jsonOutput[0]}}; + parmTypes = new Class[]{String.class, String.class, String[].class}; + } else { + parms = new Object[]{parm2, new String[]{jsonOutput[0]}}; + parmTypes = new Class[]{String.class, String[].class}; + } + + try { + Class myClass = Class.forName(typeName); + Class[] constructorParms; + Constructor constructor; + Object instance = null; + + if (useContext && (mContext != null)) { + try { + constructorParms = new Class[]{int.class, ModelContext.class}; + constructor = myClass.getConstructor(constructorParms); + instance = constructor.newInstance(new Object[]{mRemoteHandle, mContext}); + } catch (NoSuchMethodException e) { + logger.error("doCall", e); + } + } + + if (instance == null) { + constructorParms = new Class[]{int.class}; + constructor = myClass.getConstructor(constructorParms); + instance = constructor.newInstance(new Object[]{-2}); + } + + myClass.getMethod(method, parmTypes).invoke(instance, parms); + } catch (ClassNotFoundException e) { + logger.error("doCall", e); + jsonOutput[0] = "{\"error\":\"" + " class " + typeName + " not found" + "\"}"; + return false; + } catch (NoSuchMethodException e) { + logger.error("doCall", e); + jsonOutput[0] = "{\"error\":\"" + " method " + method + " not found" + "\"}"; + return false; + } catch (InstantiationException e) { + logger.error("doCall", e); + jsonOutput[0] = "{\"error\":\"" + " cannot instantiate type " + typeName + "\"}"; + return false; + } catch (IllegalAccessException e) { + logger.error("doCall", e); + jsonOutput[0] = "{\"error\":\"" + " cannot access method " + method + "\"}"; + return false; + } catch (InvocationTargetException e) { + logger.error("doCall", e); + jsonOutput[0] = "{\"error\":\"" + " InvocationTargetException in class " + typeName + "\"}"; + return false; + } + String[] result = (String[]) parms[parms.length - 1]; + jsonOutput[0] = result[0]; + logger.debug("doCall result {}", result[0]); + return true; + } +} diff --git a/gamutils/src/main/java/com/genexus/gam/utils/Encoding.java b/gamutils/src/main/java/com/genexus/gam/utils/Encoding.java new file mode 100644 index 000000000..7b25fd0e3 --- /dev/null +++ b/gamutils/src/main/java/com/genexus/gam/utils/Encoding.java @@ -0,0 +1,74 @@ +package com.genexus.gam.utils; + + +import com.nimbusds.jose.util.Base64URL; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.util.encoders.Base64; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.encoders.UrlBase64; + +import java.nio.charset.StandardCharsets; + +public class Encoding { + + private static final Logger logger = LogManager.getLogger(Encoding.class); + + public static String b64ToB64Url(String input) { + logger.debug("b64ToB64Url"); + try { + return java.util.Base64.getUrlEncoder().withoutPadding().encodeToString(Base64.decode(input)); + } catch (Exception e) { + logger.error("b64ToB64Url", e); + return ""; + } + } + + public static String hexaToBase64(String hexa) + { + logger.debug("hexaToBase64"); + try{ + return Base64.toBase64String(Hex.decode(hexa)); + }catch (Exception e) + { + logger.error("hexaToBase64", e); + return ""; + } + } + + public static String toBase64Url(String input) + { + logger.debug("UTF8toBase64Url"); + try{ + return java.util.Base64.getUrlEncoder().withoutPadding().encodeToString(input.getBytes(StandardCharsets.UTF_8)); + }catch (Exception e) + { + logger.error("UTF8toBase64Url", e); + return ""; + } + } + + public static String fromBase64Url(String base64Url) + { + logger.debug("fromBase64Url"); + try{ + return new String(java.util.Base64.getUrlDecoder().decode(base64Url), StandardCharsets.ISO_8859_1); + }catch (Exception e) + { + logger.error("fromBase64Url", e); + return ""; + } + } + + public static String base64ToHexa(String base64) + { + logger.debug("base64ToHexa"); + try{ + return Hex.toHexString(Base64.decode(base64)); + }catch (Exception e) + { + logger.error("base64ToHexa", e); + return ""; + } + } +} diff --git a/gamutils/src/main/java/com/genexus/gam/utils/Pkce.java b/gamutils/src/main/java/com/genexus/gam/utils/Pkce.java new file mode 100644 index 000000000..d72da2c91 --- /dev/null +++ b/gamutils/src/main/java/com/genexus/gam/utils/Pkce.java @@ -0,0 +1,64 @@ +package com.genexus.gam.utils; + + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.SHA256Digest; + +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; +import java.text.MessageFormat; +import java.util.Base64; + +@SuppressWarnings("LoggingSimilarMessage") +public class Pkce { + + private static final Logger logger = LogManager.getLogger(Pkce.class); + + public static String create(int len, String option) { + logger.trace("create"); + byte[] code_verifier_bytes = getRandomBytes(len); + String code_verifier = Base64.getUrlEncoder().withoutPadding().encodeToString(code_verifier_bytes); + switch (option.toUpperCase().trim()) { + case "S256": + byte[] digest = hash(new SHA256Digest(), code_verifier.getBytes(StandardCharsets.US_ASCII)); + return MessageFormat.format("{0},{1}", code_verifier, Base64.getUrlEncoder().withoutPadding().encodeToString(digest)); + case "PLAIN": + return MessageFormat.format("{0},{1}", code_verifier, code_verifier); + default: + logger.error("Unknown PKCE option"); + return ""; + } + + } + + public static boolean verify(String code_verifier, String code_challenge, String option) { + logger.trace("verify"); + switch (option.toUpperCase().trim()) { + case "S256": + byte[] digest = hash(new SHA256Digest(), code_verifier.trim().getBytes(StandardCharsets.US_ASCII)); + return Base64.getUrlEncoder().withoutPadding().encodeToString(digest).equals(code_challenge.trim()); + case "PLAIN": + return code_challenge.trim().equals(code_verifier.trim()); + default: + logger.error("Unknown PKCE option"); + return false; + } + } + + private static byte[] hash(Digest digest, byte[] inputBytes) { + byte[] retValue = new byte[digest.getDigestSize()]; + digest.update(inputBytes, 0, inputBytes.length); + digest.doFinal(retValue, 0); + return retValue; + } + + private static byte[] getRandomBytes(int len) { + logger.trace("getRandomBytes"); + SecureRandom secureRandom = new SecureRandom(); + byte[] bytes = new byte[len]; + secureRandom.nextBytes(bytes); + return bytes; + } +} diff --git a/gamutils/src/main/java/com/genexus/gam/utils/Random.java b/gamutils/src/main/java/com/genexus/gam/utils/Random.java new file mode 100644 index 000000000..4586973c6 --- /dev/null +++ b/gamutils/src/main/java/com/genexus/gam/utils/Random.java @@ -0,0 +1,63 @@ +package com.genexus.gam.utils; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; + +public class Random { + + private static Logger logger = LogManager.getLogger(Random.class); + + private static SecureRandom instanceRandom() { + try { + return new SecureRandom(); + } catch (Exception e) { + logger.error("instanceRandom", e); + return null; + } + } + + public static String alphanumeric(int length) { + SecureRandom random = instanceRandom(); + if (random == null) { + logger.error("randomAlphanumeric SecureRandom is null"); + return ""; + } + String characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + StringBuilder sb = new StringBuilder(length); + for (int i = 0; i < length; i++) + sb.append(characters.charAt(random.nextInt(characters.length()))); + return sb.toString(); + } + + public static String numeric(int length) { + SecureRandom random = instanceRandom(); + if (random == null) { + logger.error("randomNumeric SecureRandom is null"); + return ""; + } + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < length; i++) { + sb.append(random.nextInt(10)); + } + return sb.toString(); + } + + public static String hexaBits(int bits) + { + SecureRandom random = instanceRandom(); + if (random == null) { + logger.error("randomNumeric SecureRandom is null"); + return ""; + } + byte[] values = new byte[bits / 8]; + random.nextBytes(values); + StringBuilder sb = new StringBuilder(); + for (byte b : values) { + sb.append(String.format("%02x", b)); + } + return sb.toString().replaceAll("\\s", ""); + } +} diff --git a/gamutils/src/main/java/com/genexus/gam/utils/cryptography/Encryption.java b/gamutils/src/main/java/com/genexus/gam/utils/cryptography/Encryption.java new file mode 100644 index 000000000..2ce4e0350 --- /dev/null +++ b/gamutils/src/main/java/com/genexus/gam/utils/cryptography/Encryption.java @@ -0,0 +1,38 @@ +package com.genexus.gam.utils.cryptography; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.crypto.engines.AESEngine; +import org.bouncycastle.crypto.modes.AEADBlockCipher; +import org.bouncycastle.crypto.modes.GCMBlockCipher; +import org.bouncycastle.crypto.params.AEADParameters; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.util.encoders.Base64; +import org.bouncycastle.util.encoders.Hex; + +import java.nio.charset.StandardCharsets; + +public class Encryption { + + private static Logger logger = LogManager.getLogger(Encryption.class); + + public static String AesGcm(String input, String key, String nonce, int macSize, boolean toEncrypt) { + return toEncrypt ? Base64.toBase64String(internal_AesGcm(input.getBytes(StandardCharsets.UTF_8), key, nonce, macSize, toEncrypt)) : new String(internal_AesGcm(Base64.decode(input), key, nonce, macSize, toEncrypt), StandardCharsets.UTF_8); + } + + public static byte[] internal_AesGcm(byte[] inputBytes, String key, String nonce, int macSize, boolean toEncrypt) { + logger.debug("internal_AesGcm"); + AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine()); + AEADParameters AEADparams = new AEADParameters(new KeyParameter(Hex.decode(key)), macSize, Hex.decode(nonce)); + try { + cipher.init(toEncrypt, AEADparams); + byte[] outputBytes = new byte[cipher.getOutputSize(inputBytes.length)]; + int length = cipher.processBytes(inputBytes, 0, inputBytes.length, outputBytes, 0); + cipher.doFinal(outputBytes, length); + return outputBytes; + } catch (Exception e) { + logger.error("Aes_gcm", e); + return null; + } + } +} diff --git a/gamutils/src/main/java/com/genexus/gam/utils/cryptography/Hash.java b/gamutils/src/main/java/com/genexus/gam/utils/cryptography/Hash.java new file mode 100644 index 000000000..f483cc3ec --- /dev/null +++ b/gamutils/src/main/java/com/genexus/gam/utils/cryptography/Hash.java @@ -0,0 +1,45 @@ +package com.genexus.gam.utils.cryptography; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.digests.SHA512Digest; +import org.bouncycastle.util.encoders.Base64; + +import java.nio.charset.StandardCharsets; + +public enum Hash { + + SHA256, SHA512; + + private static final Logger logger = LogManager.getLogger(Hash.class); + + public static String hash(String plainText, Hash hash) + { + switch (hash) + { + case SHA256: + return internalHash(new SHA256Digest(), plainText); + case SHA512: + return internalHash(new SHA512Digest(), plainText); + default: + logger.error("unrecognized hash"); + return ""; + } + } + + private static String internalHash(Digest digest, String plainText) + { + logger.debug("internalHash"); + if (plainText.isEmpty()) { + logger.error("hash plainText is empty"); + return ""; + } + byte[] inputBytes = plainText.getBytes(StandardCharsets.UTF_8); + byte[] retValue = new byte[digest.getDigestSize()]; + digest.update(inputBytes, 0, inputBytes.length); + digest.doFinal(retValue, 0); + return Base64.toBase64String(retValue); + } +} \ No newline at end of file diff --git a/gamutils/src/main/java/com/genexus/gam/utils/json/JWTAlgorithm.java b/gamutils/src/main/java/com/genexus/gam/utils/json/JWTAlgorithm.java new file mode 100644 index 000000000..56ae300ab --- /dev/null +++ b/gamutils/src/main/java/com/genexus/gam/utils/json/JWTAlgorithm.java @@ -0,0 +1,72 @@ +package com.genexus.gam.utils.json; + +import com.nimbusds.jose.JWSAlgorithm; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public enum JWTAlgorithm { + + HS256, HS512, HS384, RS256, RS512; + + private static final Logger logger = LogManager.getLogger(JWTAlgorithm.class); + + public static JWSAlgorithm getJWSAlgorithm(JWTAlgorithm alg) + { + logger.debug("getJWSAlgorithm"); + switch (alg) + { + case HS256: + return JWSAlgorithm.HS256; + case HS512: + return JWSAlgorithm.HS512; + case HS384: + return JWSAlgorithm.HS384; + case RS256: + return JWSAlgorithm.RS256; + case RS512: + return JWSAlgorithm.RS512; + default: + logger.error("getJWSAlgorithm - not implemented algorithm"); + return null; + } + } + + public static JWTAlgorithm getJWTAlgoritm(String alg) + { + logger.debug("getJWTAlgoritm"); + switch (alg.trim().toUpperCase()) + { + case "HS256": + return JWTAlgorithm.HS256; + case "HS512": + return JWTAlgorithm.HS512; + case "HS384": + return JWTAlgorithm.HS384; + case "RS256": + return JWTAlgorithm.RS256; + case "RS512": + return JWTAlgorithm.RS512; + default: + logger.error("getJWTAlgoritm- not implemented algorithm"); + return null; + } + } + + public static boolean isSymmetric(JWTAlgorithm alg) + { + logger.debug("isSymmetric"); + switch (alg) + { + case HS256: + case HS384: + case HS512: + return true; + case RS256: + case RS512: + return false; + default: + logger.error("isSymmetric - not implemented algorithm"); + return false; + } + } +} diff --git a/gamutils/src/main/java/com/genexus/gam/utils/json/Jwk.java b/gamutils/src/main/java/com/genexus/gam/utils/json/Jwk.java new file mode 100644 index 000000000..c4342a299 --- /dev/null +++ b/gamutils/src/main/java/com/genexus/gam/utils/json/Jwk.java @@ -0,0 +1,101 @@ +package com.genexus.gam.utils.json; + +import com.nimbusds.jose.Algorithm; +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.KeyUse; +import com.nimbusds.jose.jwk.RSAKey; +import com.nimbusds.jose.jwk.gen.RSAKeyGenerator; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.UUID; + +public class Jwk { + + private static Logger logger = LogManager.getLogger(Jwk.class); + + public static String generateKeyPair() { + try { + String kid = UUID.randomUUID().toString(); + RSAKey key = new RSAKeyGenerator(2048) + .keyUse(KeyUse.SIGNATURE) + .keyID(kid) + .algorithm(Algorithm.parse("RS256")) + .generate(); + return key.toString(); + + } catch (Exception e) { + logger.error("generateKeyPair", e); + return ""; + } + } + + public static String getPublic(String jwkString) { + if (jwkString.isEmpty()) { + logger.error("getPublic jwkString parameter is empty"); + return ""; + } + try { + JWK jwk = JWK.parse(jwkString); + return jwk.toPublicJWK().toString(); + } catch (Exception e) { + logger.error("getPublic", e); + return ""; + } + } + + public static String getAlgorithm(String jwkString) { + if (jwkString.isEmpty()) { + logger.error("getAlgorithm jwkString parameter is empty"); + return ""; + } + try { + return JWK.parse(jwkString).getAlgorithm().toString(); + } catch (Exception e) { + logger.error("getPublic", e); + return ""; + } + } + + + /*public static boolean verifyJWT(String jwkString, String token) { + if (jwkString.isEmpty()) { + logger.error("verifyJWT jwkString parameter is empty"); + return false; + } + if (token.isEmpty()) { + logger.error("verifyJWT token parameter is empty"); + return false; + } + try { + JWK jwk = JWK.parse(jwkString); + return Jwt.verify(jwk.toRSAKey().toRSAPublicKey(), token); + } catch (Exception e) { + logger.error("verifyJWT", e); + return false; + } + + } + + public static String createJwt(String jwkString, String payload, String header) { + if (jwkString.isEmpty()) { + logger.error("createJwt jwkString parameter is empty"); + return ""; + } + if (payload.isEmpty()) { + logger.error("createJwt payload parameter is empty"); + return ""; + } + if (header.isEmpty()) { + logger.error("createJwt header parameter is empty"); + return ""; + } + try { + JWK jwk = JWK.parse(jwkString); + return Jwt.create(jwk.toRSAKey().toRSAPrivateKey(), payload, header); + } catch (Exception e) { + logger.error("createJwt", e); + return ""; + } + }*/ +} diff --git a/gamutils/src/main/java/com/genexus/gam/utils/json/Jwt.java b/gamutils/src/main/java/com/genexus/gam/utils/json/Jwt.java new file mode 100644 index 000000000..64709fe5b --- /dev/null +++ b/gamutils/src/main/java/com/genexus/gam/utils/json/Jwt.java @@ -0,0 +1,102 @@ +package com.genexus.gam.utils.json; + +import com.genexus.gam.utils.keys.PrivateKeyUtil; +import com.genexus.gam.utils.keys.PublicKeyUtil; +import com.nimbusds.jose.*; +import com.nimbusds.jose.crypto.MACSigner; +import com.nimbusds.jose.crypto.MACVerifier; +import com.nimbusds.jose.crypto.RSASSASigner; +import com.nimbusds.jose.crypto.RSASSAVerifier; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.text.ParseException; +import java.util.Objects; + +public class Jwt { + + private static final Logger logger = LogManager.getLogger(Jwt.class); + + /******** EXTERNAL OBJECT PUBLIC METHODS - BEGIN ********/ + + public static boolean verify(String path, String alias, String password, String token) { + logger.debug("verify"); + try { + return verify_internal(path, alias, password, token); + } catch (Exception e) { + logger.error("verify", e); + return false; + } + } + + public static String create(String path, String alias, String password, String payload, String header) { + logger.debug("create"); + try { + return create_internal(path, alias, password, payload, header); + }catch (Exception e) + { + logger.error("create", e); + return ""; + } + } + + public static String getHeader(String token) { + logger.debug("getHeader"); + try { + return SignedJWT.parse(token).getHeader().toString(); + } catch (Exception e) { + logger.error("getHeader", e); + return ""; + } + } + + public static String getPayload(String token) { + logger.debug("getPayload"); + try { + return SignedJWT.parse(token).getPayload().toString(); + } catch (Exception e) { + logger.error("getPayload", e); + return ""; + } + } + + public static boolean verifyAlgorithm(String algorithm, String token) + { + logger.debug("verifyAlgorithm"); + try{ + return SignedJWT.parse(token).getHeader().getAlgorithm().equals(JWSAlgorithm.parse(algorithm)); + }catch (Exception e) + { + logger.error("verifyAlgorithm", e); + return false; + } + } + + /******** EXTERNAL OBJECT PUBLIC METHODS - END ********/ + + private static boolean verify_internal(String path, String alias, String password, String token) throws JOSEException, ParseException { + logger.debug("verify_internal"); + JWTAlgorithm algorithm = JWTAlgorithm.getJWTAlgoritm(JWSHeader.parse(getHeader(token)).getAlgorithm().getName()); + assert algorithm != null; + boolean isSymmetric = JWTAlgorithm.isSymmetric(algorithm); + SignedJWT signedJWT = SignedJWT.parse(token); + JWSVerifier verifier = isSymmetric ? new MACVerifier(password):new RSASSAVerifier(Objects.requireNonNull(PublicKeyUtil.getPublicKey(path, alias, password, token))); + return signedJWT.verify(verifier); + } + + private static String create_internal(String path, String alias, String password, String payload, String header) throws Exception { + logger.debug("create_internal"); + JWSHeader parsedHeader = JWSHeader.parse(header); + JWTAlgorithm algorithm = JWTAlgorithm.getJWTAlgoritm(parsedHeader.getAlgorithm().getName()); + assert algorithm != null; + boolean isSymmetric = JWTAlgorithm.isSymmetric(algorithm); + SignedJWT signedJWT = new SignedJWT(parsedHeader, JWTClaimsSet.parse(payload)); + JWSSigner signer = isSymmetric ? new MACSigner(password): new RSASSASigner(Objects.requireNonNull(PrivateKeyUtil.getPrivateKey(path, alias, password))); + signedJWT.sign(signer); + return signedJWT.serialize(); + } +} diff --git a/gamutils/src/main/java/com/genexus/gam/utils/json/UnixTimestamp.java b/gamutils/src/main/java/com/genexus/gam/utils/json/UnixTimestamp.java new file mode 100644 index 000000000..e3b1044a4 --- /dev/null +++ b/gamutils/src/main/java/com/genexus/gam/utils/json/UnixTimestamp.java @@ -0,0 +1,11 @@ +package com.genexus.gam.utils.json; + +import java.util.Date; + +public class UnixTimestamp { + + public static long create(Date gxdate) { + return gxdate.toInstant().getEpochSecond(); + } + +} diff --git a/gamutils/src/main/java/com/genexus/gam/utils/keys/PrivateKeyUtil.java b/gamutils/src/main/java/com/genexus/gam/utils/keys/PrivateKeyUtil.java new file mode 100644 index 000000000..964a43ce9 --- /dev/null +++ b/gamutils/src/main/java/com/genexus/gam/utils/keys/PrivateKeyUtil.java @@ -0,0 +1,181 @@ +package com.genexus.gam.utils.keys; + +import com.nimbusds.jose.jwk.JWK; +import org.apache.commons.io.FilenameUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.operator.InputDecryptorProvider; +import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo; +import org.bouncycastle.pkcs.jcajce.JcePKCSPBEInputDecryptorProviderBuilder; +import org.bouncycastle.util.encoders.Base64; + +import javax.crypto.EncryptedPrivateKeyInfo; +import java.io.FileReader; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.Security; +import java.security.interfaces.RSAPrivateKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Objects; + +public enum PrivateKeyUtil { + + pfx, jks, pkcs12, p12, pem, key, b64, json; + + private static Logger logger = LogManager.getLogger(PrivateKeyUtil.class); + + public static PrivateKeyUtil value(String ext) { + switch (ext.toLowerCase().trim()) { + case "pfx": + return pfx; + case "jks": + return jks; + case "pkcs12": + return pkcs12; + case "p12": + return p12; + case "pem": + return pem; + case "key": + return key; + case "b64": + return b64; + case "json": + return json; + default: + logger.error("Invalid private key file extension"); + return null; + } + } + + public static RSAPrivateKey getPrivateKey(String path, String alias, String password) throws Exception { + PrivateKeyUtil ext = PrivateKeyUtil.value(fixType(path)); + switch (Objects.requireNonNull(ext)) { + case pfx: + case jks: + case pkcs12: + case p12: + return loadFromPkcs12(path, alias, password); + case pem: + case key: + return loadFromPkcs8(path, password); + case b64: + return loadFromBase64(path); + case json: + return loadFromJson(path); + default: + logger.error("Invalid private key file extension"); + return null; + } + } + + private static RSAPrivateKey loadFromJson(String json) { + logger.debug("loadFromJson"); + try { + JWK jwk = JWK.parse(json); + return jwk.toRSAKey().toRSAPrivateKey(); + } catch (Exception e) { + logger.error("loadFromJson", e); + return null; + } + } + + private static String fixType(String input) { + logger.debug("fixType"); + try { + String extension = FilenameUtils.getExtension(input); + if (extension.isEmpty()) { + try { + Base64.decode(input); + logger.debug("b64"); + return "b64"; + } catch (Exception e) { + logger.debug("json"); + return "json"; + } + } else { + return extension; + } + } catch (IllegalArgumentException e) { + logger.debug("json"); + return "json"; + } + } + + private static RSAPrivateKey loadFromBase64(String base64) { + logger.debug("loadFromBase64"); + try (ASN1InputStream stream = new ASN1InputStream(Base64.decode(base64))) { + ASN1Sequence seq = (ASN1Sequence) stream.readObject(); + return castPrivateKeyInfo(PrivateKeyInfo.getInstance(seq)); + } catch (Exception e) { + logger.error("loadFromBase64", e); + return null; + } + } + + private static RSAPrivateKey loadFromPkcs8(String path, String password) { + logger.debug("loadFromPkcs8"); + try (FileReader privateKeyReader = new FileReader(path)) { + try (PEMParser parser = new PEMParser(privateKeyReader)) { + Object obj; + obj = parser.readObject(); + if (obj instanceof PrivateKeyInfo) { + return castPrivateKeyInfo((PrivateKeyInfo) obj); + } else if (obj instanceof PEMKeyPair) { + PEMKeyPair pemKeyPair = (PEMKeyPair) obj; + return castPrivateKeyInfo(pemKeyPair.getPrivateKeyInfo()); + } else if (obj instanceof EncryptedPrivateKeyInfo || obj instanceof PKCS8EncryptedPrivateKeyInfo) { + logger.debug("loadFromPkcs8 encrypted private key"); + Security.addProvider(new BouncyCastleProvider()); + PKCS8EncryptedPrivateKeyInfo encPrivKeyInfo = (PKCS8EncryptedPrivateKeyInfo) obj; + InputDecryptorProvider pkcs8Prov = new JcePKCSPBEInputDecryptorProviderBuilder().setProvider("BC") + .build(password.toCharArray()); + return castPrivateKeyInfo(encPrivKeyInfo.decryptPrivateKeyInfo(pkcs8Prov)); + } else { + logger.error("loadFromPkcs8: Could not load private key"); + return null; + } + } + } catch (Exception e) { + logger.error("loadFromPkcs8", e); + return null; + } + } + + private static RSAPrivateKey castPrivateKeyInfo(PrivateKeyInfo privateKeyInfo) { + logger.debug("castPrivateKeyInfo"); + try { + KeyFactory kf = KeyFactory.getInstance(privateKeyInfo.getPrivateKeyAlgorithm().getAlgorithm().getId()); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyInfo.getEncoded()); + return (RSAPrivateKey) kf.generatePrivate(keySpec); + } catch (Exception e) { + logger.error("castPrivateKeyInfo", e); + return null; + } + } + + private static RSAPrivateKey loadFromPkcs12(String path, String alias, String password) { + logger.debug("loadFromPkcs12"); + try (InputStream targetStream = Files.newInputStream(Paths.get(path))) { + KeyStore ks = KeyStore.getInstance("PKCS12"); + ks.load(targetStream, password.toCharArray()); + if (alias.isEmpty()) { + return (RSAPrivateKey) ks.getKey(ks.aliases().nextElement(), password.toCharArray()); + } else { + return (RSAPrivateKey) ks.getKey(alias, password.toCharArray()); + } + } catch (Exception e) { + logger.error("loadFromPkcs12", e); + return null; + } + } +} diff --git a/gamutils/src/main/java/com/genexus/gam/utils/keys/PublicKeyUtil.java b/gamutils/src/main/java/com/genexus/gam/utils/keys/PublicKeyUtil.java new file mode 100644 index 000000000..02e6ce314 --- /dev/null +++ b/gamutils/src/main/java/com/genexus/gam/utils/keys/PublicKeyUtil.java @@ -0,0 +1,190 @@ +package com.genexus.gam.utils.keys; + +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.JWKSet; +import com.nimbusds.jwt.SignedJWT; +import org.apache.commons.io.FilenameUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.util.encoders.Base64; + +import java.io.*; +import java.security.KeyStore; +import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPublicKey; +import java.text.ParseException; +import java.util.Objects; + +public enum PublicKeyUtil { + + crt, cer, pfx, jks, pkcs12, p12, pem, key, b64, json; + + private static final Logger logger = LogManager.getLogger(PublicKeyUtil.class); + + + public static RSAPublicKey getPublicKey(String path, String alias, String password, String token) throws NullPointerException { + logger.debug("getPublicKey"); + PublicKeyUtil ext = PublicKeyUtil.value(fixType(path)); + switch (Objects.requireNonNull(ext)) { + case crt: + case cer: + return (RSAPublicKey) Objects.requireNonNull(loadFromDer(path)).getPublicKey(); + case pfx: + case jks: + case pkcs12: + case p12: + return (RSAPublicKey) Objects.requireNonNull(loadFromPkcs12(path, alias, password)).getPublicKey(); + case pem: + case key: + return (RSAPublicKey) Objects.requireNonNull(loadFromPkcs8(path)).getPublicKey(); + case b64: + return (RSAPublicKey) Objects.requireNonNull(loadFromBase64(path)).getPublicKey(); + case json: + return loadFromJson(path, token); + default: + logger.error("Invalid public key file extension"); + return null; + } + } + + private static PublicKeyUtil value(String ext) { + switch (ext.toLowerCase().trim()) { + case "crt": + return crt; + case "cer": + return cer; + case "pfx": + return pfx; + case "jks": + return jks; + case "pkcs12": + return pkcs12; + case "p12": + return p12; + case "pem": + return pem; + case "key": + return key; + case "b64": + return b64; + case "json": + return json; + default: + logger.error("Invalid certificate file extension"); + return null; + } + } + + private static RSAPublicKey loadFromJson(String json, String token) { + logger.debug("loadFromJson"); + try { + JWK jwk = JWK.parse(json); + return (RSAPublicKey) jwk.toRSAKey().toPublicKey(); + } catch (ParseException e) { + return loadFromJwks(json, token); + } catch (Exception e) { + logger.error("loadFromJson", e); + return null; + } + } + + private static RSAPublicKey loadFromJwks(String json, String token) { + logger.debug("loadFromJwks"); + try { + com.nimbusds.jose.JWSHeader header = SignedJWT.parse(token).getHeader(); + JWKSet set = JWKSet.parse(json); + JWK jwk = set.getKeyByKeyId(header.getKeyID()); + return (RSAPublicKey) jwk.toRSAKey().toPublicKey(); + } catch (Exception e) { + logger.error("loadFromJwks", e); + return null; + } + } + + private static String fixType(String input) { + logger.debug("fixType"); + try { + String extension = FilenameUtils.getExtension(input); + if (extension.isEmpty() || extension.contains("}")) { + try { + Base64.decode(input); + logger.debug("b64"); + return "b64"; + } catch (Exception e) { + logger.debug("json"); + return "json"; + } + } else { + return extension; + } + } catch (IllegalArgumentException e) { + logger.debug("json"); + return "json"; + } + } + + + private static X509Certificate loadFromBase64(String base64) { + logger.debug("loadBase64"); + try { + ByteArrayInputStream byteArray = new ByteArrayInputStream(Base64.decode(base64)); + CertificateFactory factory = new CertificateFactory(); + return (X509Certificate) factory.engineGenerateCertificate(byteArray); + } catch (Exception e) { + logger.error("loadBase64", e); + return null; + } + } + + private static X509Certificate loadFromPkcs8(String path) { + logger.debug("loadFromPkcs8"); + try (FileReader privateKeyReader = new FileReader(new File(path))) { + try (PEMParser parser = new PEMParser(privateKeyReader)) { + Object obj = parser.readObject(); + if (obj instanceof X509CertificateHolder) { + X509CertificateHolder x509 = (X509CertificateHolder) obj; + try (InputStream in = new ByteArrayInputStream(x509.getEncoded())) { + CertificateFactory certFactory = new CertificateFactory(); + return (X509Certificate) certFactory.engineGenerateCertificate(in); + } + } else { + logger.error("Error reading certificate"); + return null; + } + } + } catch (Exception e) { + logger.error("loadFromPem", e); + return null; + } + } + + private static X509Certificate loadFromPkcs12(String path, String alias, String password) { + logger.debug("loadFromPkcs12"); + try (FileInputStream inStream = new FileInputStream(path)) { + KeyStore ks = KeyStore.getInstance("PKCS12"); + ks.load(inStream, password.toCharArray()); + if (alias.isEmpty()) { + return (X509Certificate) ks.getCertificate(ks.aliases().nextElement()); + } else { + return (X509Certificate) ks.getCertificate(alias); + } + } catch (Exception e) { + logger.error("loadFromPkcs12", e); + return null; + } + } + + private static X509Certificate loadFromDer(String path) { + logger.debug("loadFromDer"); + try (FileInputStream inStream = new FileInputStream(path)) { + CertificateFactory cf = new CertificateFactory(); + return (X509Certificate) cf.engineGenerateCertificate(inStream); + } catch (Exception e) { + logger.error("loadFromDer", e); + return null; + } + } +} diff --git a/gamutils/src/test/java/com/genexus/gam/utils/test/EncodingTest.java b/gamutils/src/test/java/com/genexus/gam/utils/test/EncodingTest.java new file mode 100644 index 000000000..1aaed898c --- /dev/null +++ b/gamutils/src/test/java/com/genexus/gam/utils/test/EncodingTest.java @@ -0,0 +1,80 @@ +package com.genexus.gam.utils.test; + +import com.genexus.gam.GamUtilsEO; +import com.genexus.gam.utils.Encoding; +import com.genexus.gam.utils.Random; +import org.bouncycastle.util.encoders.Base64; +import org.bouncycastle.util.encoders.Hex; +import org.junit.Assert; +import org.junit.Test; + +import java.nio.charset.StandardCharsets; +import java.text.MessageFormat; + +public class EncodingTest { + + @Test + public void testB64ToB64Url() { + int i = 0; + do { + String randomString = GamUtilsEO.randomAlphanumeric(128); + String testing = GamUtilsEO.base64ToBase64Url(Base64.toBase64String(randomString.getBytes(StandardCharsets.UTF_8))); + Assert.assertEquals("testB64ToB64Url", randomString, b64UrlToUtf8(testing)); + i++; + } while (i < 50); + } + + private static String b64UrlToUtf8(String base64Url) { + try { + return new String(java.util.Base64.getUrlDecoder().decode(base64Url), StandardCharsets.ISO_8859_1); + } catch (Exception e) { + e.printStackTrace(); + return ""; + } + } + + @Test + public void testBase64Url() { + String[] utf8 = new String[]{"GQTuYnnS9AbcKXndwxiZbxk4Q60nhuEd", "rf7tZx8aWO28YOKLISDWY33HuarNHkIZ", "sF7Ic0iuZxE50nz3W5Jnj7R0nQlRD0b1", "GGKmW2ubkhnA9ASaVlVAKM6FQdPCQ1pj", "LMW0GSCVyeGiGzf84eIwuX6OHAfur9fp", "zq9Kni7W1r0UIzG9hjYeiqJhSYlWVZSa", "WcyhGLQNyQkP2YmOjVtIilpqcHgYCzjq", "DuhO4PBiXRDDj50RBRo8wNUU8R3UXbp0", "pkPfYXOyoLUsEwm4HjjDB6E2c3aUjYNh", "fgbrZoKKMym9HN5zlKj0a8ohgQlJm3PM", "owGXQ7p6BeFeK1KFVOsdbSRd0sMwgFRU"}; + String[] b64 = new String[]{"R1FUdVlublM5QWJjS1huZHd4aVpieGs0UTYwbmh1RWQ", "cmY3dFp4OGFXTzI4WU9LTElTRFdZMzNIdWFyTkhrSVo", "c0Y3SWMwaXVaeEU1MG56M1c1Sm5qN1IwblFsUkQwYjE", "R0dLbVcydWJraG5BOUFTYVZsVkFLTTZGUWRQQ1ExcGo", "TE1XMEdTQ1Z5ZUdpR3pmODRlSXd1WDZPSEFmdXI5ZnA", "enE5S25pN1cxcjBVSXpHOWhqWWVpcUpoU1lsV1ZaU2E", "V2N5aEdMUU55UWtQMlltT2pWdElpbHBxY0hnWUN6anE", "RHVoTzRQQmlYUkREajUwUkJSbzh3TlVVOFIzVVhicDA", "cGtQZllYT3lvTFVzRXdtNEhqakRCNkUyYzNhVWpZTmg", "ZmdiclpvS0tNeW05SE41emxLajBhOG9oZ1FsSm0zUE0", "b3dHWFE3cDZCZUZlSzFLRlZPc2RiU1JkMHNNd2dGUlU"}; + for (int i = 0; i < utf8.length; i++) { + Assert.assertEquals(MessageFormat.format("testBase64Url toBase64Url fail index: {0}", i), b64[i], Encoding.toBase64Url(utf8[i])); + Assert.assertEquals(MessageFormat.format("testBase64Url fromBase64Url fail index: {0}", i), utf8[i], Encoding.fromBase64Url(b64[i])); + } + } + + @Test + public void testToBase64Url() { + int i = 0; + do { + String randomString = GamUtilsEO.randomAlphanumeric(128); + String testing = GamUtilsEO.toBase64Url(randomString); + Assert.assertEquals("testB64ToB64Url", randomString, GamUtilsEO.fromBase64Url(testing)); + i++; + } while (i < 50); + } + + @Test + public void testHexaToBase64() + { + int i = 0; + do { + String randomHexa = Random.hexaBits(128); + String testing = b64ToHexa(Encoding.hexaToBase64(randomHexa)); + Assert.assertEquals("testB64ToB64Url", randomHexa, testing); + i++; + } while (i < 50); + } + + private static String b64ToHexa(String base64) { + try { + byte[] bytes = Base64.decode(base64); + return Hex.toHexString(bytes); + } catch (Exception e) { + e.printStackTrace(); + return ""; + } + } + + +} diff --git a/gamutils/src/test/java/com/genexus/gam/utils/test/EncryptionTest.java b/gamutils/src/test/java/com/genexus/gam/utils/test/EncryptionTest.java new file mode 100644 index 000000000..f5b616cb4 --- /dev/null +++ b/gamutils/src/test/java/com/genexus/gam/utils/test/EncryptionTest.java @@ -0,0 +1,20 @@ +package com.genexus.gam.utils.test; + +import com.genexus.gam.GamUtilsEO; +import org.junit.Assert; +import org.junit.Test; + +public class EncryptionTest { + + @Test + public void testAesGcm() { + String key = GamUtilsEO.randomHexaBits(256); + String nonce = GamUtilsEO.randomHexaBits(128); + String txt = "hello world"; + int macSize = 64; + String encrypted = GamUtilsEO.AesGcm(txt, key, nonce, macSize, true); + Assert.assertFalse("testAesGcm encrypt", encrypted.isEmpty()); + String decrypted = GamUtilsEO.AesGcm(encrypted, key, nonce, macSize, false); + Assert.assertEquals("testAesGcm decrypt", txt, decrypted); + } +} diff --git a/gamutils/src/test/java/com/genexus/gam/utils/test/HashTest.java b/gamutils/src/test/java/com/genexus/gam/utils/test/HashTest.java new file mode 100644 index 000000000..ad81f8b3a --- /dev/null +++ b/gamutils/src/test/java/com/genexus/gam/utils/test/HashTest.java @@ -0,0 +1,57 @@ +package com.genexus.gam.utils.test; + +import com.genexus.gam.GamUtilsEO; +import com.genexus.gam.utils.Random; +import com.genexus.gam.utils.test.resources.CryptographicHash; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class HashTest { + + private static String one; + private static String two; + private static String three; + private static String four; + private static String five; + + private static CryptographicHash cryptographicHash; + + @BeforeClass + public static void setUp() { + one = "one"; + two = "two"; + three = "three"; + four = "four"; + five = "five"; + cryptographicHash = new CryptographicHash("SHA-512"); + } + + @Test + public void testSha512() { + Assert.assertEquals("one: ", cryptographicHash.ComputeHash(one), GamUtilsEO.sha512(one)); + Assert.assertEquals("two: ", cryptographicHash.ComputeHash(two), GamUtilsEO.sha512(two)); + Assert.assertEquals("three: ", cryptographicHash.ComputeHash(three), GamUtilsEO.sha512(three)); + Assert.assertEquals("four: ", cryptographicHash.ComputeHash(four), GamUtilsEO.sha512(four)); + Assert.assertEquals("five: ", cryptographicHash.ComputeHash(five), GamUtilsEO.sha512(five)); + } + + @Test + public void testSha512Random() { + for (int i = 0; i < 100; i++) { + String value = Random.alphanumeric(15); + Assert.assertEquals("random sha512 ", cryptographicHash.ComputeHash(value), GamUtilsEO.sha512(value)); + } + } + @Test + public void testSha256() + { + String[] arrayInputs = new String[] {one, two, three, four, five}; + String[] arrayRes= new String[] {"dpLDrTVAu4A8Ags67mbNiIcSMjTqDG5xQ8Ct1z/0Me0=", "P8TM/nRYcOLA2Z9x8w/wZWyN7dQcwdfT03aw2+aF4vM=", "i1udsME9skJWyCmqNkqpDG0uujGLkjKkq5MTuVTTVV8=", "BO+vCA9aPnThwp0cpqSFaTgsu80yTo1Z0rg+8hwDnwA=", "IisL1R/O9+ZcLmLbLtZUVwE7q1a+b6/rGe4R1FMVPIA="}; + for(int i = 0; i < arrayInputs.length; i++) + { + Assert.assertEquals("testSha256 error", GamUtilsEO.sha256(arrayInputs[i]), arrayRes[i]); + } + } + +} \ No newline at end of file diff --git a/gamutils/src/test/java/com/genexus/gam/utils/test/JwkTest.java b/gamutils/src/test/java/com/genexus/gam/utils/test/JwkTest.java new file mode 100644 index 000000000..3c6a40d6c --- /dev/null +++ b/gamutils/src/test/java/com/genexus/gam/utils/test/JwkTest.java @@ -0,0 +1,38 @@ +package com.genexus.gam.utils.test; + +import com.genexus.gam.GamUtilsEO; +import com.nimbusds.jose.jwk.JWKSet; +import org.junit.Assert; +import org.junit.Test; + +import java.text.ParseException; + +public class JwkTest { + + @Test + public void testGenerateKeyPair() { + String jwk = GamUtilsEO.generateKeyPair(); + Assert.assertFalse("Generate key pair jwk", jwk.isEmpty()); + } + + @Test + public void testPublicJwk() { + String jwk = GamUtilsEO.generateKeyPair(); + String public_jwk = GamUtilsEO.getPublicJwk(jwk); + String public_jwks = "{\"keys\": [" + public_jwk + "]}"; + try { + JWKSet jwks = JWKSet.parse(public_jwks).toPublicJWKSet(); + Assert.assertNotNull("To public JWK fail", jwks); + } catch (ParseException e) { + Assert.fail("Exception on testPublicJwk" + e.getMessage()); + } + } + + @Test + public void testGetAlgorithm() + { + String jwk = GamUtilsEO.generateKeyPair(); + String algorithm = GamUtilsEO.getJwkAlgorithm(jwk); + Assert.assertEquals("testGetAlgorithm", algorithm, "RS256"); + } +} diff --git a/gamutils/src/test/java/com/genexus/gam/utils/test/JwtTest.java b/gamutils/src/test/java/com/genexus/gam/utils/test/JwtTest.java new file mode 100644 index 000000000..45025a4ae --- /dev/null +++ b/gamutils/src/test/java/com/genexus/gam/utils/test/JwtTest.java @@ -0,0 +1,221 @@ +package com.genexus.gam.utils.test; + +import com.genexus.gam.GamUtilsEO; +import com.nimbusds.jose.jwk.JWK; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.UUID; + +public class JwtTest { + + private static String headerRsa; + private static String payload; + private static String path_RSA_sha256_2048; + private static String alias; + private static String password; + + @BeforeClass + public static void setUp() { + + String resources = System.getProperty("user.dir").concat("/src/test/resources"); + String kid = UUID.randomUUID().toString(); + headerRsa = "{\n" + + " \"alg\": \"RS256\",\n" + + " \"kid\": \"" + kid + "\",\n" + + " \"typ\": \"JWT\"\n" + + "}"; + payload = "{\n" + + " \"sub\": \"1234567890\",\n" + + " \"name\": \"John Doe\",\n" + + " \"iat\": 1516239022\n" + + "}"; + path_RSA_sha256_2048 = resources.concat("/dummycerts/RSA_sha256_2048/"); + alias = "1"; + password = "dummy"; + } + + @Test + public void test_pkcs8_pem() { + String token = GamUtilsEO.createJwt(path_RSA_sha256_2048 + "sha256d_key.pem", "", "", payload, headerRsa); + Assert.assertFalse("test_pkcs8 create", token.isEmpty()); + boolean result = GamUtilsEO.verifyJwt(path_RSA_sha256_2048 + "sha256_cert.cer", "", "", token); + Assert.assertTrue("test_pkcs8 verify cer", result); + } + + @Test + public void test_get() { + String token = GamUtilsEO.createJwt(path_RSA_sha256_2048 + "sha256d_key.pem", "", "", payload, headerRsa); + Assert.assertFalse("test_get create", token.isEmpty()); + String header_get = GamUtilsEO.getJwtHeader(token); + Assert.assertFalse("test_get getHeader", header_get.isEmpty()); + String payload_get = GamUtilsEO.getJwtPayload(token); + Assert.assertFalse("test_get getPayload", payload_get.isEmpty()); + } + + @Test + public void test_pkcs8_key() { + String token = GamUtilsEO.createJwt(path_RSA_sha256_2048 + "sha256d_key.key", "", "", payload, headerRsa); + Assert.assertFalse("test_pkcs8 create", token.isEmpty()); + boolean result = GamUtilsEO.verifyJwt(path_RSA_sha256_2048 + "sha256_cert.crt", "", "", token); + Assert.assertTrue("test_pkcs8 verify crt", result); + } + + @Test + public void test_pkcs8_encrypted() { + String token = GamUtilsEO.createJwt(path_RSA_sha256_2048 + "sha256_key.pem", "", password, payload, headerRsa); + Assert.assertFalse("test_pkcs8_encrypted", token.isEmpty()); + boolean result = GamUtilsEO.verifyJwt(path_RSA_sha256_2048 + "sha256_cert.crt", "", "", token); + Assert.assertTrue("test_pkcs8_encrypted verify crt", result); + } + + @Test + public void test_pkcs12_p12() { + String token = GamUtilsEO.createJwt(path_RSA_sha256_2048 + "sha256_cert.p12", alias, password, payload, headerRsa); + Assert.assertFalse("test_pkcs12_p12 create", token.isEmpty()); + boolean result = GamUtilsEO.verifyJwt(path_RSA_sha256_2048 + "sha256_cert.p12", alias, password, token); + Assert.assertTrue("test_pkcs12_p12 verify", result); + } + + @Test + public void test_pkcs12_pkcs12() { + String token = GamUtilsEO.createJwt(path_RSA_sha256_2048 + "sha256_cert.pkcs12", alias, password, payload, headerRsa); + Assert.assertFalse("test_pkcs12_pkcs12 create", token.isEmpty()); + boolean result = GamUtilsEO.verifyJwt(path_RSA_sha256_2048 + "sha256_cert.pkcs12", alias, password, token); + Assert.assertTrue("test_pkcs12_pkcs12 verify", result); + } + + @Test + public void test_pkcs12_jks() { + String token = GamUtilsEO.createJwt(path_RSA_sha256_2048 + "sha256_cert.jks", alias, password, payload, headerRsa); + Assert.assertFalse("test_pkcs12_jks create", token.isEmpty()); + boolean result = GamUtilsEO.verifyJwt(path_RSA_sha256_2048 + "sha256_cert.jks", alias, password, token); + Assert.assertTrue("test_pkcs12_jks verify", result); + } + + @Test + public void test_pkcs12_pfx() { + String token = GamUtilsEO.createJwt(path_RSA_sha256_2048 + "sha256_cert.pfx", alias, password, payload, headerRsa); + Assert.assertFalse("test_pkcs12_pfx create", token.isEmpty()); + boolean result = GamUtilsEO.verifyJwt(path_RSA_sha256_2048 + "sha256_cert.pfx", alias, password, token); + Assert.assertTrue("test_pkcs12_pfx verify", result); + } + + @Test + public void test_pkcs12_noalias() { + String token = GamUtilsEO.createJwt(path_RSA_sha256_2048 + "sha256_cert.jks", "", password, payload, headerRsa); + Assert.assertFalse("test_pkcs12_noalias jks create", token.isEmpty()); + boolean result = GamUtilsEO.verifyJwt(path_RSA_sha256_2048 + "sha256_cert.jks", "", password, token); + Assert.assertTrue("test_pkcs12_noalias jks verify", result); + } + + @Test + public void test_b64() { + String publicKey = "MIIEATCCAumgAwIBAgIJAIAqvKHZ+gFhMA0GCSqGSIb3DQEBCwUAMIGWMQswCQYDVQQGEwJVWTETMBEGA1UECAwKTW9udGV2aWRlbzETMBEGA1UEBwwKTW9udGV2aWRlbzEQMA4GA1UECgwHR2VuZVh1czERMA8GA1UECwwIU2VjdXJpdHkxEjAQBgNVBAMMCXNncmFtcG9uZTEkMCIGCSqGSIb3DQEJARYVc2dyYW1wb25lQGdlbmV4dXMuY29tMB4XDTIwMDcwODE4NTcxN1oXDTI1MDcwNzE4NTcxN1owgZYxCzAJBgNVBAYTAlVZMRMwEQYDVQQIDApNb250ZXZpZGVvMRMwEQYDVQQHDApNb250ZXZpZGVvMRAwDgYDVQQKDAdHZW5lWHVzMREwDwYDVQQLDAhTZWN1cml0eTESMBAGA1UEAwwJc2dyYW1wb25lMSQwIgYJKoZIhvcNAQkBFhVzZ3JhbXBvbmVAZ2VuZXh1cy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1zgaU+Wh63p9DNWoAy64252EvZjN49AY3x0QCnAa8JO9Pk7znQwrxEFUKgZzv0GHEYW7+X+uyJr7BW4TA6fuJJ8agE/bmZRZyjdJjoue0FML6fbmCZ9Tsxpxe4pzispyWQ8jYT4Kl4I3fdZNUSn4XSidnDKBISeC05mrcchDKhInpiYDJ481lsB4JTEti3S4Xy/ToKwY4t6attw6z5QDhBc+Yro+YUqruliOAKqcfybe9k07jwMCvFVM1hrYYJ7hwHDSFo3MKwZ0y2gw0w6SgVBxLFo+KYP3q63b5wVhD8lzaSh+8UcyiHM2/yjEej7EnRFzdclTSNXRFNaiLnEVdAgMBAAGjUDBOMB0GA1UdDgQWBBQtQAWJRWNr/OswPSAdwCQh0Eei/DAfBgNVHSMEGDAWgBQtQAWJRWNr/OswPSAdwCQh0Eei/DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCjHe3JbNKv0Ywc1zlLacUNWcjLbmzvnjs8Wq5oxtf5wG5PUlhLSYZ9MPhuf95PlibnrO/xVY292P5lo4NKhS7VOonpbPQ/PrCMO84Pz1LGfM/wCWQIowh6VHq18PiZka9zbwl6So0tgClKkFSRk4wpKrWX3+M3+Y+D0brd8sEtA6dXeYHDtqV0YgjKdZIIOx0vDT4alCoVQrQ1yAIq5INT3cSLgJezIhEadDv3Tc7bMxMFeL+81qHm9Z/9/KE6Z+JB0ZEOkF/2NSQJd+Z7MBR8CxOdTQis3ltMoXDatNkjZ2Env40sw4NICB8YYhsWMIarew5uNT+RS28YHNlbmogh"; + String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC1zgaU+Wh63p9DNWoAy64252EvZjN49AY3x0QCnAa8JO9Pk7znQwrxEFUKgZzv0GHEYW7+X+uyJr7BW4TA6fuJJ8agE/bmZRZyjdJjoue0FML6fbmCZ9Tsxpxe4pzispyWQ8jYT4Kl4I3fdZNUSn4XSidnDKBISeC05mrcchDKhInpiYDJ481lsB4JTEti3S4Xy/ToKwY4t6attw6z5QDhBc+Yro+YUqruliOAKqcfybe9k07jwMCvFVM1hrYYJ7hwHDSFo3MKwZ0y2gw0w6SgVBxLFo+KYP3q63b5wVhD8lzaSh+8UcyiHM2/yjEej7EnRFzdclTSNXRFNaiLnEVdAgMBAAECggEBAJP8ajslcThisjzg47JWGS8z1FXi2Q8hg1Yv61o8avcHEY0y8tdEKUnkQ3TT4E0M0CgsL078ATz4cNmvhzYIv+j66aEv3w/XRRhl/NWBqx1YsQV5BWHy5sz9Nhe+WnnlbbSa5Ie+4NfpG1LDv/Mi19RZVg15p5ZwHGrkDCP47VYKgFXw51ZPxq/l3IIeq4PyueC/EPSAp4e9qei7p85k3i2yiWsHgZaHwHgDTx2Hgq1y/+/E5+HNxL2OlPr5lzlN2uIPZ9Rix2LDh0FriuCEjrXFsTJHw4LTK04rkeGledMtw6/bOTxibFbgeuQtY1XzG/M0+xlP2niBbAEA4Z6vTsECgYEA6k7LSsh6azsk0W9+dE6pbc3HisOoKI76rXi38gEQdCuF04OKt46WttQh4r1+dseO4OgjXtRMS0+5Hmx2jFXjPJexMgLftvrbwaVqg9WHenKL/qj5imCn4kVaa4Jo1VHFaIY+1b+iv+6WY/leFxGntAki9u4PRogRrUrWLRH9keUCgYEAxqLisgMHQGcpJDHJtI2N+HUgLDN065PtKlEP9o6WBwAb86/InVaTo2gmEvmslNQDYH16zdTczWMHnnBx1B012KpUD+t5CWIvMZdsTnMRDjWhdgm5ylN9NT89t5X8GPvo36WjuXAKWWjcRodzRgo57z9achCyMKhGU5yDOxh8jhkCgYAx6rtwoSlDcwQzAjfEe4Wo+PAL5gcLLPrGvjMiAYwJ08Pc/ectl9kP9j2J2qj4kSclTw9KApyGZuOfUagn2Zxhqkd7yhTzHJp4tM7uay1DrueYR1NyYYkisXfD87J1z8forsDwNLVtglzTy6p56674sgGa7bifZBmv+4OJco286QKBgQC4dGXDHGDNg36G590A1zpw8ILxyM7YPEPOOfxy3rGeypEqV6AZy13KLlq84DFM+xwvrBYvsW1hJIbcsFpjuMRZ8MGjDu0Us6JTkOO4bc32vgKzlBB9O85XdeSf6J1zrenwVOaWut5BbMiwjfOTpMdrzg71QV/XI0w7NGoApJp1cQKBgERfI6AfJTaKtEpfX3udR1B3zra1Y42ppU2TvGI5J2/cItENoyRmtyKYDp2I036/Pe63nuIzs31i6q/hCr9Tv3AGoSVKuPLpCWv5xVO/BPhGs5dwx81nUo0/P+H2X8dx7g57PQY4uf4F9+EIXeAdbPqfB8GBW7RX3FDx5NpB+Hh/"; + String token = GamUtilsEO.createJwt(privateKey, "", "", payload, headerRsa); + Assert.assertFalse("test_b64 create", token.isEmpty()); + boolean result = GamUtilsEO.verifyJwt(publicKey, "", "", token); + Assert.assertTrue("test_b64 verify", result); + } + + @Test + public void test_json_jwk() { + String keyPair = GamUtilsEO.generateKeyPair(); + String token = GamUtilsEO.createJwt(keyPair, "", "", payload, headerRsa); + Assert.assertFalse("test_json_jwk create", token.isEmpty()); + String publicJwk = GamUtilsEO.getPublicJwk(keyPair); + boolean result = GamUtilsEO.verifyJwt(publicJwk, "", "", token); + Assert.assertTrue("test_json_jwk verify", result); + } + + @Test + public void test_json_jwks() { + String keyPair = GamUtilsEO.generateKeyPair(); + String publicJwk = GamUtilsEO.getPublicJwk(keyPair); + String header_jwks = makeHeader(publicJwk); + String token = GamUtilsEO.createJwt(keyPair, "", "", payload, header_jwks); + Assert.assertFalse("test_json_jwks create", token.isEmpty()); + String publicJwks = "{\"keys\": [" + publicJwk + "]}"; + boolean result = GamUtilsEO.verifyJwt(publicJwks, "", "", token); + Assert.assertTrue("test_json_jwks verify", result); + } + + @Test + public void test_json_Sha256() + { + String header = "{\n" + + " \"alg\": \"HS256\",\n" + + " \"typ\": \"JWT\"\n" + + "}"; + int[] lengths = new int[]{32, 64, 128}; + for (int n : lengths) { + String secret = GamUtilsEO.randomAlphanumeric(n); + String token = GamUtilsEO.createJwt("", "", secret, payload, header); + Assert.assertFalse("test_json_Sha256 create", token.isEmpty()); + boolean result = GamUtilsEO.verifyJwt("", "", secret, token); + Assert.assertTrue("test_json_Sha256 verify", result); + } + } + + @Test + public void test_json_Sha512() + { + String header = "{\n" + + " \"alg\": \"HS512\",\n" + + " \"typ\": \"JWT\"\n" + + "}"; + int[] lengths = new int[]{64, 128}; + for (int n : lengths) { + String secret = GamUtilsEO.randomAlphanumeric(n); + String token = GamUtilsEO.createJwt("", "", secret, payload, header); + Assert.assertFalse("test_json_Sha512 create", token.isEmpty()); + boolean result = GamUtilsEO.verifyJwt("", "", secret, token); + Assert.assertTrue("test_json_Sha512 verify", result); + } + } + + @Test + public void test_VerifyAlgorithm_True() + { + String header = "{\n" + + " \"alg\": \"HS512\",\n" + + " \"typ\": \"JWT\"\n" + + "}"; + String secret = GamUtilsEO.randomAlphanumeric(64); + String token = GamUtilsEO.createJwt("", "", secret, payload, header); + boolean resultSha512 = GamUtilsEO.verifyAlgorithm("HS512", token); + Assert.assertTrue("test_VerifyAlgorithm_True", resultSha512); + } + + @Test + public void test_VerifyAlgorithm_False() + { + String header = "{\n" + + " \"alg\": \"HS512\",\n" + + " \"typ\": \"JWT\"\n" + + "}"; + String secret = GamUtilsEO.randomAlphanumeric(64); + String token = GamUtilsEO.createJwt("", "", secret, payload, header); + boolean resultSha512 = GamUtilsEO.verifyAlgorithm("RS256", token); + Assert.assertFalse("test_VerifyAlgorithm_False", resultSha512); + } + + private static String makeHeader(String publicJwk) { + try { + JWK jwk = JWK.parse(publicJwk); + return "{\n" + + " \"alg\": \"RS256\",\n" + + " \"kid\": \"" + jwk.getKeyID() + "\",\n" + + " \"typ\": \"JWT\"\n" + + "}"; + + } catch (Exception e) { + e.printStackTrace(); + return ""; + } + } + + +} diff --git a/gamutils/src/test/java/com/genexus/gam/utils/test/PkceTest.java b/gamutils/src/test/java/com/genexus/gam/utils/test/PkceTest.java new file mode 100644 index 000000000..eb588e7db --- /dev/null +++ b/gamutils/src/test/java/com/genexus/gam/utils/test/PkceTest.java @@ -0,0 +1,42 @@ +package com.genexus.gam.utils.test; + +import com.genexus.gam.GamUtilsEO; +import com.genexus.gam.utils.Pkce; +import org.junit.Assert; +import org.junit.Test; + +import java.text.MessageFormat; + +public class PkceTest { + + @Test + public void testPkceS256() + { + int i = 0; + while (i<50) + { + String[] s256_true = GamUtilsEO.pkce_create(20, "S256").split(","); + Assert.assertTrue("testPkceS256 true", GamUtilsEO.pkce_verify(s256_true[0], s256_true[1], "S256")); + + String[] s256_false = GamUtilsEO.pkce_create(20, "S256").split(","); + Assert.assertFalse("testPkceS256 false", GamUtilsEO.pkce_verify(MessageFormat.format("{0}tralala",s256_false[0]), s256_false[1], "S256")); + i++; + } + } + + @Test + public void testPkcePlain() + { + int i = 0; + while (i<50) + { + String[] plain_true = GamUtilsEO.pkce_create(20, "PLAIN").split(","); + Assert.assertTrue("testPkceS256", GamUtilsEO.pkce_verify(plain_true[0], plain_true[1], "PLAIN")); + + String[] plain_false = GamUtilsEO.pkce_create(20, "PLAIN").split(","); + Assert.assertFalse("testPkceS256 false", GamUtilsEO.pkce_verify(MessageFormat.format("{0}tralala",plain_false[0]), plain_false[1], "PLAIN")); + i++; + } + } + +} diff --git a/gamutils/src/test/java/com/genexus/gam/utils/test/RandomTest.java b/gamutils/src/test/java/com/genexus/gam/utils/test/RandomTest.java new file mode 100644 index 000000000..792db9832 --- /dev/null +++ b/gamutils/src/test/java/com/genexus/gam/utils/test/RandomTest.java @@ -0,0 +1,78 @@ +package com.genexus.gam.utils.test; + +import com.genexus.gam.GamUtilsEO; +import org.bouncycastle.util.encoders.Hex; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.nio.charset.StandardCharsets; + +public class RandomTest { + + private static int l128; + private static int l256; + + private static int l5; + + private static int l10; + + @BeforeClass + public static void setUp() { + l128 = 128; + l256 = 256; + l5 = 5; + l10 = 10; + } + + @Test + public void testRandomNumeric() { + String l5_string = GamUtilsEO.randomNumeric(l5); + Assert.assertEquals("l5 numeric: ", l5, l5_string.length()); + + String l10_string = GamUtilsEO.randomNumeric(l10); + Assert.assertEquals("l10 numeric: ", l10, l10_string.length()); + + String l128_string = GamUtilsEO.randomNumeric(l128); + Assert.assertEquals("l128 numeric: ", l128, l128_string.length()); + + String l256_string = GamUtilsEO.randomNumeric(l256); + Assert.assertEquals("l256 numeric: ", l256, l256_string.length()); + + } + + @Test + public void testRandomAlphanumeric() { + String l5_string = GamUtilsEO.randomAlphanumeric(l5); + Assert.assertEquals("l5 alphanumeric: ", l5, l5_string.length()); + + String l10_string = GamUtilsEO.randomAlphanumeric(l10); + Assert.assertEquals("l10 alphanumeric: ", l10, l10_string.length()); + + String l128_string = GamUtilsEO.randomAlphanumeric(l128); + Assert.assertEquals("l128 alphanumeric: ", l128, l128_string.length()); + + String l256_string = GamUtilsEO.randomAlphanumeric(l256); + Assert.assertEquals("l256 alphanumeric: ", l256, l256_string.length()); + } + + @Test + public void testHexaBits() { + int[] lengths = new int[]{32, 64, 128, 256, 512, 1024}; + for (int n : lengths) { + String hexa = GamUtilsEO.randomHexaBits(n); + Assert.assertFalse("TestHexaBits", hexa.isEmpty()); + try + { + byte[] decoded = Hex.decode(hexa); + if(decoded.length*8 != n) + { + Assert.fail("testHexaBits wrong hexa length"); + } + }catch(Exception e) + { + Assert.fail("testHexaBits not hexa characters" + e.getMessage()); + } + } + } +} diff --git a/gamutils/src/test/java/com/genexus/gam/utils/test/UnixTimestampTest.java b/gamutils/src/test/java/com/genexus/gam/utils/test/UnixTimestampTest.java new file mode 100644 index 000000000..43ebf395b --- /dev/null +++ b/gamutils/src/test/java/com/genexus/gam/utils/test/UnixTimestampTest.java @@ -0,0 +1,44 @@ +package com.genexus.gam.utils.test; + +import com.genexus.gam.GamUtilsEO; +import org.junit.Assert; +import org.junit.Test; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +public class UnixTimestampTest { + + @Test + public void testCreate() { + Date one = createDate("2024/02/02 02:02:02"); //1706839322 + Date two = createDate("2023/03/03 03:03:03"); //1677812583 + Date three = createDate("2022/04/04 04:04:04"); //1649045044 + Date four = createDate("2020/02/02 02:22:22"); //1580610142 + Date five = createDate("2010/05/05 05:05:05"); //1273035905 + Date six = createDate("2000/05/05 05:05:05"); //957503105 + + Date[] arrayDates = new Date[]{one, two, three, four, five, six}; + long[] arrayStamps = new long[]{1706839322L, 1677812583L, 1649045044L, 1580610142L, 1273035905L, 957503105L}; + + for (int i = 0; i < arrayDates.length; i++) { + Assert.assertEquals("testCreate", GamUtilsEO.createUnixTimestamp(arrayDates[i]), arrayStamps[i]); + } + + } + + private static Date createDate(String date) { + + DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); + try { + return dateFormat.parse(date); + } catch (Exception e) { + + e.printStackTrace(); + return null; + } + } +} diff --git a/gamutils/src/test/java/com/genexus/gam/utils/test/resources/CryptographicHash.java b/gamutils/src/test/java/com/genexus/gam/utils/test/resources/CryptographicHash.java new file mode 100644 index 000000000..006097d1c --- /dev/null +++ b/gamutils/src/test/java/com/genexus/gam/utils/test/resources/CryptographicHash.java @@ -0,0 +1,38 @@ +package com.genexus.gam.utils.test.resources; + +import org.apache.commons.codec.CharEncoding; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class CryptographicHash { + private MessageDigest alg = null; + + // private HashAlgorithm alg; + public CryptographicHash(String algorithm) { + if (algorithm.equals("SHA512")) + algorithm = "SHA-512"; + // Supports algorithm = {MD2, MD5, SHA-1, SHA-256, SHA-384, SHA-512} + try { + alg = MessageDigest.getInstance(algorithm); // step 2 + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + } + + public String ComputeHash(String data) { + try { + if (alg == null) alg = MessageDigest.getInstance("SHA-512"); + alg.update(data.getBytes(CharEncoding.UTF_8)); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + + byte[] bin = alg.digest(); + org.apache.commons.codec.binary.Base64 base = new org.apache.commons.codec.binary.Base64(100000); + return base.encodeToString(bin).trim(); + } +} diff --git a/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.cer b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.cer new file mode 100644 index 000000000..bd788b5de Binary files /dev/null and b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.cer differ diff --git a/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.crt b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.crt new file mode 100644 index 000000000..bd788b5de Binary files /dev/null and b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.crt differ diff --git a/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.jks b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.jks new file mode 100644 index 000000000..54cc6fb26 Binary files /dev/null and b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.jks differ diff --git a/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.key b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.key new file mode 100644 index 000000000..d9abc2f04 --- /dev/null +++ b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.key @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEATCCAumgAwIBAgIJAIAqvKHZ+gFhMA0GCSqGSIb3DQEBCwUAMIGWMQswCQYD +VQQGEwJVWTETMBEGA1UECAwKTW9udGV2aWRlbzETMBEGA1UEBwwKTW9udGV2aWRl +bzEQMA4GA1UECgwHR2VuZVh1czERMA8GA1UECwwIU2VjdXJpdHkxEjAQBgNVBAMM +CXNncmFtcG9uZTEkMCIGCSqGSIb3DQEJARYVc2dyYW1wb25lQGdlbmV4dXMuY29t +MB4XDTIwMDcwODE4NTcxN1oXDTI1MDcwNzE4NTcxN1owgZYxCzAJBgNVBAYTAlVZ +MRMwEQYDVQQIDApNb250ZXZpZGVvMRMwEQYDVQQHDApNb250ZXZpZGVvMRAwDgYD +VQQKDAdHZW5lWHVzMREwDwYDVQQLDAhTZWN1cml0eTESMBAGA1UEAwwJc2dyYW1w +b25lMSQwIgYJKoZIhvcNAQkBFhVzZ3JhbXBvbmVAZ2VuZXh1cy5jb20wggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1zgaU+Wh63p9DNWoAy64252EvZjN4 +9AY3x0QCnAa8JO9Pk7znQwrxEFUKgZzv0GHEYW7+X+uyJr7BW4TA6fuJJ8agE/bm +ZRZyjdJjoue0FML6fbmCZ9Tsxpxe4pzispyWQ8jYT4Kl4I3fdZNUSn4XSidnDKBI +SeC05mrcchDKhInpiYDJ481lsB4JTEti3S4Xy/ToKwY4t6attw6z5QDhBc+Yro+Y +UqruliOAKqcfybe9k07jwMCvFVM1hrYYJ7hwHDSFo3MKwZ0y2gw0w6SgVBxLFo+K +YP3q63b5wVhD8lzaSh+8UcyiHM2/yjEej7EnRFzdclTSNXRFNaiLnEVdAgMBAAGj +UDBOMB0GA1UdDgQWBBQtQAWJRWNr/OswPSAdwCQh0Eei/DAfBgNVHSMEGDAWgBQt +QAWJRWNr/OswPSAdwCQh0Eei/DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUA +A4IBAQCjHe3JbNKv0Ywc1zlLacUNWcjLbmzvnjs8Wq5oxtf5wG5PUlhLSYZ9MPhu +f95PlibnrO/xVY292P5lo4NKhS7VOonpbPQ/PrCMO84Pz1LGfM/wCWQIowh6VHq1 +8PiZka9zbwl6So0tgClKkFSRk4wpKrWX3+M3+Y+D0brd8sEtA6dXeYHDtqV0YgjK +dZIIOx0vDT4alCoVQrQ1yAIq5INT3cSLgJezIhEadDv3Tc7bMxMFeL+81qHm9Z/9 +/KE6Z+JB0ZEOkF/2NSQJd+Z7MBR8CxOdTQis3ltMoXDatNkjZ2Env40sw4NICB8Y +YhsWMIarew5uNT+RS28YHNlbmogh +-----END CERTIFICATE----- diff --git a/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.p12 b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.p12 new file mode 100644 index 000000000..54cc6fb26 Binary files /dev/null and b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.p12 differ diff --git a/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.pem b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.pem new file mode 100644 index 000000000..d9abc2f04 --- /dev/null +++ b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEATCCAumgAwIBAgIJAIAqvKHZ+gFhMA0GCSqGSIb3DQEBCwUAMIGWMQswCQYD +VQQGEwJVWTETMBEGA1UECAwKTW9udGV2aWRlbzETMBEGA1UEBwwKTW9udGV2aWRl +bzEQMA4GA1UECgwHR2VuZVh1czERMA8GA1UECwwIU2VjdXJpdHkxEjAQBgNVBAMM +CXNncmFtcG9uZTEkMCIGCSqGSIb3DQEJARYVc2dyYW1wb25lQGdlbmV4dXMuY29t +MB4XDTIwMDcwODE4NTcxN1oXDTI1MDcwNzE4NTcxN1owgZYxCzAJBgNVBAYTAlVZ +MRMwEQYDVQQIDApNb250ZXZpZGVvMRMwEQYDVQQHDApNb250ZXZpZGVvMRAwDgYD +VQQKDAdHZW5lWHVzMREwDwYDVQQLDAhTZWN1cml0eTESMBAGA1UEAwwJc2dyYW1w +b25lMSQwIgYJKoZIhvcNAQkBFhVzZ3JhbXBvbmVAZ2VuZXh1cy5jb20wggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1zgaU+Wh63p9DNWoAy64252EvZjN4 +9AY3x0QCnAa8JO9Pk7znQwrxEFUKgZzv0GHEYW7+X+uyJr7BW4TA6fuJJ8agE/bm +ZRZyjdJjoue0FML6fbmCZ9Tsxpxe4pzispyWQ8jYT4Kl4I3fdZNUSn4XSidnDKBI +SeC05mrcchDKhInpiYDJ481lsB4JTEti3S4Xy/ToKwY4t6attw6z5QDhBc+Yro+Y +UqruliOAKqcfybe9k07jwMCvFVM1hrYYJ7hwHDSFo3MKwZ0y2gw0w6SgVBxLFo+K +YP3q63b5wVhD8lzaSh+8UcyiHM2/yjEej7EnRFzdclTSNXRFNaiLnEVdAgMBAAGj +UDBOMB0GA1UdDgQWBBQtQAWJRWNr/OswPSAdwCQh0Eei/DAfBgNVHSMEGDAWgBQt +QAWJRWNr/OswPSAdwCQh0Eei/DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUA +A4IBAQCjHe3JbNKv0Ywc1zlLacUNWcjLbmzvnjs8Wq5oxtf5wG5PUlhLSYZ9MPhu +f95PlibnrO/xVY292P5lo4NKhS7VOonpbPQ/PrCMO84Pz1LGfM/wCWQIowh6VHq1 +8PiZka9zbwl6So0tgClKkFSRk4wpKrWX3+M3+Y+D0brd8sEtA6dXeYHDtqV0YgjK +dZIIOx0vDT4alCoVQrQ1yAIq5INT3cSLgJezIhEadDv3Tc7bMxMFeL+81qHm9Z/9 +/KE6Z+JB0ZEOkF/2NSQJd+Z7MBR8CxOdTQis3ltMoXDatNkjZ2Env40sw4NICB8Y +YhsWMIarew5uNT+RS28YHNlbmogh +-----END CERTIFICATE----- diff --git a/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.pfx b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.pfx new file mode 100644 index 000000000..54cc6fb26 Binary files /dev/null and b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.pfx differ diff --git a/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.pkcs12 b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.pkcs12 new file mode 100644 index 000000000..54cc6fb26 Binary files /dev/null and b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_cert.pkcs12 differ diff --git a/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_key.pem b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_key.pem new file mode 100644 index 000000000..35a78f26f --- /dev/null +++ b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256_key.pem @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI5Xxbb/ckzykCAggA +MBQGCCqGSIb3DQMHBAiLR0U2DKvbeQSCBMg5y4x5chPFsx7N081HcFhNVV2xwvIx +vbRcBeBUhiVaJUuJfRSXzwm16oqV7bZj91IkbePXGwkhHPeBoyy0yrNgsfFJ+t05 +lQU7vKxSFCiTdijGVbp2nxntOb1yRPkc2pA0iSSED00QNHpjDgcpywAKPDmRJSyM +mWDmrYzQYLTzyFgmcec53B27DXfHoGHBGwq1lfdKGzlphJPDxd+w+2VuAl33Zuq4 +oriu69l4CHcg64vJAa/Y9BkW3HiAytKaEBHHAuE8W2mHYfW4DwZlBur+lo+aUiNV +4RFOH1xdej/k4feMXsOnYak93gZIbWyr/8MljbQJLNlE+NkheNzAyTT2a9P04JOv +GUqv45EO9PIdDBemJxCiK4wmeoS/KhJ/zyictOsc1W659lzdCpCIecu0t/1La5b2 +/MWIkdDmqvE4CSNEmGNykLejKKjvGwk9hTsnpckoo44eNjSiElcItd6d0nZr8o9Q +h8kK8GF0bRjYYqiXoCO5WCO6h6IcJEVmx58/Y07yQ504Djih5eU5TrbPfVcUNcYy +fWbcai0/X6SSEJXOuvHtx6s+7Y8f3Uo9mh3UI2we4C7zPABvlGTDMRliamcUc4AD +8BCahJZuOTN1mkYae+G1IpFW3E12z7+C9bQ6tuundntPuc+GJvEw/Clss6dZ5b3w ++Gf+5Kc9PlPu/UxGTUFntN5AB2GGheERtw6UFG+Wf34aHg8uYPXnOYXK1ssnhXlI +TxbKSUBiirR/LwoXVapepQdLXfVxirW/QVB42dcH0mjtkgSy5k7teaKZV3eZQhid +Ja9tGq6cMEngzd7BNWA+zzrjn/Jj5DbnnQhYuBr+w82qfiVqc6VYxTCcs2KJK1lU +LuwXfygNcksvw+7w0tLqUWNsY1M1ZtmnSWiAW76G6ARugmTevYa0WXLAo/N74c96 +ORW0n8ZSQa7zhROJgefbXsuG4brTRnQMAJc+SzH09BRU8m3xeo8bygUGrN/aDyQy +DZ37pcWwFXlLRRshYoRqCW7v8TDVnCMSM9aujGnlSyK/l/88BCJuiLziHWPiJgvk +1x68JoEN/fxLh7O3qTYpUiyfVN8LHB6tqqJe97p12BAzo1HX5WSoq2a/oYl4Hqa2 +VfCwiI11NrUpv1EIKfa2yspRk4JVz38dT+KbxB5mz0MgEL8aq/9cJ4ISersTK2UU +ZJQPgPvJzbnXU4Vh0ON4P7f724eZvTxzGiiJZAIWrqiR7TyEd4J65r3FbBU1xpLU +tTgrvv38EkA9+qoAG9CjukGTn1k0T8jNC3vxkn4f2nIMn1n9YbvsiVsjzq1UKzRt +nxHHzMOfKnt092HsO8dtXwZadrpYcoZk9qVx9xbA1RO9rv9CSqLszWThUOT6xXmG +3F2ff9jXJjHtAog70TCrU2/bXZmZCmuHG78aLtSUSyNSgCc+Lp9CBQmc0QHrqjCS +7qwHXhdriOElj0fDwuFOk9pXUfc4t4YDWQGxY7+YWnS7RXCye9YI90U/8TyhLBOs +JZY2DDlOskC0NoIkqHfb2jyHMtW1BzmQIQ0tfKTxTzyg/dOZKJuIWhvpi9LTHDCI ++Er7OrZ0vf91c5FraxPaXatdw4laiNMak9fgWNsp44jIxYHi0zVQLkjQKDuKVoZo +Vzk= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256d_key.key b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256d_key.key new file mode 100644 index 000000000..e41d6f252 --- /dev/null +++ b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256d_key.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAtc4GlPloet6fQzVqAMuuNudhL2YzePQGN8dEApwGvCTvT5O8 +50MK8RBVCoGc79BhxGFu/l/rsia+wVuEwOn7iSfGoBP25mUWco3SY6LntBTC+n25 +gmfU7MacXuKc4rKclkPI2E+CpeCN33WTVEp+F0onZwygSEngtOZq3HIQyoSJ6YmA +yePNZbAeCUxLYt0uF8v06CsGOLemrbcOs+UA4QXPmK6PmFKq7pYjgCqnH8m3vZNO +48DArxVTNYa2GCe4cBw0haNzCsGdMtoMNMOkoFQcSxaPimD96ut2+cFYQ/Jc2kof +vFHMohzNv8oxHo+xJ0Rc3XJU0jV0RTWoi5xFXQIDAQABAoIBAQCT/Go7JXE4YrI8 +4OOyVhkvM9RV4tkPIYNWL+taPGr3BxGNMvLXRClJ5EN00+BNDNAoLC9O/AE8+HDZ +r4c2CL/o+umhL98P10UYZfzVgasdWLEFeQVh8ubM/TYXvlp55W20muSHvuDX6RtS +w7/zItfUWVYNeaeWcBxq5Awj+O1WCoBV8OdWT8av5dyCHquD8rngvxD0gKeHvano +u6fOZN4tsolrB4GWh8B4A08dh4Ktcv/vxOfhzcS9jpT6+Zc5TdriD2fUYsdiw4dB +a4rghI61xbEyR8OC0ytOK5HhpXnTLcOv2zk8YmxW4HrkLWNV8xvzNPsZT9p4gWwB +AOGer07BAoGBAOpOy0rIems7JNFvfnROqW3Nx4rDqCiO+q14t/IBEHQrhdODireO +lrbUIeK9fnbHjuDoI17UTEtPuR5sdoxV4zyXsTIC37b628GlaoPVh3pyi/6o+Ypg +p+JFWmuCaNVRxWiGPtW/or/ulmP5XhcRp7QJIvbuD0aIEa1K1i0R/ZHlAoGBAMai +4rIDB0BnKSQxybSNjfh1ICwzdOuT7SpRD/aOlgcAG/OvyJ1Wk6NoJhL5rJTUA2B9 +es3U3M1jB55wcdQdNdiqVA/reQliLzGXbE5zEQ41oXYJucpTfTU/PbeV/Bj76N+l +o7lwCllo3EaHc0YKOe8/WnIQsjCoRlOcgzsYfI4ZAoGAMeq7cKEpQ3MEMwI3xHuF +qPjwC+YHCyz6xr4zIgGMCdPD3P3nLZfZD/Y9idqo+JEnJU8PSgKchmbjn1GoJ9mc +YapHe8oU8xyaeLTO7mstQ67nmEdTcmGJIrF3w/Oydc/H6K7A8DS1bYJc08uqeeuu ++LIBmu24n2QZr/uDiXKNvOkCgYEAuHRlwxxgzYN+hufdANc6cPCC8cjO2DxDzjn8 +ct6xnsqRKlegGctdyi5avOAxTPscL6wWL7FtYSSG3LBaY7jEWfDBow7tFLOiU5Dj +uG3N9r4Cs5QQfTvOV3Xkn+idc63p8FTmlrreQWzIsI3zk6THa84O9UFf1yNMOzRq +AKSadXECgYBEXyOgHyU2irRKX197nUdQd862tWONqaVNk7xiOSdv3CLRDaMkZrci +mA6diNN+vz3ut57iM7N9Yuqv4Qq/U79wBqElSrjy6Qlr+cVTvwT4RrOXcMfNZ1KN +Pz/h9l/Hce4Oez0GOLn+BffhCF3gHWz6nwfBgVu0V9xQ8eTaQfh4fw== +-----END RSA PRIVATE KEY----- diff --git a/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256d_key.pem b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256d_key.pem new file mode 100644 index 000000000..e41d6f252 --- /dev/null +++ b/gamutils/src/test/resources/dummycerts/RSA_sha256_2048/sha256d_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAtc4GlPloet6fQzVqAMuuNudhL2YzePQGN8dEApwGvCTvT5O8 +50MK8RBVCoGc79BhxGFu/l/rsia+wVuEwOn7iSfGoBP25mUWco3SY6LntBTC+n25 +gmfU7MacXuKc4rKclkPI2E+CpeCN33WTVEp+F0onZwygSEngtOZq3HIQyoSJ6YmA +yePNZbAeCUxLYt0uF8v06CsGOLemrbcOs+UA4QXPmK6PmFKq7pYjgCqnH8m3vZNO +48DArxVTNYa2GCe4cBw0haNzCsGdMtoMNMOkoFQcSxaPimD96ut2+cFYQ/Jc2kof +vFHMohzNv8oxHo+xJ0Rc3XJU0jV0RTWoi5xFXQIDAQABAoIBAQCT/Go7JXE4YrI8 +4OOyVhkvM9RV4tkPIYNWL+taPGr3BxGNMvLXRClJ5EN00+BNDNAoLC9O/AE8+HDZ +r4c2CL/o+umhL98P10UYZfzVgasdWLEFeQVh8ubM/TYXvlp55W20muSHvuDX6RtS +w7/zItfUWVYNeaeWcBxq5Awj+O1WCoBV8OdWT8av5dyCHquD8rngvxD0gKeHvano +u6fOZN4tsolrB4GWh8B4A08dh4Ktcv/vxOfhzcS9jpT6+Zc5TdriD2fUYsdiw4dB +a4rghI61xbEyR8OC0ytOK5HhpXnTLcOv2zk8YmxW4HrkLWNV8xvzNPsZT9p4gWwB +AOGer07BAoGBAOpOy0rIems7JNFvfnROqW3Nx4rDqCiO+q14t/IBEHQrhdODireO +lrbUIeK9fnbHjuDoI17UTEtPuR5sdoxV4zyXsTIC37b628GlaoPVh3pyi/6o+Ypg +p+JFWmuCaNVRxWiGPtW/or/ulmP5XhcRp7QJIvbuD0aIEa1K1i0R/ZHlAoGBAMai +4rIDB0BnKSQxybSNjfh1ICwzdOuT7SpRD/aOlgcAG/OvyJ1Wk6NoJhL5rJTUA2B9 +es3U3M1jB55wcdQdNdiqVA/reQliLzGXbE5zEQ41oXYJucpTfTU/PbeV/Bj76N+l +o7lwCllo3EaHc0YKOe8/WnIQsjCoRlOcgzsYfI4ZAoGAMeq7cKEpQ3MEMwI3xHuF +qPjwC+YHCyz6xr4zIgGMCdPD3P3nLZfZD/Y9idqo+JEnJU8PSgKchmbjn1GoJ9mc +YapHe8oU8xyaeLTO7mstQ67nmEdTcmGJIrF3w/Oydc/H6K7A8DS1bYJc08uqeeuu ++LIBmu24n2QZr/uDiXKNvOkCgYEAuHRlwxxgzYN+hufdANc6cPCC8cjO2DxDzjn8 +ct6xnsqRKlegGctdyi5avOAxTPscL6wWL7FtYSSG3LBaY7jEWfDBow7tFLOiU5Dj +uG3N9r4Cs5QQfTvOV3Xkn+idc63p8FTmlrreQWzIsI3zk6THa84O9UFf1yNMOzRq +AKSadXECgYBEXyOgHyU2irRKX197nUdQd862tWONqaVNk7xiOSdv3CLRDaMkZrci +mA6diNN+vz3ut57iM7N9Yuqv4Qq/U79wBqElSrjy6Qlr+cVTvwT4RrOXcMfNZ1KN +Pz/h9l/Hce4Oez0GOLn+BffhCF3gHWz6nwfBgVu0V9xQ8eTaQfh4fw== +-----END RSA PRIVATE KEY----- diff --git a/pom.xml b/pom.xml index 011604a21..52b063d45 100644 --- a/pom.xml +++ b/pom.xml @@ -111,6 +111,7 @@ gxcloudstorage-tests gxobservability gxcloudstorage-awss3-v2 + gamutils