Score:0

Is it secure to sign a JWT using RS256?

km flag

I have implemented an authentication scheme using JWT with assymetric keys (RS256). The idea is that (assuming some microservice-based acrhitecture) the authentication service will sign all JWTs with a secret/private key (PRIVATE_KEY) and distribute the public key (PUBLIC_KEY) to all other microservices, so they can verify whether 1) the JWT was issued by the authentication service and 2) the JWT was not tampered in any way.

I have generated a private and public key pair with:

$ ssh-keygen -t rsa -b 4096 -m PEM -f jwtRS256.key
$ openssl rsa -in jwtRS256.key -pubout -outform PEM -out jwtRS256.key.pub

I then created a signed JWT using python-jose (RS256 with PRIVATE_KEY). Below is the JWT and the key pair.

JWT:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1YjM5ZDQ3Mi1iNmU4LTQwOTktOWNkYi1hMWVmYTMzMWEwM2UiLCJpYXQiOjE2NjY1MjA3NjF9.DbQRuAvZdO7rJGyWRHBtdpeiDaN2Dc3LFi2aiCX3Z6YXpGBoLVyOofO05SSPerdgTm_rKrzvtXGlBg3ZojjdpUPqR0SdPjUhYnfvViIpZWWCC34H_MI99jacH8LtINL-BCJ-t-xdwojhS35uljxiHLmnN2ekdbVFbfULS5f-F2a7xyD7axZXjziwxOJUQC9cp71qc6Va5G7wWIAca1Xq6x2Gq5bsUeQ2QjoLLUmrPZ7KRPklGgjZcPkNqDLk7fe6bgfVWQkpapMuIr6zDv6yN90M2SzKHIhCvsUa4s01dOIx10v39NpHd32-buiGcDgLRMM98OM6us6MGSncrIo3ngj4TrDlK7URHapHnKPdmb_x1If7yjMwQyC8it72QSQD1m0BvNoULHILaZSuStiZLWsosq8tZB9KTR-PpnIW32jLKqzTzuPGcH7hnBjBW9YMhxa42RkNDHh7NUa2FRvQ3oFv7auZKer3BaAW1ExzQfKJ-BKOr93hWCw9W76ulOmpzqreaUUhp7nUdcCItV-OWQJnp1nwgiej59ZgjKsxURbjdZ2UtQzPOn8ra506TQs3FcBZDAqO-bIA7TCQvSu0I_rJLM1nE85znSB_UQZZQVP0-52vxSs30Oc8l6mkTFxgRTSMREgOIsjWY_OXVQNEHjCByzaGcTiUIacxxHMjxTM

PRIVATE_KEY:

