11-07-22 03:22 PM
11-07-22 09:41 PM
12-07-22 01:29 PM
13-07-22 06:42 PM
14-07-22 11:55 AM
14-07-22 03:52 PM
return JWT.Encode(payload, rsa, JwsAlgorithm.RS256, headers, options: new JwtOptions { DetachPayload = true, EncodePayload = true});
Cheers,
Eric
16-07-22 12:12 AM
return JWT.Encode(payload, rsa, JwsAlgorithm.RS256, headers );
or your suggested replacement ofreturn JWT.Encode(payload, rsa, JwsAlgorithm.RS256, headers, options: new JwtOptions { DetachPayload = true, EncodePayload = true});
having run several test cases with both code variants, and compared the signatures generated (for both test cases that contain special characters, and those that don't) - the signature is always identical for a given payload irrespective of which of the lines of code I use.
However, if I use the code:-
return JWT.Encode(payload, rsa, JwsAlgorithm.RS256, headers, options: new JwtOptions { DetachPayload = true, EncodePayload = false});
then the signature is always different to that generated by both the above lines, and the API call fails, so I'm pretty sure that the base64URL encoding option is enabled by default, the document you pointed me towards in your post seems to supports that:-
I believe the DetachPayload option has no effect to the signature, as the signature is generated by the code line
which is already ensuring that only the header and the signature are used in the token, and the payload is not returned.
So, in order to do some more testing, I changed the above code line to:-
and also ensured Detachpayload was set to false, so that I could compare the entire signature being generated in my test cases.
Then I took several of the test cases where the name contains no special characters, where I know the code is working and generated a signature through the jwt.io website, and also with my code stage and compared them both. As expected, they were absolutely identical.
Then I took a test case where the name has a special character and did the same, so I could compare both of the output:-
The section I have highlighted red above is the encoded payload, the top line was generated by the code stage, the bottom line was generated by the jwt.io website. so, now I can see what the encoded payload looks like for both a signature that works (jwt.io) and one that doesn't (my code stage), I can use Blue Prim to decode the base64url back to plain text to examine the difference.
The payload generated by jwt.io was
eyJhY2NvdW50Ijp7InNvcnRDb2RlIjoiMTEwMDI2IiwiYWNjb3VudE51bWJlciI6IjExNDU5NTYwIiwibmFtZSI6IlAuRCZFLlAgREFTQ0hFUiIsImFjY291bnRUeXBlIjoiUGVyc29uYWwifX0
and decoding back to plain text gives:-
which is correct, as this was the original payload input,
However, the payload generated by my code stage was:-
eyJhY2NvdW50Ijp7InNvcnRDb2RlIjoiMTEwMDI2IiwiYWNjb3VudE51bWJlciI6IjExNDU5NTYwIiwibmFtZSI6IlAuRFx1MDAyNkUuUCBEQVNDSEVSIiwiYWNjb3VudFR5cGUiOiJQZXJzb25hbCJ9fQ
Which when decoded gives:-
So its clear that my code is replacing the '&' with '\u0026' for some reason, however I am no closer to understanding why?
I don't suppose, now we can see what the difference are, you have any suggestions as to what may be causing this?
Just to confirm, I get the same results when I used the 'EncodePayload=True' option.
Many thanks
Tony
20-07-22 08:50 PM
20-07-22 11:09 PM
// Utility function to build an instance of a JWT encoder that uses the RS256 algorithm.
internal IJwtEncoder GetRS256JWTEncoder(RSAParameters rsaParams)
{
var csp = new RSACryptoServiceProvider();
csp.ImportParameters(rsaParams);
var algorithm = new RS256Algorithm(csp, csp);
var serializer = new JsonNetSerializer();
var urlEncoder = new JwtBase64UrlEncoder();
var encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
return encoder;
}
// Utility function to build an RSAParameters instance with just a private key (i.e. not actual X.509 certificate).
internal RSAParameters GetRSAParameters(string privateKey)
{
string pkey = GetPrivateKeyValue(privateKey);
byte[] keyBytes = Convert.FromBase64String(pkey);
AsymmetricKeyParameter asymKeyParam = PrivateKeyFactory.CreateKey(keyBytes);
RsaPrivateCrtKeyParameters rsaPrivateCrtKeyParams = (RsaPrivateCrtKeyParameters)asymKeyParam;
RSAParameters rsaParams = DotNetUtilities.ToRSAParameters(rsaPrivateCrtKeyParams);
return rsaParams;
}
// Utility function for extracting just the actual private key value from an RSA private key string.
internal string GetPrivateKeyValue(string privateKey)
{
var beginPrivateKeyTag = @"-----BEGIN PRIVATE KEY-----";
var endPrivateKeyTag = @"-----END PRIVATE KEY-----";
StringBuilder tmpPrivateKey = new StringBuilder();
// We need to pull out just the value of the private key. We don't need the header/footer.
if (privateKey.Contains(beginPrivateKeyTag))
{
int start = privateKey.IndexOf(beginPrivateKeyTag, StringComparison.OrdinalIgnoreCase) + beginPrivateKeyTag.Length;
int end = privateKey.IndexOf(endPrivateKeyTag, StringComparison.OrdinalIgnoreCase);
tmpPrivateKey.Append(privateKey.Substring(start, (end - start)));
}
else
tmpPrivateKey.Append(privateKey);
// After extracting just the <VALUE> portion, we need to replace all \n (Unix newlines) with
// Windows new line values (i.e. \r\n).
tmpPrivateKey.Replace(@"\n", Environment.NewLine);
return tmpPrivateKey.ToString().Trim();
}
var rsaParams = GetRSAParameters(privateKey);
var encoder = GetRS256JWTEncoder(rsaParams);
var token = encoder.Encode(payloadFinal, new byte[0]);
// You can add your additional logic for pulling out the payload portion and creating the JWS Token here.
Cheers,
Eric
26-07-22 12:53 PM