Skip to content

GamCryptography EO migration to GamUtils EO on Github #885

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
2d150fc
GamUtils + tests
sgrampone Jul 26, 2024
083bef0
Fix test
sgrampone Jul 26, 2024
77fab66
Fix quality issues and Log
sgrampone Aug 2, 2024
9cb8c02
New features, tests and some needed refactoring
sgrampone Aug 6, 2024
a2c7ae2
Load encrypted private keys + tests
sgrampone Aug 7, 2024
055badf
GamUtilsEO simplification and module refactor + tests
sgrampone Aug 15, 2024
63abd1d
Fix cast type error for jwk keys on gamutils
sgrampone Aug 15, 2024
c1be143
Add DynamicCall EO to GamUtils
sgrampone Aug 19, 2024
761a632
Merge branch 'master' into gamutils_eo
sgrampone Aug 29, 2024
842a761
Add sha256 and encoding features for PKCE implementation
sgrampone Aug 29, 2024
5322c50
Fix hash class
sgrampone Aug 29, 2024
09ce4c3
Refactor Encoding class name
sgrampone Aug 30, 2024
be96dd3
Merge branch 'master' into gamutils_eo
sgrampone Oct 7, 2024
2bc6616
Adding missing functions
sgrampone Jan 14, 2025
84b897d
Delete usless function
sgrampone Jan 15, 2025
038b14c
Merge branch 'master' into gamutils_eo
sgrampone Jan 16, 2025
75fcc40
Adding Base64Url encoding functions and fixing symmetric and asymmetric
sgrampone Jan 21, 2025
c7e5e15
Adding Base64Url encoding functions and fixing symmetric and asymmetr…
sgrampone Jan 21, 2025
788dff3
Fix JWTAlgorithm bug with RSA header
sgrampone Jan 21, 2025
594c264
Add Base64ToHexa function
sgrampone Feb 27, 2025
a2e0259
GamUtilsEO add functions for PKCE
sgrampone Feb 28, 2025
096fcb6
Merge branch 'master' into gamutils_eo
sgrampone Mar 12, 2025
f077408
Fix pkce functions
sgrampone Mar 26, 2025
5c3eb3c
Change Base64Url functions
sgrampone Mar 27, 2025
30563cc
Merge branch 'master' into gamutils_eo
sgrampone Mar 27, 2025
c8ba835
Fix key extension detection for azure OIDC discovery
sgrampone Apr 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
63 changes: 63 additions & 0 deletions gamutils/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.genexus</groupId>
<artifactId>parent</artifactId>
<version>${revision}${changelist}</version>
</parent>

<artifactId>gamutils</artifactId>
<name>GAM Utils EO</name>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>gxcommon</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.37.3</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.78.1</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>1.78.1</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<finalName>gamutils</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>

</project>
54 changes: 54 additions & 0 deletions gamutils/src/main/java/com/genexus/gam/GamUtilsEO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.genexus.gam;

import com.genexus.gam.utils.*;

public class GamUtilsEO {

/********EXTERNAL OBJECT PUBLIC METHODS - BEGIN ********/

//**HASH**//
public static String sha512(String plainText) {
return Hash.sha512(plainText);
}

//**RANDOM**//
public static String randomAlphanumeric(int length) {
return Random.randomAlphanumeric(length);
}

public static String randomNumeric(int length) {
return Random.randomNumeric(length);
}


//**JWK**//

public static String generateKeyPair() {
return Jwk.generateKeyPair();
}

public static String getPublicJwk(String jwkString) {
return Jwk.getPublic(jwkString);
}

public static boolean jwk_verifyJWT(String jwkString, String token) {
return Jwk.verifyJWT(jwkString, token);
}

public static String jwk_createJwt(String jwkString, String payload, String header) {
return Jwk.createJwt(jwkString, payload, header);
}

//**JWKS**//

public static boolean jwks_verifyJWT(String jwksString, String token, String kid) {
return Jwks.verifyJWT(jwksString, token, kid);
}

//**JWT**//
public static boolean verifyJWTWithFile(String path, String alias, String password, String token) {
return Jwt.verify(path, alias, password, token);
}

/********EXTERNAL OBJECT PUBLIC METHODS - END ********/
}
113 changes: 113 additions & 0 deletions gamutils/src/main/java/com/genexus/gam/utils/CertificateUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package com.genexus.gam.utils;

