Skip to content

Commit fbe0474

Browse files
authored
Improve JWT Signature logging for Troubleshooting Web Environments (#485)
1 parent b8f4cf1 commit fbe0474

6 files changed

Lines changed: 90 additions & 55 deletions

File tree

common/src/main/java/com/genexus/util/Encryption.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import com.genexus.common.interfaces.SpecificImplementation;
66
import java.nio.charset.StandardCharsets;
77

8+
import com.genexus.diagnostics.core.ILogger;
9+
import com.genexus.diagnostics.core.LogManager;
810
import org.apache.commons.codec.binary.Base64;
911
import org.bouncycastle.crypto.BlockCipher;
1012
import org.bouncycastle.crypto.BufferedBlockCipher;
@@ -23,6 +25,7 @@
2325

2426
public class Encryption
2527
{
28+
public static final ILogger logger = LogManager.getLogger(Encryption.class);
2629
public static String AJAX_ENCRYPTION_KEY = "GX_AJAX_KEY";
2730
public static String AJAX_ENCRYPTION_IV = "GX_AJAX_IV";
2831
public static String AJAX_SECURITY_TOKEN = "AJAX_SECURITY_TOKEN";
@@ -210,12 +213,12 @@ public static String decrypt64(String value, String key, boolean safeEncoding)
210213
}
211214
catch (InvalidKeyException e)
212215
{
213-
System.err.println(e);
216+
logger.error("decrypt64 error", e);
214217
throw new InvalidGXKeyException(e.getMessage());
215218
}
216219
catch(UnsupportedEncodingException e)
217220
{
218-
System.err.println(e);
221+
logger.error("decrypt64 error", e);
219222
throw new RuntimeException(e.getMessage());
220223
}
221224
catch (ArrayIndexOutOfBoundsException e)
@@ -406,7 +409,7 @@ public static String encryptRijndael(String plainText, String key) {
406409
try {
407410
outputBytes = aesCipher(inputBytes, true, key, GX_AJAX_PRIVATE_IV);
408411
} catch (DataLengthException | IllegalStateException | InvalidCipherTextException e) {
409-
e.printStackTrace();
412+
logger.error("encryptRijndael error", e);
410413
return "";
411414
}
412415
return Hex.toHexString(outputBytes);

gxexternalproviders/src/test/java/com/genexus/db/driver/TestExternalProvider.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.genexus.db.driver;
22

3+
import com.genexus.specific.java.Connect;
34
import org.junit.Assume;
45
import org.junit.Before;
56
import org.junit.Test;
@@ -37,6 +38,8 @@ public boolean supportsObjectAcls() {
3738
@Before
3839
public void beforeEachTestMethod() {
3940

41+
Connect.init();
42+
4043
boolean testEnabled = false;
4144
try {
4245
testEnabled = ExternalProviderHelper.getEnvironmentVariable(getProviderName() + "_TEST_ENABLED", false).equals("true");

java/src/main/java/com/genexus/security/web/SecureTokenHelper.java

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import java.util.Map;
44

5-
import org.apache.commons.lang.NotImplementedException;
5+
import com.fasterxml.jackson.core.JsonProcessingException;
66
import org.apache.commons.lang.StringUtils;
77

88
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -31,25 +31,29 @@ public static String sign(SecureToken token, SecurityMode mode, String secretKey
3131
String encoded = "";
3232
JWTSigner signer = new JWTSigner(secretKey);
3333
Map<String,Object> claims;
34-
try {
34+
35+
try {
3536
claims = new ObjectMapper().readValue(payload, Map.class);
36-
switch (mode)
37-
{
38-
case Sign:
37+
}
38+
catch (JsonProcessingException e) {
39+
logger.error("Signature Error - Failed to serialize payload", e);
40+
return "";
41+
}
42+
43+
switch (mode)
44+
{
45+
case Sign:
3946
try {
40-
encoded = signer.sign(claims);
47+
encoded = signer.sign(claims);
4148
} catch (Exception e) {
42-
logger.error("Sign Error", e);
49+
logger.error("Signature Error", e);
4350
}
44-
break;
45-
case SignEncrypt:
46-
case None:
47-
throw new NotImplementedException();
48-
}
49-
}
50-
catch (Exception e1) {
51-
logger.error("Sign Error", e1);
52-
}
51+
break;
52+
case SignEncrypt:
53+
case None:
54+
logger.warn(String.format("Signature Mode '%s' not implemented", mode));
55+
break;
56+
}
5357
return encoded;
5458
}
5559

java/src/main/java/com/genexus/security/web/WebSecurityHelper.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,5 @@ public static boolean verify(String pgmName, String issuer, String value, String
5656
}
5757
return ret;
5858
}
59-
60-
/*public static boolean verify(String pgmName, String issuer, String value, String jwtToken, WebSecureToken token)
61-
{
62-
boolean ok = SecureTokenHelper.Verify(jwtToken, token, GetSecretKey());
63-
return ok && !string.IsNullOrEmpty(pgmName) && token.ProgramName == pgmName && issuer == token.Issuer &&
64-
*/
59+
6560
}

java/src/main/java/com/genexus/security/web/jose/jwt/JWTSigner.java

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,15 @@
1919
import java.util.Map;
2020
import java.util.UUID;
2121

22+
import org.apache.commons.lang.StringUtils;
23+
import org.apache.logging.log4j.LogManager;
24+
import org.apache.logging.log4j.Logger;
25+
2226
import javax.crypto.Mac;
2327
import javax.crypto.spec.SecretKeySpec;
2428

2529
import org.apache.commons.codec.binary.Base64;
30+
import org.apache.commons.lang.Validate;
2631
import org.bouncycastle.jce.provider.BouncyCastleProvider;
2732

2833
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -47,6 +52,7 @@ public class JWTSigner {
4752

4853
private byte[] secret;
4954
private PrivateKey privateKey;
55+
private static Logger logger = LogManager.getLogger(JWTSigner.class);
5056

5157
// Default algorithm HMAC SHA-256 ("HS256")
5258
protected final static Algorithm DEFAULT_ALGORITHM = Algorithm.HS256;
@@ -55,7 +61,8 @@ public JWTSigner(final String secret) {
5561
this(secret.getBytes());
5662
}
5763

58-
public JWTSigner(final byte[] secret) {
64+
public JWTSigner(final byte[] secret) {
65+
Validate.notNull(secret);
5966
this.secret = secret;
6067
}
6168

@@ -78,7 +85,8 @@ public JWTSigner(final PrivateKey privateKey) {
7885
* the "options" parameter override claims in this map.
7986
* @param options Allow choosing the signing algorithm, and automatic setting of some registered claims.
8087
*/
81-
public String sign(final Map<String, Object> claims, final Options options) {
88+
public String sign(final Map<String, Object> claims, final Options options) {
89+
Validate.notNull(claims, "JWT claims cannot be null");
8290
final Algorithm algorithm = (options != null && options.algorithm != null) ? options.algorithm : DEFAULT_ALGORITHM;
8391
final List<String> segments = new ArrayList<String>();
8492
try {
@@ -87,6 +95,7 @@ public String sign(final Map<String, Object> claims, final Options options) {
8795
segments.add(encodedSignature(join(segments, "."), algorithm));
8896
return join(segments, ".");
8997
} catch (Exception e) {
98+
logger.error("JWT Sign error", e);
9099
throw new RuntimeException(e.getCause());
91100
}
92101
}
@@ -95,14 +104,17 @@ public String sign(final Map<String, Object> claims, final Options options) {
95104
* Generate a JSON Web Token using the default algorithm HMAC SHA-256 ("HS256")
96105
* and no claims automatically set.
97106
*/
98-
public String sign(final Map<String, Object> claims) {
107+
public String sign(final Map<String, Object> claims) {
108+
Validate.notNull(claims);
99109
return sign(claims, null);
100110
}
101111

102112
/**
103113
* Generate the header part of a JSON web token.
104114
*/
105-
private String encodedHeader(final Algorithm algorithm) throws UnsupportedEncodingException {
115+
private String encodedHeader(final Algorithm algorithm) throws UnsupportedEncodingException {
116+
Validate.notNull(algorithm);
117+
// create the header
106118
final ObjectNode header = JsonNodeFactory.instance.objectNode();
107119
header.put("typ", "JWT");
108120
header.put("alg", algorithm.name());
@@ -131,6 +143,8 @@ private String encodedPayload(final Map<String, Object> _claims, final Options o
131143
}
132144

133145
private void processPayloadOptions(final Map<String, Object> claims, final Options options) {
146+
Validate.notNull(claims);
147+
Validate.notNull(options);
134148
final long now = System.currentTimeMillis() / 1000l;
135149
if (options.expirySeconds != null)
136150
claims.put("exp", now + options.expirySeconds);
@@ -144,6 +158,8 @@ private void processPayloadOptions(final Map<String, Object> claims, final Optio
144158

145159
// consider cleanup
146160
private void enforceIntDate(final Map<String, Object> claims, final String claimName) {
161+
Validate.notNull(claims);
162+
Validate.notNull(claimName);
147163
final Object value = handleNullValue(claims, claimName);
148164
if (value == null)
149165
return;
@@ -226,6 +242,8 @@ private String checkStringOrURI(final Object value) {
226242
*/
227243
private String encodedSignature(final String signingInput, final Algorithm algorithm) throws NoSuchAlgorithmException, InvalidKeyException,
228244
NoSuchProviderException, SignatureException, JWTAlgorithmException {
245+
Validate.notNull(signingInput);
246+
Validate.notNull(algorithm);
229247
switch (algorithm) {
230248
case HS256:
231249
case HS384:
@@ -244,13 +262,17 @@ private String encodedSignature(final String signingInput, final Algorithm algor
244262
* Safe URL encode a byte array to a String
245263
*/
246264
private String base64UrlEncode(final byte[] str) {
265+
Validate.notNull(str);
247266
return new String(Base64.encodeBase64URLSafe(str));
248267
}
249268

250269
/**
251270
* Sign an input string using HMAC and return the encrypted bytes
252271
*/
253272
private static byte[] signHmac(final Algorithm algorithm, final String msg, final byte[] secret) throws NoSuchAlgorithmException, InvalidKeyException {
273+
Validate.notNull(algorithm);
274+
Validate.notNull(msg);
275+
Validate.notNull(secret);
254276
final Mac mac = Mac.getInstance(algorithm.getValue());
255277
mac.init(new SecretKeySpec(secret, algorithm.getValue()));
256278
return mac.doFinal(msg.getBytes());
@@ -261,15 +283,20 @@ private static byte[] signHmac(final Algorithm algorithm, final String msg, fina
261283
*/
262284
private static byte[] signRs(final Algorithm algorithm, final String msg, final PrivateKey privateKey) throws NoSuchProviderException,
263285
NoSuchAlgorithmException, InvalidKeyException, SignatureException {
286+
Validate.notNull(algorithm);
287+
Validate.notNull(msg);
288+
Validate.notNull(privateKey);
264289
final byte[] messageBytes = msg.getBytes();
265290
final Signature signature = Signature.getInstance(algorithm.getValue(), "BC");
266291
signature.initSign(privateKey);
267292
signature.update(messageBytes);
268293
return signature.sign();
269294
}
270295

271-
private String join(final List<String> input, final String separator) {
272-
return org.apache.commons.lang.StringUtils.join(input.iterator(), separator);
296+
private String join(final List<String> input, final String separator) {
297+
Validate.notNull(input);
298+
Validate.notNull(separator);
299+
return StringUtils.join(input.iterator(), separator);
273300
}
274301

275302
/**

java/src/main/java/com/genexus/webpanels/WebUtils.java

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,20 @@
44
import java.text.CharacterIterator;
55
import java.text.StringCharacterIterator;
66
import java.util.Date;
7+
import java.util.Locale;
78
import java.util.Set;
89

9-
import com.genexus.ModelContext;
10+
import com.genexus.*;
1011
import com.genexus.internet.HttpContext;
1112
import org.apache.commons.io.IOUtils;
1213
import org.apache.commons.io.input.BOMInputStream;
1314

14-
import com.genexus.CommonUtil;
15-
import com.genexus.GXutil;
16-
import com.genexus.PrivateUtilities;
1715
import com.genexus.diagnostics.core.ILogger;
1816
import com.genexus.diagnostics.core.LogManager;
1917
import com.genexus.xml.XMLReader;
18+
import org.apache.commons.lang.StringUtils;
19+
20+
import static com.genexus.util.Encryption.decrypt64;
2021

2122

2223
public class WebUtils
@@ -419,7 +420,7 @@ protected static Object[] parmsToObjectArray(com.genexus.ModelContext context, S
419420
public static String decryptParm(Object parm, String encryptionKey) {
420421
String value = parm.toString();
421422
try {
422-
if (!encryptionKey.equals("")) {
423+
if (!encryptionKey.isEmpty()) {
423424
String strValue = value.toString();
424425
strValue = com.genexus.util.Encryption.uridecrypt64(strValue,
425426
encryptionKey);
@@ -437,29 +438,31 @@ public static String decryptParm(Object parm, String encryptionKey) {
437438

438439
public static String parmsEncryptionKey(com.genexus.ModelContext context)
439440
{
440-
String GXKey = "";
441441
String keySourceType = com.genexus.Application.getClientPreferences().getUSE_ENCRYPTION();
442-
if (!keySourceType.equals("")) {
443-
GXKey = getEncryptionKey(context, keySourceType);
444-
}
445-
return GXKey;
442+
if (keySourceType.isEmpty()) {
443+
return "";
444+
}
445+
return getEncryptionKey(context, keySourceType);
446446
}
447447

448-
public static String getEncryptionKey(com.genexus.ModelContext context, String keySourceType){
449-
if (keySourceType.equals(""))
450-
{
451-
keySourceType = com.genexus.Application.getClientPreferences().getUSE_ENCRYPTION();
448+
public static String getEncryptionKey(com.genexus.ModelContext context, String keySourceType) {
449+
keySourceType = (keySourceType.isEmpty()) ? Application.getClientPreferences().getUSE_ENCRYPTION(): keySourceType;
450+
String encryptionKey;
451+
452+
switch (keySourceType.toUpperCase(Locale.ROOT)) {
453+
case "SESSION":
454+
encryptionKey = decrypt64(((HttpContext) context.getHttpContext()).getCookie("GX_SESSION_ID"), context.getServerKey());
455+
break;
456+
default:
457+
encryptionKey = context.getSiteKey();
458+
break;
452459
}
453-
String GXKey = "";
454-
if (keySourceType.equalsIgnoreCase("SESSION"))
455-
{
456-
GXKey = com.genexus.util.Encryption.decrypt64(((HttpContext) context.getHttpContext()).getCookie("GX_SESSION_ID"), context.getServerKey());
457-
}
458-
else
459-
{
460-
GXKey = context.getSiteKey();
461-
}
462-
return GXKey;
460+
461+
if (encryptionKey.isEmpty()) {
462+
logger.error(String.format("Encryption Key cannot be empty - Key Source: %s", keySourceType));
463+
}
464+
465+
return encryptionKey;
463466
}
464467

465468
private static final String gxApplicationClassesFileName = "GXApplicationClasses.txt";

0 commit comments

Comments
 (0)