|
15 | 15 | import com.migcomponents.migbase64.Base64; |
16 | 16 | import io.jsonwebtoken.Jwts; |
17 | 17 | import io.jsonwebtoken.SignatureAlgorithm; |
| 18 | +import io.jsonwebtoken.io.DeserializationException; |
| 19 | +import io.jsonwebtoken.io.Deserializer; |
18 | 20 | import lombok.SneakyThrows; |
19 | 21 | import lombok.extern.slf4j.Slf4j; |
20 | 22 | import org.bouncycastle.asn1.ASN1Encodable; |
|
24 | 26 | import org.bouncycastle.util.io.pem.PemWriter; |
25 | 27 | import org.junit.jupiter.api.Test; |
26 | 28 |
|
| 29 | +import java.io.ByteArrayInputStream; |
27 | 30 | import java.io.FileInputStream; |
| 31 | +import java.io.IOException; |
| 32 | +import java.io.InputStreamReader; |
| 33 | +import java.io.Reader; |
28 | 34 | import java.io.StringWriter; |
| 35 | +import java.lang.reflect.Method; |
29 | 36 | import java.nio.charset.StandardCharsets; |
30 | 37 | import java.security.GeneralSecurityException; |
31 | 38 | import java.security.Key; |
|
36 | 43 | import java.security.Signature; |
37 | 44 | import java.security.cert.Certificate; |
38 | 45 | import java.util.Date; |
| 46 | +import java.util.Map; |
39 | 47 |
|
40 | 48 | /** |
41 | 49 | * Test class for the {@link JwtHelper}. |
@@ -206,6 +214,90 @@ public void testValidateJwtWithNumericSubject() { |
206 | 214 | assertEquals("../avatars/static/50/default.png", userClaim.getAvatarSmallUrl()); |
207 | 215 | } |
208 | 216 |
|
| 217 | + @Test |
| 218 | + @SneakyThrows |
| 219 | + public void testCreateSignedJwt() { |
| 220 | + final KeyStore keyStore = getKeyStoreFromFile(); |
| 221 | + final PrivateKey privateKey = (PrivateKey) keyStore.getKey(CERT_ALIAS, CERT_PASSWORD.toCharArray()); |
| 222 | + |
| 223 | + String jwt = JwtHelper.createSignedJwt("alice", JwtHelper.JWT_EXPIRATION_MILLIS, privateKey); |
| 224 | + |
| 225 | + assertNotNull(jwt); |
| 226 | + // jjwt compact form must have exactly three dot-separated segments |
| 227 | + assertEquals(3, jwt.split("\\.").length); |
| 228 | + } |
| 229 | + |
| 230 | + @Test |
| 231 | + void loadUnknownFormatPrivateKey() { |
| 232 | + String unknownFormat = "-----BEGIN SOMETHING ELSE-----\nabcdef\n-----END SOMETHING ELSE-----"; |
| 233 | + GeneralSecurityException ex = assertThrows( |
| 234 | + GeneralSecurityException.class, () -> JwtHelper.parseRsaPrivateKey(unknownFormat)); |
| 235 | + assertTrue(ex.getMessage().contains("Header not recognized")); |
| 236 | + } |
| 237 | + |
| 238 | + @Test |
| 239 | + public void testIsSkdEnabledMissingClaim() { |
| 240 | + // JWT parses successfully but has no canUseSimplifiedKeyDelivery claim → final return false |
| 241 | + assertFalse(JwtHelper.isSkdEnabled(JWT)); |
| 242 | + } |
| 243 | + |
| 244 | + @Test |
| 245 | + public void testIsSkdEnabledNonBooleanClaim() { |
| 246 | + // canUseSimplifiedKeyDelivery present but as a string → falls through to final return false |
| 247 | + // payload: {"sub":"123","canUseSimplifiedKeyDelivery":"yes"} |
| 248 | + String jwtWithStringSkd = |
| 249 | + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." |
| 250 | + + "eyJzdWIiOiIxMjMiLCJjYW5Vc2VTaW1wbGlmaWVkS2V5RGVsaXZlcnkiOiJ5ZXMifQ." |
| 251 | + + "signature"; |
| 252 | + assertFalse(JwtHelper.isSkdEnabled(jwtWithStringSkd)); |
| 253 | + } |
| 254 | + |
| 255 | + @Test |
| 256 | + public void testIsSkdEnabledTriggersCatch() { |
| 257 | + // Too few segments → extractDecodedClaims throws → catch returns false |
| 258 | + assertFalse(JwtHelper.isSkdEnabled("only.two")); |
| 259 | + } |
| 260 | + |
| 261 | + @Test |
| 262 | + public void testJwtHelperConstructor() { |
| 263 | + // Cover the implicit default constructor of the utility class |
| 264 | + assertNotNull(new JwtHelper()); |
| 265 | + } |
| 266 | + |
| 267 | + @Test |
| 268 | + @SneakyThrows |
| 269 | + public void testNormalizeSubjectDeserializerByteArray() { |
| 270 | + // The byte[] overload is unreachable through jjwt's public API (Reader is used), |
| 271 | + // so exercise it directly via reflection to cover both the success and the |
| 272 | + // IOException catch path. |
| 273 | + Method method = JwtHelper.class.getDeclaredMethod("normalizeSubjectDeserializer"); |
| 274 | + method.setAccessible(true); |
| 275 | + @SuppressWarnings("unchecked") |
| 276 | + Deserializer<Map<String, ?>> deserializer = (Deserializer<Map<String, ?>>) method.invoke(null); |
| 277 | + |
| 278 | + Map<String, ?> result = deserializer.deserialize( |
| 279 | + "{\"sub\":12345,\"user\":{\"id\":42}}".getBytes(StandardCharsets.UTF_8)); |
| 280 | + assertEquals("12345", result.get("sub")); |
| 281 | + |
| 282 | + assertThrows(DeserializationException.class, |
| 283 | + () -> deserializer.deserialize("not-json".getBytes(StandardCharsets.UTF_8))); |
| 284 | + } |
| 285 | + |
| 286 | + @Test |
| 287 | + @SneakyThrows |
| 288 | + public void testNormalizeSubjectDeserializerReaderIOException() { |
| 289 | + // Exercise the IOException catch in the Reader overload by feeding malformed JSON |
| 290 | + Method method = JwtHelper.class.getDeclaredMethod("normalizeSubjectDeserializer"); |
| 291 | + method.setAccessible(true); |
| 292 | + @SuppressWarnings("unchecked") |
| 293 | + Deserializer<Map<String, ?>> deserializer = (Deserializer<Map<String, ?>>) method.invoke(null); |
| 294 | + |
| 295 | + try (Reader reader = new InputStreamReader( |
| 296 | + new ByteArrayInputStream("not-json".getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8)) { |
| 297 | + assertThrows(DeserializationException.class, () -> deserializer.deserialize(reader)); |
| 298 | + } |
| 299 | + } |
| 300 | + |
209 | 301 | @SneakyThrows |
210 | 302 | private static String generatePkcs8RsaPrivateKey() { |
211 | 303 | final KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); |
|
0 commit comments