import com.genexus.diagnostics.core.ILogger;
import com.genexus.diagnostics.core.LogManager;
import org.apache.commons.io.FilenameUtils;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory;
import org.bouncycastle.openssl.PEMParser;

import java.io.*;
import java.security.KeyStore;
import java.security.cert.X509Certificate;

public enum CertificateUtil {

crt, cer, pfx, jks, pkcs12, p12, pem, key;

public static final ILogger logger = LogManager.getLogger(CertificateUtil.class);

public static CertificateUtil 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;
default:
logger.error("Invalid certificate file extension");
return null;
}
}

public static X509Certificate getCertificate(String path, String alias, String password) {
CertificateUtil ext = CertificateUtil.value(FilenameUtils.getExtension(path));
if (ext == null) {
logger.error("Error reading certificate path");
return null;
}
switch (ext) {
case crt:
case cer:
return loadFromDer(path);
case pfx:
case jks:
case pkcs12:
case p12:
return loadFromPkcs12(path, alias, password);
case pem:
case key:
return loadFromPkcs8(path);
default:
logger.error("Invalid certificate file extension");
return null;
}
}

private static X509Certificate loadFromPkcs8(String path) {
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) {
try (FileInputStream inStream = new FileInputStream(path)) {
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(inStream, password.toCharArray());
if (alias.equals("") || 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) {
try (FileInputStream inStream = new FileInputStream(path)) {
CertificateFactory cf = new CertificateFactory();
return (X509Certificate) cf.engineGenerateCertificate(inStream);
} catch (Exception e) {
logger.error("loadFromDer", e);
return null;
}
}
}
26 changes: 26 additions & 0 deletions gamutils/src/main/java/com/genexus/gam/utils/Hash.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.genexus.gam.utils;

import com.genexus.diagnostics.core.ILogger;
import com.genexus.diagnostics.core.LogManager;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.SHA512Digest;
import org.bouncycastle.util.encoders.Base64;

import java.nio.charset.StandardCharsets;

public class Hash {
public static final ILogger logger = LogManager.getLogger(Hash.class);

public static String sha512(String plainText) {
if (plainText.isEmpty()) {
logger.error("sha512 plainText is empty");
return "";
}
byte[] inputBytes = plainText.getBytes(StandardCharsets.UTF_8);
Digest alg = new SHA512Digest();
byte[] retValue = new byte[alg.getDigestSize()];
alg.update(inputBytes, 0, inputBytes.length);
alg.doFinal(retValue, 0);
return Base64.toBase64String(retValue);
}
}
87 changes: 87 additions & 0 deletions gamutils/src/main/java/com/genexus/gam/utils/Jwk.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.genexus.gam.utils;

import com.genexus.diagnostics.core.ILogger;
import com.genexus.diagnostics.core.LogManager;
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 java.util.UUID;

public class Jwk {

public static final ILogger 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 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 "";
}
}
}
34 changes: 34 additions & 0 deletions gamutils/src/main/java/com/genexus/gam/utils/Jwks.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.genexus.gam.utils;

import com.genexus.diagnostics.core.ILogger;
import com.genexus.diagnostics.core.LogManager;
import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.JWKSet;

public class Jwks {

public static final ILogger logger = LogManager.getLogger(Jwks.class);

public static boolean verifyJWT(String jwksString, String token, String kid) {
if (jwksString.isEmpty()) {
logger.error("verifyJWT jwksString parameter is empty");
return false;
}
if (token.isEmpty()) {
logger.error("verifyJWT token parameter is empty");
return false;
}
if (kid.isEmpty()) {
logger.error("verifyJWT kid parameter is empty");
return false;
}
try {
JWKSet set = JWKSet.parse(jwksString);
JWK jwk = set.getKeyByKeyId(kid);
return Jwt.verify(jwk.toRSAKey().toRSAPublicKey(), token);
} catch (Exception e) {
logger.error("verifyJWT", e);
return false;
}
}
}
Loading
Loading