-----BEGIN RSA PRIVATE KEY-----
MIIJJwIBAAKCAgEA8XrqQgXpK1JA4iXzpx8pnmnV2KvFHZ5Jsz48Fy8Z923VvloS
sNF0KDS6kmOIfpx8jnXeAOmt6ctW0KM5WEFu8fvgBvyB1Jvqgnr7kLDd+rhgoKwt
5eCFAtfchNxq8HKle5P/4d2k/Kf7Swn89IBRpDZUiL3GHivyEIlXcATg0j8P5ekv
JKMcLZqdak+2fxQkNLGXLXSydFS7capio/7aerj+U3XBvz2lknWdaKADOjPju06v
gwP8/1Ru7aqxFVoQABA1as2Ue5v8Vo+wKkJqbB3HSaWn1bWhsFYSEjrVmnGXUVWz
wQHckMYCdxwPofy7ufB/7Rg8PfGg/j3HEolN8BOL8PgnZIkDRV5f+RFfupMGn1T9
JEkDDAxuE2+Ac7v5ZFXZH0IoWPzwtpS49O+KdzkkGtTd75q6+3XCvX8lViYDOQvc
6Ik3zN9TYTtUWRNGCRdNkBZVxM3W6Mz7TpAIY5YEu7xC7T6kZgmQfmFwnF/an/6q
2Gy30eZzLo2iLSUYU0t/hVWCHFBSlly/RApmRBuppC2liIYeXbmuDA46ts1Ra9+N
/D0smPHfoqcutX42W8PlYNaMoQAd4/QS96KQZNXUoJVsb7OgRi0OFqWijPer7nm3
sNfsu/JB2GTRNI9/S3EjbA6lsGOjqxYWMyUEzPRjDUoziVxH2lDGWqSBhM8CAwEA
AQKCAgAPfnB7bf+o/O0W1ZKNnY1BEc9byKGsJdTawFqArk//NTfqr8LVP4sxbTiv
Xd2LKiU/ysZEzrmO77IRTvfF9uTUd/HG4Pq/loV0e8maXg7QIHZquMF1J3PBW/JV
QANIjEKb7EIVzu/gGjMgfHKTiYwzehzwbSTCGNW+Q+GtWVLHiq5NdSnGMwUC1BHX
mWe/PZ6ZFu/5RayRlEI2p9UOarK//xCqcDrPN3hhLlS8OtAaMuwgv4q5YE8iXtuD
OlmEYjP2nROgV7J4P+jv8OY/v+UuLLb1vcBIERBfzRX5v3anIzSvTk1rS+BIFbxg
whcqsJmm84xxvQVYgCFxTqtwQAkVB3H0jm2B8AoyFKzNtxMJvBBEYwwBivWBfnts
+di+sMBIth8uO0QhMUCHaPjvUuRlbXTmaAy+VjRc7XITCC9pAr6Cok4z1VCgCv+N
kgFrJ3uz633ecs4pyu2ki6t5HPSBI8GPJCLi/2pUgn5R0S+sTwBPbsBdF6Dz6A19
gqGpoHE8JIpJOf1GpciNiDSZrB2qMU79j7H3+BN2Ab+MSX7MHrGwOEndfRZIhEWs
gtne2S7q0vdrGg1sRRHmIvY76Jfzh6bRkVJkzJKI9NZBHmPCfapxjBqeGbcmsyZS
ScCgCD0IZ0FpEeIJbeDyEhhnfouc/Z22NZMkgva0q/mVxz6boQKCAQEA/koW/jw0
bQ+5exo1k84suufjpr7jTRG1xMsRAVry5fgRyrvvPvyzDjA/s9AkY1G+ClymK/ZS
C6N4FqlTmJC6mKVVqea6n6CUOesUCofJxKapDab13oCks4dltZZrUDmwngE72dgq
M5PTb/lfgmnESqsYc0RC0E5phsssigszUp6uF5d/ifVt8r1vJlz07K9nSDQ+jfsM
chpxxWaF+CVFHVbR927qbkzlYrdVwUoMSVQRpvFTvca75CEkR5H79tQf4vwxxNe/
Sh9VPk0qTTD7mwa/GZfoALTSxGj9ZC1wSVHz7MGZm8GjV7SPTlH7m5olmVaOSDIH
dZNH+Zkri6jfvwKCAQEA8xrEOAMnATeKvSsyPQ0HuDgIIG4RzeogKIgxxzsVOyDU
f09UwbPelqzxRwt/hvErhi7G+PPjwbOKUs0pt60RxYb1iT03vlaLhs82/w+ULvlF
LYWb8UvdncjgtKVP1TrgfblqOtAeZ9NkleMUbhCOAwF6VHBGGi/X91hII+gNW7T3
tW7DmhWfQ4OKKDDhywTUuwjbJBu94vsWzQn4152n5shR04ntZ6KpuUZTzm5jvUTq
HNEaGK4h1mQrzhZEvLq9ZKtt5JzW83+COvZg5Cufw7gLe+AXei23+EV/3FxOimtm
edx5qwP+cMoJJgpgJZEod81f0eOrx7RKGHReG1Ge8QKCAQBiwomtkdpWpS3HZsV6
My/iI1+iCi8jZoZu+OMQ4K7HrBHU6CqiDujH0OtcvbD0NfIV+ie7mT4CMSnZu0ex
UDx4PnZHt0mx517KI8ez00sEqimsGLUTBmlxJFvXK6VgEhfLNfV1xOOXBomuym5S
qxtGWK71TYSZfGq1pEEXGASFyQUaOoeZIA3kobgCcUXaisEVJN1KrT8HLcgT/552
Yps2Ktr/Oz+nOQw5y7RtjoG5FQKKEN4SLZYZotmBRmxST85WjNYTeitb8VFdEgEk
26pMalFb8khzxca564DGIHOktkSDJimveYQj8wdZRrPume6SYDCAT5XgDyR8BwYH
hr3nAoIBAFziTTZZff2pCwlcB8XrxJdOUUySwZq7aa4S23IT0PjWglpiJTAdWV6Y
lVdfvUoBu295T+yuwsavopogaWAUcEXYWtgk27eNuaG4anrpqOSJuTS7Z+m54uL2
bWSYBPijlkJzJXNzMSraSgQ6zwN+r8PWPLcmbqJyxUFsKaQOdYwkiMB5oaKtwDw7
df4FL0AyiFUt/Qy+zL580SlmNf2r7wruXtVETcDbFZ4EkOC+rL0UJ577Bc2IOsV/
YH1NZNX3Q4XDOimvAzHl9gwxNrP3NwPAviKGbHHyJehmJYQ8gIXCo1TMOHBvBjhx
+SzfqfywiZW8km/PXULgT68oRwyJEwECggEAQy/D7X+hu94bdGk5E00xzTf4BHkM
qAbTz/YikPMldYGpV3oXvI79vcGN7mqQE/7+FqKRRDFbeXzSXc5hf+k3I587BWGf
w0lb+psuv+4GUnOvK0DsqPCUSFRjBfYQBfhysh2QTU1tQlRzvABMFCv/ZbAhHOK9
Fq6GW0ffl/0fGiOrGDsjTUqBs0r7wypJByQbV2AwyRTEmh9XdWNAQbngQyURg4ve
/7S1Ch7kpIJayYv1Ug0/VTM8sG62TGZXkLoAMft12TeaFtwhzuwEMFrCIapZ8yV5
PgcGxpHOYDWbR3gxS4j6nGiSnF1SMIqa7Ef1DAk/7GRtRKn5xIbbfhjyog==
-----END RSA PRIVATE KEY-----

