Skip to content

Commit b126bde

Browse files
committed
More updates for MP JWT 2.1
- Update calculation of clock skew to have the right precision - Update to handle -1 clock skew for the JWT cache - Only parse the JWE header once
1 parent 22456ca commit b126bde

File tree

3 files changed

+111
-91
lines changed

3 files changed

+111
-91
lines changed

dev/com.ibm.ws.security.jwt/src/com/ibm/ws/security/jwt/internal/ConsumerUtil.java

Lines changed: 64 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import java.security.PublicKey;
1717
import java.security.interfaces.RSAPublicKey;
1818
import java.util.Arrays;
19-
import java.util.Base64;
2019
import java.util.Collections;
2120
import java.util.Date;
2221
import java.util.List;
@@ -44,7 +43,6 @@
4443
import com.ibm.websphere.security.jwt.JwtToken;
4544
import com.ibm.websphere.security.jwt.KeyException;
4645
import com.ibm.websphere.security.jwt.KeyStoreServiceException;
47-
import com.ibm.ws.ffdc.annotation.FFDCIgnore;
4846
import com.ibm.ws.kernel.productinfo.ProductInfo;
4947
import com.ibm.ws.security.common.crypto.KeyAlgorithmChecker;
5048
import com.ibm.ws.security.common.jwk.impl.JwKRetriever;
@@ -91,7 +89,7 @@ public JwtToken parseJwt(String jwtString, JwtConsumerConfig config, MpConfigPro
9189
JwtTokenConsumerImpl jwtToken = new JwtTokenConsumerImpl(jwtContext);
9290
checkForReusedJwt(jwtToken, config);
9391
if (!isJwtContextAlreadyCached) {
94-
cacheJwtContext(jwtString, jwtContext, config);
92+
cacheJwtContext(jwtString, jwtContext, config, properties);
9593
}
9694
return jwtToken;
9795
}
@@ -100,7 +98,7 @@ JwtContext parseJwtAndGetJwtContext(String jwtString, JwtConsumerConfig config,
10098
throws Exception {
10199
JwtContext jwtContext = parseJwtWithoutValidation(jwtString, config, mpConfigProps);
102100
if (config.isValidationRequired()) {
103-
validateJwtContext(jwtContext, jwtString, config, mpConfigProps);
101+
validateJwtContext(jwtContext, config, mpConfigProps);
104102
}
105103
return jwtContext;
106104
}
@@ -377,20 +375,33 @@ protected JwtContext parseJwtWithoutValidation(String jwtString, JwtConsumerConf
377375
new Object[] { config.getId(), jwtString });
378376
throw new InvalidTokenException(errorMsg);
379377
}
380-
checkJwtFormatAgainstConfigRequirements(jwtString, config, mpConfigProps);
381378
return parseNewJwtWithoutValidation(jwtString, config, mpConfigProps);
382379
}
383380

