https://kotlinlang.org logo
#multiplatform
Title
# multiplatform
s

Stefan Oltmann

12/07/2023, 3:51 PM
I need to validate the signature of JSON Web Tokens (JWT). I looked at the KMM-Jwt-Parser library, which efficiently decodes the Base64-encoded payload and parses the JSON. This library lacks the functionality to verify the RSA256 signature of my JWTs against a public key. I am uncertain about the process of implementing this verification and how to utilize an appropriate KMM library for the task. I believe it might be achievable using the cryptography-kotlin library available at https://github.com/whyoleg/cryptography-kotlin, but I am unsure about the specific steps involved. Could someone provide a sample code demonstrating how to verify a JWT token? My target platforms include JVM, Android, and Native (iOS & macOS). I got a client app, not a server. I only see server side capabilities in Ktor for JWT. Are there client side features I miss?
1
o

Oleg Yukhnevich

12/07/2023, 4:24 PM
May be this could help for the start (it's JVM only, but it should be rather easy to use
cryptogrpahy-kotlin
instead of JDK APIs): https://github.com/PhilJay/JWT/blob/master/src/main/java/com/philjay/jwt/JWT.kt overall to verify you just need to split header+payload and signature and then call one function. F.e. if you already know the algorithm (after parsing header), you can do something like this:
Copy code
val token: String = ...
val headerAndPayload = token.substringBeforeLast(".")
val signature = token.substringAfterLast(".")
// here goes cryptography-kotlin code
val result: Boolean = CryptographyProvider.Default.get(RSA.PKCS1) // or other algorithm, depending on what is in token
  .publicKeyDecoder(SHA256) // or other algorithm, depending on what is in token
  .decodeFrom(KEY_FORMAT, KEY_DATA) // depending on public key format
  .signatureVerifier() // this will return SignatureVerifier which can be cached and used to verify any amount of signatures
  .verifySignature(headerAndPayload.encodeToByteArray(), signature.encodeToByteArray())
d

David Kanenwisher

12/07/2023, 4:25 PM
There are potentially a lot of pitfalls in rolling your own JWT verification code. Have you looked at using the various libraries provided by OpenID? It might be a pain to build a different library for each target, but you wouldn't have to worry as much about missing an important security hole covered by the libraries.
s

Stefan Oltmann

12/07/2023, 4:28 PM
I wonder why no one build and released a JWT library for this so far. If I google it I saw a lot of people ending up building something with expect/actual, but no one sharing the solutions.
Thanks, Oleg, I will try that. 🙏🏻
o

Oleg Yukhnevich

12/07/2023, 4:46 PM
I wonder why no one build and released a JWT library for this so far
you can be first 🙂 Also, I was thinking of including this functionality into
cryptography-kotlin
someday, though, there a lot more other foundational things need to be done 🙂
There are potentially a lot of pitfalls in rolling your own JWT verification code
As far as I remember, JWT/JOSE/etc have rather simple specs but of course I can not know everything upfront But what you've send is for OAuth, which is a little different thing, and yes, it's much harder to make it right Overall, all time, if there is a possibility, it's better to use something already existing via expect/actual - this is BTW the underlying principle which l used in cryptography-kotlin 🙂
d

David Kanenwisher

12/07/2023, 4:57 PM
That's a good point, if it's just a JWT without OAuth then it might be a bit less dangerous to roll your own, but if you're using OAuth you have quite a bit of extra stuff to worry about like different types of clients, expiration, refresh, claims, etc. a lot of which is handled pretty smoothly by existing libraries.
s

Stefan Oltmann

12/08/2023, 3:08 PM
@Oleg Yukhnevich Ok, I don't understand cryptography well enough to translate the JAVA API calls to your library. Can you show me how these calls must look like using your lib?
Copy code
val kf = KeyFactory.getInstance("RSA")

            val modulus = BigInteger(1, decoder.decode(n))
            val exponent = BigInteger(1, decoder.decode(e))
            return kf.generatePublic(RSAPublicKeySpec(modulus, exponent))
Copy code
val rsaSignature = Signature.getInstance(verifyAlgorithm)
                rsaSignature.initVerify(rsa)
                rsaSignature.update(header)
                rsaSignature.update(tokenDelimiter.code.toByte())
                rsaSignature.update(payload)
                rsaSignature.verify(tokenSignature)
My token has RS256 encryption. Is this supported on all platforms?
I studied https://whyoleg.github.io/cryptography-kotlin/examples/ , but this does not help me much. 😕
For example I can't specify "RSA" as this is only an interface and has these implementations.
2 Views