PUBLIC_KEY:

-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA8XrqQgXpK1JA4iXzpx8p
nmnV2KvFHZ5Jsz48Fy8Z923VvloSsNF0KDS6kmOIfpx8jnXeAOmt6ctW0KM5WEFu
8fvgBvyB1Jvqgnr7kLDd+rhgoKwt5eCFAtfchNxq8HKle5P/4d2k/Kf7Swn89IBR
pDZUiL3GHivyEIlXcATg0j8P5ekvJKMcLZqdak+2fxQkNLGXLXSydFS7capio/7a
erj+U3XBvz2lknWdaKADOjPju06vgwP8/1Ru7aqxFVoQABA1as2Ue5v8Vo+wKkJq
bB3HSaWn1bWhsFYSEjrVmnGXUVWzwQHckMYCdxwPofy7ufB/7Rg8PfGg/j3HEolN
8BOL8PgnZIkDRV5f+RFfupMGn1T9JEkDDAxuE2+Ac7v5ZFXZH0IoWPzwtpS49O+K
dzkkGtTd75q6+3XCvX8lViYDOQvc6Ik3zN9TYTtUWRNGCRdNkBZVxM3W6Mz7TpAI
Y5YEu7xC7T6kZgmQfmFwnF/an/6q2Gy30eZzLo2iLSUYU0t/hVWCHFBSlly/RApm
RBuppC2liIYeXbmuDA46ts1Ra9+N/D0smPHfoqcutX42W8PlYNaMoQAd4/QS96KQ
ZNXUoJVsb7OgRi0OFqWijPer7nm3sNfsu/JB2GTRNI9/S3EjbA6lsGOjqxYWMyUE
zPRjDUoziVxH2lDGWqSBhM8CAwEAAQ==
-----END PUBLIC KEY-----

To test my implementation I entered the JWT on jwt.io and confirmed that the signature was valid. I then perturbed the JWT by changing the last character from an "M" to an "N" and to my surprise the signature was still valid (given PUBLIC_KEY). Now finally I get to my questions:

Perturbed JWT:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1YjM5ZDQ3Mi1iNmU4LTQwOTktOWNkYi1hMWVmYTMzMWEwM2UiLCJpYXQiOjE2NjY1MjA3NjF9.DbQRuAvZdO7rJGyWRHBtdpeiDaN2Dc3LFi2aiCX3Z6YXpGBoLVyOofO05SSPerdgTm_rKrzvtXGlBg3ZojjdpUPqR0SdPjUhYnfvViIpZWWCC34H_MI99jacH8LtINL-BCJ-t-xdwojhS35uljxiHLmnN2ekdbVFbfULS5f-F2a7xyD7axZXjziwxOJUQC9cp71qc6Va5G7wWIAca1Xq6x2Gq5bsUeQ2QjoLLUmrPZ7KRPklGgjZcPkNqDLk7fe6bgfVWQkpapMuIr6zDv6yN90M2SzKHIhCvsUa4s01dOIx10v39NpHd32-buiGcDgLRMM98OM6us6MGSncrIo3ngj4TrDlK7URHapHnKPdmb_x1If7yjMwQyC8it72QSQD1m0BvNoULHILaZSuStiZLWsosq8tZB9KTR-PpnIW32jLKqzTzuPGcH7hnBjBW9YMhxa42RkNDHh7NUa2FRvQ3oFv7auZKer3BaAW1ExzQfKJ-BKOr93hWCw9W76ulOmpzqreaUUhp7nUdcCItV-OWQJnp1nwgiej59ZgjKsxURbjdZ2UtQzPOn8ra506TQs3FcBZDAqO-bIA7TCQvSu0I_rJLM1nE85znSB_UQZZQVP0-52vxSs30Oc8l6mkTFxgRTSMREgOIsjWY_OXVQNEHjCByzaGcTiUIacxxHMjxTN
  1. Is this behaviour expected? As in, should it be this easy to find other valid signatures?
  2. Is it possible that the signature will remain valid even after the payload is changed? I.e. in this example I changed a character from the signature, but I may as well have changed a character in the payload.
  3. Is this approach valid and secure?

(P.S. I have implemented all of this to teach myself the basics of authentication and this approach is not meant to be deployed to a production system.)

dave_thompson_085 avatar
cn flag
**It's [base64](https://en.wikipedia.org/wiki/Base64#Examples):** depending on the length of the data being encoded, the last char (if unpadded, like JOSE) or last char before padding (if padded, like PEM or MIME) may have 2 or 4 bits unused. If you decode your original and altered encodings (with a tool that handles base64url-unpadded) they will be identical. Make a change to the base64 that _does_ change the decoded value(s) and signature verification will fail. This is nothing to do with RSA or cryptography.
dave_thompson_085 avatar
cn flag
PS ontopic part: until quantum, for RSASSA-PKCS1v1_5-with-SHA256 which is what JWS RS256 is, using RSA keys longer than about 3k adds nothing to the security strength and is wasted. And if and when quantum arrives, _all_ RSA including 4k will be dead.
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.