diff --git a/core/build.gradle.kts b/core/build.gradle.kts index f61b8d5964..69eb5d23d7 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -28,6 +28,8 @@ dependencies { implementation(libs.reactor.core) implementation(libs.jakarta.xml.bind.api) implementation(libs.java.stellar.sdk) + implementation(libs.jaxb.runtime) + implementation(libs.jackson.annotations) testImplementation(libs.servlet.api) testImplementation(libs.slf4j.api) diff --git a/core/src/main/java/org/stellar/anchor/sep10/JwtService.java b/core/src/main/java/org/stellar/anchor/sep10/JwtService.java index a0ae61df36..46eefbc132 100644 --- a/core/src/main/java/org/stellar/anchor/sep10/JwtService.java +++ b/core/src/main/java/org/stellar/anchor/sep10/JwtService.java @@ -44,21 +44,25 @@ public String encode(JwtToken token) { builder.addClaims(Map.of(REQUESTED_ACCOUNT, token.requestedAccount)); } - return builder.signWith(SignatureAlgorithm.HS256, jwtKey).compact(); + return builder.signWith(KeyUtil.toSecretKeySpecOrNull(jwtKey), Jwts.SIG.HS256).compact(); } @SuppressWarnings("rawtypes") public JwtToken decode(String cipher) { - JwtParser jwtParser = Jwts.parser(); - jwtParser.setSigningKey(jwtKey); + + JwtParser jwtParser = + Jwts.parser() + .json(JwtsGsonDeserializer.newInstance()) + .verifyWith(KeyUtil.toSecretKeySpecOrNull(jwtKey)) + .build(); Jwt jwt = jwtParser.parse(cipher); Claims claims = (Claims) jwt.getBody(); Object requestedAccount = claims.get(REQUESTED_ACCOUNT); return JwtToken.of( (String) claims.get("iss"), (String) claims.get("sub"), - Long.valueOf((Integer) claims.get("iat")), - Long.valueOf((Integer) claims.get("exp")), + (Long) claims.get("iat"), + (Long) claims.get("exp"), (String) claims.get("jti"), (String) claims.get("client_domain"), requestedAccount == null ? null : (String) requestedAccount); diff --git a/core/src/main/java/org/stellar/anchor/sep10/JwtsGsonDeserializer.java b/core/src/main/java/org/stellar/anchor/sep10/JwtsGsonDeserializer.java new file mode 100644 index 0000000000..b06bee67cc --- /dev/null +++ b/core/src/main/java/org/stellar/anchor/sep10/JwtsGsonDeserializer.java @@ -0,0 +1,25 @@ +package org.stellar.anchor.sep10; + +import com.google.gson.Gson; +import io.jsonwebtoken.io.DeserializationException; +import io.jsonwebtoken.io.Deserializer; +import java.io.Reader; +import java.util.Map; + +public class JwtsGsonDeserializer implements Deserializer> { + private static final Gson gson = new Gson(); + + public static JwtsGsonDeserializer newInstance() { + return new JwtsGsonDeserializer(); + } + + @Override + public Map deserialize(byte[] bytes) throws DeserializationException { + return gson.fromJson(new String(bytes), Map.class); + } + + @Override + public Map deserialize(Reader reader) throws DeserializationException { + return gson.fromJson(reader, Map.class); + } +} diff --git a/core/src/main/java/org/stellar/anchor/sep10/KeyUtil.java b/core/src/main/java/org/stellar/anchor/sep10/KeyUtil.java new file mode 100644 index 0000000000..3521c3a1d0 --- /dev/null +++ b/core/src/main/java/org/stellar/anchor/sep10/KeyUtil.java @@ -0,0 +1,14 @@ +package org.stellar.anchor.sep10; + +import io.jsonwebtoken.security.Keys; +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import javax.crypto.SecretKey; + +public final class KeyUtil { + public static SecretKey toSecretKeySpecOrNull(String secret) { + return Objects.toString(secret, "").isEmpty() + ? null + : Keys.hmacShaKeyFor(((secret.getBytes(StandardCharsets.UTF_8)))); + } +} diff --git a/core/src/test/kotlin/org/stellar/anchor/Constants.kt b/core/src/test/kotlin/org/stellar/anchor/Constants.kt index 6fa4bfe56f..dbf06c61fc 100644 --- a/core/src/test/kotlin/org/stellar/anchor/Constants.kt +++ b/core/src/test/kotlin/org/stellar/anchor/Constants.kt @@ -9,7 +9,7 @@ class Constants { const val TEST_CLIENT_DOMAIN = "test.client.stellar.org" const val TEST_NETWORK_PASS_PHRASE = "Test SDF Network ; September 2015" const val TEST_HOST_URL = "https://test.stellar.org" - const val TEST_JWT_SECRET = "jwt_secret" + const val TEST_JWT_SECRET = "secret_secret_secret_secret_secret_secret_secret_secret" const val TEST_ASSET = "USDC" const val TEST_ASSET_ISSUER_ACCOUNT_ID = "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" diff --git a/core/src/test/kotlin/org/stellar/anchor/filter/Sep10TokenFilterTest.kt b/core/src/test/kotlin/org/stellar/anchor/filter/Sep10TokenFilterTest.kt index ed1e6940dd..6f9629c059 100644 --- a/core/src/test/kotlin/org/stellar/anchor/filter/Sep10TokenFilterTest.kt +++ b/core/src/test/kotlin/org/stellar/anchor/filter/Sep10TokenFilterTest.kt @@ -12,6 +12,7 @@ import org.junit.jupiter.api.* import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource +import org.stellar.anchor.Constants.Companion.TEST_JWT_SECRET import org.stellar.anchor.config.AppConfig import org.stellar.anchor.config.Sep10Config import org.stellar.anchor.filter.BaseTokenFilter.APPLICATION_JSON_VALUE @@ -37,7 +38,7 @@ internal class Sep10TokenFilterTest { @BeforeEach fun setup() { this.appConfig = mockk(relaxed = true) - every { appConfig.jwtSecretKey } returns "secret" + every { appConfig.jwtSecretKey } returns TEST_JWT_SECRET this.jwtService = JwtService(appConfig) this.sep10Config = mockk(relaxed = true) this.sep10TokenFilter = Sep10TokenFilter(sep10Config, jwtService) diff --git a/core/src/test/kotlin/org/stellar/anchor/sep10/JwtServiceTest.kt b/core/src/test/kotlin/org/stellar/anchor/sep10/JwtServiceTest.kt index e2331b8cb5..5a96891c82 100644 --- a/core/src/test/kotlin/org/stellar/anchor/sep10/JwtServiceTest.kt +++ b/core/src/test/kotlin/org/stellar/anchor/sep10/JwtServiceTest.kt @@ -6,6 +6,7 @@ import io.mockk.mockk import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows +import org.stellar.anchor.Constants.Companion.TEST_JWT_SECRET import org.stellar.anchor.config.AppConfig internal class JwtServiceTest { @@ -22,7 +23,7 @@ internal class JwtServiceTest { @Test fun testcodec() { val appConfig = mockk() - every { appConfig.jwtSecretKey } returns "jwt_secret" + every { appConfig.jwtSecretKey } returns TEST_JWT_SECRET val jwtService = JwtService(appConfig) val token = @@ -46,7 +47,7 @@ internal class JwtServiceTest { @Test fun testcodecRequestAccount() { val appConfig = mockk() - every { appConfig.jwtSecretKey } returns "jwt_secret" + every { appConfig.jwtSecretKey } returns TEST_JWT_SECRET val jwtService = JwtService(appConfig) val token = @@ -80,7 +81,7 @@ internal class JwtServiceTest { @Test fun testBadCipher() { val appConfig = mockk() - every { appConfig.jwtSecretKey } returns "jwt_secret" + every { appConfig.jwtSecretKey } returns TEST_JWT_SECRET val jwtService = JwtService(appConfig) diff --git a/settings.gradle.kts b/settings.gradle.kts index fb9d9eaf75..2a922326e9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,20 +12,23 @@ dependencyResolutionManagement { alias("httpclient").to("org.apache.httpcomponents:httpclient:4.5.13") alias("java.stellar.sdk").to("com.github.stellar:java-stellar-sdk:0.42.0") - alias("jakarta.xml.bind.api").to("jakarta.xml.bind:jakarta.xml.bind-api:2.3.3") + alias("jakarta.xml.bind.api").to("jakarta.xml.bind:jakarta.xml.bind-api:4.0.2") + alias("jakarta.xml.bind").to("jakarta.xml.bind:jakarta.xml.bind:4.0.2") - alias("jjwt").to("io.jsonwebtoken:jjwt:0.9.1") + alias("jjwt").to("io.jsonwebtoken:jjwt:0.12.6") alias("log4j.core").to("org.apache.logging.log4j:log4j-core:2.17.1") alias("lombok").to("org.projectlombok:lombok:1.18.22") alias("okhttp3").to("com.squareup.okhttp3:okhttp:4.9.3") alias("okhttp3.mockserver").to("com.squareup.okhttp3:mockwebserver:4.9.3") alias("reactor.core").to("io.projectreactor:reactor-core:3.4.14") alias("reactor.netty").to("io.projectreactor.netty:reactor-netty:1.0.15") - alias("servlet.api").to("jakarta.servlet:jakarta.servlet-api:6.1.0") + alias("servlet.api").to("jakarta.servlet:jakarta.servlet-api:6.0.0") alias("sqlite.jdbc").to("org.xerial:sqlite-jdbc:3.16.1") alias("slf4j.api").to("org.slf4j:slf4j-api:1.7.35") alias("slf4j.log4j12").to("org.slf4j:slf4j-log4j12:1.7.33") alias("toml4j").to("com.moandjiezana.toml:toml4j:0.7.2") + alias("jaxb-runtime").to("org.glassfish.jaxb:jaxb-runtime:4.0.2") + alias("jackson-annotations").to("com.fasterxml.jackson.core:jackson-annotations:2.15.4") } } }