diff --git a/java-spiffe-core/src/main/java/io/spiffe/svid/jwtsvid/JwtSvid.java b/java-spiffe-core/src/main/java/io/spiffe/svid/jwtsvid/JwtSvid.java index 46b19b52..210363a1 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/svid/jwtsvid/JwtSvid.java +++ b/java-spiffe-core/src/main/java/io/spiffe/svid/jwtsvid/JwtSvid.java @@ -276,7 +276,18 @@ public Date getExpiry() { return new Date(expiry.getTime()); } + /** + * Returns a copy of the issued at date time of the JWT SVID. + * + * @return a copy of the issued at date time of the JWT SVID, or {@code null} if the token + * does not have an 'iat' claim + */ public Date getIssuedAt() { + // the 'iat' claim is optional in a JWT-SVID + if (issuedAt == null) { + return null; + } + // defensive copy to prevent exposing a mutable object return new Date(issuedAt.getTime()); } diff --git a/java-spiffe-core/src/main/java/io/spiffe/workloadapi/CachedJwtSource.java b/java-spiffe-core/src/main/java/io/spiffe/workloadapi/CachedJwtSource.java index bd39915a..e5ddbd3c 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/workloadapi/CachedJwtSource.java +++ b/java-spiffe-core/src/main/java/io/spiffe/workloadapi/CachedJwtSource.java @@ -270,13 +270,13 @@ private static Set getAudienceSet(String audience, String[] extraAudienc } private boolean isTokenPastHalfLifetime(JwtSvid jwtSvid) { - Object issuedAtClaim = jwtSvid.getClaims().get("iat"); - if (!(issuedAtClaim instanceof Date)) { + Date issuedAt = jwtSvid.getIssuedAt(); + if (issuedAt == null) { return true; } long expiryTime = jwtSvid.getExpiry().getTime(); - long issuedAtTime = ((Date) issuedAtClaim).getTime(); + long issuedAtTime = issuedAt.getTime(); long halfLifeTime = expiryTime - (expiryTime - issuedAtTime) / 2; return clock.instant().toEpochMilli() > halfLifeTime; diff --git a/java-spiffe-core/src/test/java/io/spiffe/svid/jwtsvid/JwtSvidParseInsecureTest.java b/java-spiffe-core/src/test/java/io/spiffe/svid/jwtsvid/JwtSvidParseInsecureTest.java index 25b72ea7..a23c9edc 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/svid/jwtsvid/JwtSvidParseInsecureTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/svid/jwtsvid/JwtSvidParseInsecureTest.java @@ -15,6 +15,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.security.KeyPair; +import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.Map; @@ -23,6 +24,7 @@ import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; class JwtSvidParseInsecureTest { @@ -96,6 +98,40 @@ void testParseInsecure_nullAudience_throwsNullPointerException() throws JwtSvidE } } + @Test + void testParseInsecure_missingIssuedAt_getIssuedAtReturnsNull() throws JwtSvidException { + KeyPair key1 = TestUtils.generateECKeyPair(Curve.P_521); + SpiffeId spiffeId = TrustDomain.parse("test.domain").newSpiffeId("host"); + Set audience = Collections.singleton("audience"); + Date expiration = new Date(System.currentTimeMillis() + 3600000); + JWTClaimsSet claims = TestUtils.buildJWTClaimSet(audience, spiffeId.toString(), expiration); + + JwtSvid jwtSvid = JwtSvid.parseInsecure(TestUtils.generateToken(claims, key1, "authority1"), audience); + + assertNull(jwtSvid.getIssuedAt()); + } + + @Test + void testParseInsecure_withIssuedAt_getIssuedAtReturnsCopy() throws JwtSvidException { + KeyPair key1 = TestUtils.generateECKeyPair(Curve.P_521); + SpiffeId spiffeId = TrustDomain.parse("test.domain").newSpiffeId("host"); + Set audience = Collections.singleton("audience"); + Date issuedAt = new Date((System.currentTimeMillis() / 1000) * 1000); + Date expiration = new Date(issuedAt.getTime() + 3600000); + JWTClaimsSet claims = new JWTClaimsSet.Builder() + .subject(spiffeId.toString()) + .audience(new ArrayList<>(audience)) + .issueTime(issuedAt) + .expirationTime(expiration) + .build(); + + JwtSvid jwtSvid = JwtSvid.parseInsecure(TestUtils.generateToken(claims, key1, "authority1"), audience); + + assertEquals(issuedAt, jwtSvid.getIssuedAt()); + jwtSvid.getIssuedAt().setTime(0); + assertEquals(issuedAt, jwtSvid.getIssuedAt()); + } + static Stream provideSuccessScenarios() { KeyPair key1 = TestUtils.generateECKeyPair(Curve.P_521); KeyPair key2 = TestUtils.generateECKeyPair(Curve.P_521);