384381
void checkJwtFormatAgainstConfigRequirements(String jwtString, JwtConsumerConfig config,
385382
MpConfigProperties mpConfigProps) throws InvalidTokenException {
383+
JwtClaims jweHeaderParameters = null;
384+
boolean isJWE = JweHelper.isJwe(jwtString);
385+
if (isJWE) {
386+
jweHeaderParameters = JweHelper.getJweHeaderParams(jwtString);
387+
}
388+
checkJwtFormatAgainstConfigRequirements(jwtString, config, mpConfigProps, isJWE, jweHeaderParameters);
389+
}
390+
391+
private void checkJwtFormatAgainstConfigRequirements(String jwtString, JwtConsumerConfig config,
392+
MpConfigProperties mpConfigProps, boolean isJWE, JwtClaims jweHeaderParameters) throws InvalidTokenException {
386393
if (JweHelper.isJwsRequired(config, mpConfigProps) && !JweHelper.isJws(jwtString)) {
387394
String errorMsg = Tr.formatMessage(tc, "JWS_REQUIRED_BUT_TOKEN_NOT_JWS", new Object[] { config.getId() });
388395
throw new InvalidTokenException(errorMsg);
389396
}
390-
if (JweHelper.isJweRequired(config, mpConfigProps) && !JweHelper.isJwe(jwtString)) {
397+
if (JweHelper.isJweRequired(config, mpConfigProps) && !isJWE) {
391398
String errorMsg = Tr.formatMessage(tc, "JWE_REQUIRED_BUT_TOKEN_NOT_JWE", new Object[] { config.getId() });
392399
throw new InvalidTokenException(errorMsg);
393400
}
401+
402+
if (isJWE) {
403+
validateHeaders(config, mpConfigProps, jweHeaderParameters);
404+
}
394405
}
395406

396407
JwtContext getJwtContextFromCache(@Sensitive String jwtString, JwtConsumerConfig config) {
@@ -405,23 +416,28 @@ private synchronized void initializeCache() {
405416
}
406417
}
407418

408-
void cacheJwtContext(@Sensitive String jwtString, JwtContext jwtContext, JwtConsumerConfig config) {
419+
void cacheJwtContext(@Sensitive String jwtString, JwtContext jwtContext, JwtConsumerConfig config, MpConfigProperties mpConfigProps) {
409420
initializeCache();
410-
jwtCache.put(jwtString, config.getId(), jwtContext, config.getClockSkew());
421+
jwtCache.put(jwtString, config.getId(), jwtContext, getClockSkew(config, mpConfigProps));
411422
}
412423

413424
JwtContext parseNewJwtWithoutValidation(@Sensitive String jwtString, JwtConsumerConfig config,
414425
MpConfigProperties mpConfigProps) throws InvalidTokenException, InvalidJwtException {
415-
if (JweHelper.isJwe(jwtString)) {
416-
jwtString = JweHelper.extractJwsFromJweToken(jwtString, config, mpConfigProps);
426+
JwtClaims jweHeaderParameters = null;
427+
boolean isJWE = JweHelper.isJwe(jwtString);
428+
if (isJWE) {
429+
jweHeaderParameters = JweHelper.getJweHeaderParams(jwtString);
430+
}
431+
checkJwtFormatAgainstConfigRequirements(jwtString, config, mpConfigProps, isJWE, jweHeaderParameters);
432+
if (isJWE) {
433+
jwtString = JweHelper.extractJwsFromJweToken(jwtString, config, mpConfigProps, jweHeaderParameters);
417434
}
418435
JwtConsumerBuilder builder = initializeJwtConsumerBuilderWithoutValidation(config);
419436
JwtConsumer firstPassJwtConsumer = builder.build();
420437
return firstPassJwtConsumer.process(jwtString);
421438
}
422439

423-
protected void validateJwtContext(JwtContext jwtContext, String jwtString, JwtConsumerConfig config,
424-
MpConfigProperties mpConfigProps) throws Exception {
440+
protected void validateJwtContext(JwtContext jwtContext, JwtConsumerConfig config, MpConfigProperties mpConfigProps) throws Exception {
425441
Key key = getSigningKey(config, jwtContext, mpConfigProps);
426442
JwtClaims jwtClaims = jwtContext.getJwtClaims();
427443

@@ -431,51 +447,36 @@ protected void validateJwtContext(JwtContext jwtContext, String jwtString, JwtCo
431447

432448
validateClaims(jwtClaims, jwtContext, config, mpConfigProps);
433449
validateSignatureAlgorithmWithKey(config, key, mpConfigProps);
434-
validateHeaders(jwtContext, jwtString, config, mpConfigProps);
435450

436451
JwtConsumerBuilder consumerBuilder = initializeJwtConsumerBuilderWithValidation(config, jwtClaims, key);
437452
JwtConsumer jwtConsumer = consumerBuilder.build();
438453
processJwtContextWithConsumer(jwtConsumer, jwtContext);
439454
}
440455

441-
@FFDCIgnore(Exception.class)
442-
private void validateHeaders(JwtContext jwtContext, String jwtString, JwtConsumerConfig config,
443-
MpConfigProperties mpConfigProps) throws Exception {
444-
445-
if (isRunningBetaMode() && JweHelper.isJwe(jwtString)) {
446-
try {
447-
String keyManagementKeyAlgorithm = null;
448-
// Get keyManagementKeyAlgorithm from server.xml
449-
keyManagementKeyAlgorithm = config.getKeyManagementKeyAlgorithm();
450-
/**
451-
* If keyManagementKeyAlgorithm from server.xml is null, then take the value of
452-
* keyManagementKeyAlgorithm from mpConfigProps
453-
*/
454-
if (keyManagementKeyAlgorithm == null) {
455-
String value = mpConfigProps.get(MpConfigProperties.DECRYPT_KEY_ALGORITHM);
456-
if (value != null) {
457-
keyManagementKeyAlgorithm = value;
458-
}
459-
}
460-
/**
461-
* If keyManagementKeyAlgorithm is not null, do the following check if
462-
* keyManagementKeyAlgorithm is null (i.e. MP JWT < 2.1) skip the check
463-
*/
464-
if (keyManagementKeyAlgorithm != null) {
465-
String[] parts = jwtString.split(("\\."));
466-
String headerParametersAsJsonString = new String(Base64.getDecoder().decode(parts[0]), "UTF-8");
467-
JwtClaims headerParameters = JwtClaims.parse(headerParametersAsJsonString);
468-
String tokenAlg = (String) headerParameters.getClaimValue("alg");
469-
470-
validateKeyManagementKeyAlgorithm(keyManagementKeyAlgorithm, tokenAlg);
471-
}
472-
} catch (Exception e) {
473-
if (tc.isDebugEnabled()) {
474-
Tr.debug(tc, "Exception validating headers: ", e);
456+
private void validateHeaders(JwtConsumerConfig config, MpConfigProperties mpConfigProps, JwtClaims jweHeaderParameters) throws InvalidTokenException {
457+
458+
if (isRunningBetaMode()) {
459+
String keyManagementKeyAlgorithm = null;
460+
// Get keyManagementKeyAlgorithm from server.xml
461+
keyManagementKeyAlgorithm = config.getKeyManagementKeyAlgorithm();
462+
/**
463+
* If keyManagementKeyAlgorithm from server.xml is null, then take the value of
464+
* keyManagementKeyAlgorithm from mpConfigProps
465+
*/
466+
if (keyManagementKeyAlgorithm == null) {
467+
String value = mpConfigProps.get(MpConfigProperties.DECRYPT_KEY_ALGORITHM);
468+
if (value != null) {
469+
keyManagementKeyAlgorithm = value;
475470
}
476-
throw e;
477471
}
478-
472+
/**
473+
* If keyManagementKeyAlgorithm is not null, do the following check if
474+
* keyManagementKeyAlgorithm is null (i.e. MP JWT < 2.1) skip the check
475+
*/
476+
if (keyManagementKeyAlgorithm != null) {
477+
String tokenAlg = (String) jweHeaderParameters.getClaimValue("alg");
478+
validateKeyManagementKeyAlgorithm(keyManagementKeyAlgorithm, tokenAlg);
479+
}
479480
}
480481
}
481482

@@ -523,14 +524,7 @@ JwtConsumerBuilder initializeJwtConsumerBuilderWithValidation(JwtConsumerConfig
523524
return builder;
524525
}
525526

526-
void validateClaims(JwtClaims jwtClaims, JwtContext jwtContext, JwtConsumerConfig config,
527-
MpConfigProperties mpConfigProps)
528-
throws MalformedClaimException, InvalidClaimException, InvalidTokenException {
529-
String issuer = config.getIssuer();
530-
if (issuer == null) {
531-
issuer = mpConfigProps.get(MpConfigProperties.ISSUER);
532-
}
533-
527+
private long getClockSkew(JwtConsumerConfig config, MpConfigProperties mpConfigProps) {
534528
long clockSkew = config.getClockSkew();
535529
/**
536530
* If clockSkew from server.xml is negative, then take the value of clock_skew
@@ -539,11 +533,23 @@ void validateClaims(JwtClaims jwtClaims, JwtContext jwtContext, JwtConsumerConfi
539533
if (clockSkew < 0) {
540534
String value = mpConfigProps.get(MpConfigProperties.CLOCK_SKEW);
541535
if (value != null) {
542-
clockSkew = Long.valueOf(value);
536+
clockSkew = Long.valueOf(value) * 1000;
543537
} else {
544538
clockSkew = 0;
545539
}
546540
}
541+
return clockSkew;
542+
}
543+
544+
void validateClaims(JwtClaims jwtClaims, JwtContext jwtContext, JwtConsumerConfig config,
545+
MpConfigProperties mpConfigProps)
546+
throws MalformedClaimException, InvalidClaimException, InvalidTokenException {
547+
String issuer = config.getIssuer();
548+
if (issuer == null) {
549+
issuer = mpConfigProps.get(MpConfigProperties.ISSUER);
550+
}
551+
552+
long clockSkew = getClockSkew(config, mpConfigProps);
547553

548554
long tokenAgeInMilliSeconds = 0;
549555
// Take tokenAge value from server.xml

0 commit comments

Comments
 (0)