I have a quarkus app which does not generate jwt tokens itself but possesses a secret key of HS256-signed tokens (qwertyuiopasdfghjklzxcvbnm123456
). I need to verify tokens of the incoming network requests, but for every request I get the error:
io.smallrye.jwt.auth.principal.ParseException: SRJWT07000: Failed to verify a token
...
Caused by: org.jose4j.jwt.consumer.InvalidJwtSignatureException: JWT rejected due to invalid signature. Additional details: [[9] Invalid JWS Signature: JsonWebSignature{"typ":"JWT","alg":"HS256"}->eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NjczODI2NzIsImV4cCI6MTY5ODkxODY3MiwiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.5vBHzbTKjLnAkAIYuA3c50nWV--o9jIWV2i0GZI-aw4]
My application.properties
config:
smallrye.jwt.verify.key-format=JWK
smallrye.jwt.verify.key.location=JWTSecret.jwk
smallrye.jwt.verify.algorithm=HS256
quarkus.native.resources.includes=JWTSecret.jwk
JWTSecret.jwk
{
"kty": "oct",
"k": "qwertyuiopasdfghjklzxcvbnm123456",
"alg": "HS256"
}
I tried to verify the signature of the token with jwt.io using secret key above (and it verified the signature just fine), so my guess there's something wrong with my JWK file or application.properties
configuration. I also tried RS256 verification algorithm (with public/private pem keys) and it worked fine, but unfortunately I need it to work with HS256.
Below the code, but it should be ok since it works fine with other verification algorithms.
package co.ogram.domain
import org.eclipse.microprofile.jwt.JsonWebToken
import javax.annotation.security.RolesAllowed
import javax.enterprise.inject.Default
import javax.inject.Inject
import javax.ws.rs.*
import javax.ws.rs.core.Context
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.SecurityContext
@Path("/secured")
class TokenSecuredResource {
@Inject
@field:Default
var jwt: JsonWebToken? = null
@GET
@Path("/roles-allowed")
@RolesAllowed("Admin")
@Produces(MediaType.TEXT_PLAIN)
fun helloRolesAllowed(@Context ctx: SecurityContext): String? {
return getResponseString(ctx!!)
}
private fun getResponseString(ctx: SecurityContext): String {
val name: String
name = if (ctx.userPrincipal == null) {
"anonymous"
} else if (ctx.userPrincipal.name != jwt!!.name) {
throw InternalServerErrorException("Principal and JsonWebToken names do not match")
} else {
ctx.userPrincipal.name
}
val type = jwt!!.getClaim<Int>("type")
return String.format(
"hello + %s,"
+ " isHttps: %s,"
+ " authScheme: %s,"
+ " type: %s,"
+ " hasJWT: %s",
name, ctx.isSecure, ctx.authenticationScheme, type, hasJwt()
)
}
private fun hasJwt(): Boolean {
return jwt!!.claimNames != null
}
}
The jose4j package does the correct verification given the JWK as an input.
Your JWT is signed with the actual octets of jwk.k
("qwertyuiopasdfghjklzxcvbnm123456"
).
In reality you should base64url decode the k
to get a buffer to use as the HS256 secret to sign. This will align with what the jose4j package does (which is correct).