diff --git a/NATS.Jwt.Tests/NatsJwtTests.cs b/NATS.Jwt.Tests/NatsJwtTests.cs index 682c3e0..cf1024d 100644 --- a/NATS.Jwt.Tests/NatsJwtTests.cs +++ b/NATS.Jwt.Tests/NatsJwtTests.cs @@ -336,4 +336,11 @@ public void TestMultipleExports() string jsonStr = JsonSerializer.Serialize(json, new JsonSerializerOptions { WriteIndented = true }); output.WriteLine(jsonStr); } + + [Fact] + public void TestDecodeUserClaimWithTamperedJWTThrowsError() + { + var jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJPSk9CUkZDQ0NGNEMzU1JWQzRLNEhTVFNYRlBTSVZBSzJPRUxOMlZXNE9GQ0IzQTVMMkNBIiwiaWF0IjoxNzMwMzk3NTU0LCJpc3MiOiJVQklUR0VSQk9JVEZDWkJHNTNUSkk3M1BHTjdBMzdPTVkyWE5YUU82VUZUSlA1TE5VWVFORUpXSSIsIm5hbWUiOiJVWFgiLCJzdWIiOiJVQklUR0VSQk9JVEZDWkJHNTNUSkk3M1BHTjdBMzdPTVkyWE5YUU82VUZUSlA1TE5VWVFORUpXSSIsIm5hdHMiOnsicHViIjp7ImFsbG93IjpbImFsbG93Llx1MDAzRSJdfSwic3ViIjp7ImFsbG93IjpbInN1YnNjcmliZS5cdTAwM0UiXX0sInN1YnMiOi0xLCJkYXRhIjotMSwicGF5bG9hZCI6LTEsInR5cGUiOiJ1c2VyIiwidmVyc2lvbiI6Mn19.SjIBpWWLNCZmgYZwrFHEJSTkm5M9bik0kgQyG-3V9Nn5sTrfO1Llj3hs7z9R7b1rCyGsFm1RkpZAVAnS5ay2BA"; + Assert.Throws(() => _natsJwt.DecodeUserClaims(jwt)); + } } diff --git a/NATS.Jwt/NatsJwt.cs b/NATS.Jwt/NatsJwt.cs index db9779f..a28577f 100644 --- a/NATS.Jwt/NatsJwt.cs +++ b/NATS.Jwt/NatsJwt.cs @@ -303,14 +303,15 @@ public T DecodeClaims(string jwt, JsonTypeInfo jsonTypeInfo) } // Verify the signature - // var signingInput = $"{parts[0]}.{parts[1]}"; - // var signature = Encoding.ASCII.GetBytes(EncodingUtils.FromBase64UrlEncoded(parts[2])); - // var publicKey = KeyPair.FromPublicKey(claims.Issuer.AsSpan()); - // - // if (!publicKey.Verify(Encoding.ASCII.GetBytes(signingInput), signature)) - // { - // throw new NatsJwtException("JWT signature verification failed"); - // } + var signingInput = $"{parts[0]}.{parts[1]}"; + var signature = DecodeSignature(parts[2]); + var publicKey = KeyPair.FromPublicKey(claims.Issuer.AsSpan()); + + if (!publicKey.Verify(Encoding.ASCII.GetBytes(signingInput), signature)) + { + throw new NatsJwtException("JWT signature verification failed"); + } + return claims; } @@ -447,4 +448,17 @@ private static string Serialize(T data, JsonTypeInfo typeInfo) JsonSerializer.Serialize(jsonWriter, data, typeInfo); return EncodingUtils.ToBase64UrlEncoded(writer.WrittenMemory.ToArray()); } + + /// + /// Can't use EncodingUtils.FromBase64UrlEncoded as not backward compatible with golang code. + /// + /// JWT signature. + /// base64 decoded JWT signature. + private static byte[] DecodeSignature(string signature) + { + signature = signature.Replace('-', '+').Replace('_', '/'); + var pd = signature.Length % 4; // ensure length divisible by 4..pad with == + signature = signature + "====".Substring(0, pd); + return Convert.FromBase64String(signature); + } }