Reputation: 121
Trying to sign a JWT token using jose-jwt but for some reason I'm failing miserably to understand why it's not working as expected.
The signing part is working in my test but if I use this to request the server resource it fails. But if I paste this to jwt.io and paste my private key it works perfectly when I try to request server resources with this new token
So it looks like even though it seems to be working in my .net code base, it's not actually signing the token.
I've wrote few tests and it does works in terms of signing and verifying without any issues. gist
public class TestUsingJoseJwt
{
public string Identifier { get; set; }
public string Nonce { get; set; }
public JsonNetSerializer JsonNetSerializer { get; set; }
[SetUp]
public void SetupTest()
{
Identifier = Guid.NewGuid().ToString();
Nonce = Guid.NewGuid().ToString();
JsonNetSerializer = new JsonNetSerializer();
}
[Test]
public void CanGenerateSignedJwtUsingJose()
{
// Get the private/ public keys
var privateKeyTexts = File.ReadAllText("FULL PATH TO MY PRIVATE KEY");
var publicKeyTexts = File.ReadAllText("FULL PATH TO MY PUBLIC KEY");
var signedClaim = CreateJwtToken("AAC_1c0b51b0-d673-4872-898e-ce9d6d3f0482", Identifier, Nonce);
var token = CreateToken(signedClaim, privateKeyTexts);
var payload = DecodeToken(token, publicKeyTexts);
var expected = JsonNetSerializer.Serialize(signedClaim);
payload.Should()
.Be(expected, "provided object should be correctly serialized in the token");
}
public static string CreateToken(SignedClaim signedClaim, string privateRsaKey)
{
RSAParameters rsaParams;
using (var tr = new StringReader(privateRsaKey))
{
var pemReader = new PemReader(tr);
var keyPair = pemReader.ReadObject() as AsymmetricCipherKeyPair;
if (keyPair == null)
{
throw new Exception("Could not read RSA private key");
}
var privateRsaParams = keyPair.Private as RsaPrivateCrtKeyParameters;
rsaParams = DotNetUtilities.ToRSAParameters(privateRsaParams);
}
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.ImportParameters(rsaParams);
//Dictionary<string, object> payload = claims.ToDictionary(k => k.Type, v => (object)v.Value);
var payload = JsonConvert.SerializeObject(signedClaim);
return Jose.JWT.Encode(payload, rsa, JwsAlgorithm.RS256);
}
}
public string DecodeToken(string token, string publicRsaKey)
{
RSAParameters rsaParams;
using (var tr = new StringReader(publicRsaKey))
{
var pemReader = new PemReader(tr);
var publicKeyParams = pemReader.ReadObject() as RsaKeyParameters;
if (publicKeyParams == null)
{
throw new Exception("Could not read RSA public key");
}
rsaParams = DotNetUtilities.ToRSAParameters(publicKeyParams);
}
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.ImportParameters(rsaParams);
// This will throw if the signature is invalid
return Jose.JWT.Decode(token, rsa, JwsAlgorithm.RS256);
}
}
public SignedClaim CreateJwtToken(string consentId, string identifier, string nonce)
{
return new SignedClaim
{
Aud = "https://sandbox.api.endpoint",
Scope = "openid accounts",
Iss = "bdn-8Zpkl9dCbN2tArm2TrgretrtKSexT2XxQ26uL8B",
ResponseType = "code id_token",
RedirectUri = "TEST_REDIRECT_URL",
State = identifier,
Exp = DateTimeOffset.UtcNow.AddMinutes(10).ToUnixTimeSeconds(),
Nonce = nonce,
ClientId = "bdn-8Zpkl9dCbN2tAeerertreterexT2XxQ26uL8B",
Claims = new Claims
{
IdToken = new IdToken
{
Acr = new Acr
{
Essential = true,
Value = "urn:openbanking:psd2:sca",
},
OpenbankingIntentId = new Acr
{
Essential = true,
Value = consentId,
},
},
Userinfo = new Userinfo
{
OpenbankingIntentId = new Acr
{
Value = consentId,
Essential = true,
},
},
},
};
}
}
I'm pretty much in the end of the tunnel with confused than ever before :( Can anyone shed any lights as to why it works when I manually copy/paste the private key in jwt.io but the code is failing to sign the token please?
Here's a sample token request
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwczovL3NhbmRib3guYXBpLmJhcmNsYXlzIiwic2NvcGUiOiJvcGVuaWQgYWNjb3VudHMiLCJpc3MiOiJiZG4tOFpwa2w5ZENiTjJ0QXJtMlRrZGVzSG1YWUtTZXhUMlh4UTI2dUw4QiIsImNsYWltcyI6eyJpZF90b2tlbiI6eyJhY3IiOnsidmFsdWUiOiJ1cm46b3BlbmJhbmtpbmc6cHNkMjpzY2EiLCJlc3NlbnRpYWwiOnRydWV9LCJvcGVuYmFua2luZ19pbnRlbnRfaWQiOnsidmFsdWUiOiJBQUNfMWMwYjUxYjAtZDY3My00ODcyLTg5OGUtY2U5ZDZkM2YwNDgyIiwiZXNzZW50aWFsIjp0cnVlfX0sInVzZXJpbmZvIjp7Im9wZW5iYW5raW5nX2ludGVudF9pZCI6eyJ2YWx1ZSI6IkFBQ18xYzBiNTFiMC1kNjczLTQ4NzItODk4ZS1jZTlkNmQzZjA0ODIiLCJlc3NlbnRpYWwiOnRydWV9fX0sInJlc3BvbnNlX3R5cGUiOiJjb2RlIGlkX3Rva2VuIiwicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9pbnN5bmMudGVzdC9pbnN5bmNXZWIvb2F1dGgvY29uZmlybSIsInN0YXRlIjoiMmYxMGQyN2EtYmE0Zi00YjJhLTlhNmEtOTQ3NTkyZDdhMThkIiwiZXhwIjoxNTc0MjU1MDgwLjAsIm5vbmNlIjoiZjQyZDkyMTUtMDBmMS00MTVmLTg5NGEtNTJiN2RjZjZmY2M2IiwiY2xpZW50X2lkIjoiYmRuLThacGtsOWRDYk4ydEFybTJUa2Rlc0htWFlLU2V4VDJYeFEyNnVMOEIifQ.Uce2-ZcqQk3JAABltywib79UADhh7HoFv2e6pa0q1b_JuZUeaHk0L_7iC__hVY-TCfG5dmjIFWi80ioQ9hM5WeIX2RLRhhjk8s8pjrOmY243gg2_YJNKK9qwVZtHbieY8OI46ZH8CnQhSILAtVVgqXZeXtFPfk4VvW_yJf4DELSoDtB3hSxoMZtjGbnF5JBzNB0fFBpnTzKQ7a3RPsL_rJUc9IbXOgXBoF-OmlAFNu8F4l5gMmrL146WSSytkUBVQQhDxxc0JQHcz3TqbapxcWSuchu0-nGR8cIXimvQKyaWpG0OISUA-4H5dH3GHNbxOBDT7wTKYLzdG5tj_d9beQ
Upvotes: 0
Views: 490
Reputation: 121
Turns out it's the Json Serialization that messes things up.
var payload = JsonConvert.SerializeObject(signedClaim);
I've just removed this and pass the object directly to Encode and that seems to have fixed it. Bit annoyed as I didn't spot that earlier. But at least it works now.
Upvotes: 0