I am trying to implement a signature verification endpoint - or ASP.net WebAPI action filter, to verify that a token has in fact come from AWS Cognito - validate its signature.
I am using the following code, but it always returns invalid. The Javascript code example also below works perfectly with the same keys / token.
Can anyone help?
Thanks, KH
CSharp
public IHttpActionResult Verify([FromBody] string accessToken)
{
string[] parts = accessToken.Split('.');
//From the Cognito JWK set
//{"alg":"RS256","e":"myE","kid":"myKid","kty":"RSA","n":"myN","use":"sig"}]}
var n = Base64UrlDecode("q7ocE2u-JSe1P4AF6_Nasae7e7wUoUxJq058CueDFs9R5fvWQTtAN1rMxBCeLQ7Q8Q0u-vqxr83b6N9ZR5zWUU2stgYzrDTANbIn9zMGDZvSR1tMpun5eAArKW5fcxGFj6klQ0bctlUATSGU5y6xmYoe_U9ycLlPxh5mDluR7V6GbunE1IXJHqcyy-s7dxYdGynTbsLemwmyjDaInGGsM3gMdPAJc29PXozm87ZKY52U7XQN0TMB9Ipwsix443zbE_8WX2mvKjU5yvucFdc4WZdoXN9SGs3HGAeL6Asjc0S6DCruuNiKYj4-MkKh_hlTkH7Rj2CeoV7H3GNS0IOqnQ");
var e = Base64UrlDecode("AQAB");
RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
provider.ImportParameters(new RSAParameters
{
Exponent = new BigInteger(e).ToByteArrayUnsigned(),
Modulus = new BigInteger(n).ToByteArrayUnsigned()
});
SHA512Managed sha512 = new SHA512Managed();
byte[] hash = sha512.ComputeHash(Encoding.UTF8.GetBytes(parts[0] + "." + parts[1]));
RSAPKCS1SignatureDeformatter rsaDeformatter = new RSAPKCS1SignatureDeformatter(provider);
rsaDeformatter.SetHashAlgorithm(sha512.GetType().FullName);
if (!rsaDeformatter.VerifySignature(hash, Base64UrlDecode(parts[2])))
throw new ApplicationException(string.Format("Invalid signature"));
return Ok(true);
}
// from JWT spec
private static byte[] Base64UrlDecode(string input)
{
var output = input;
output = output.Replace('-', '+'); // 62nd char of encoding
output = output.Replace('_', '/'); // 63rd char of encoding
switch (output.Length % 4) // Pad with trailing '='s
{
case 0: break; // No pad chars in this case
case 1: output += "==="; break; // Three pad chars
case 2: output += "=="; break; // Two pad chars
case 3: output += "="; break; // One pad char
default: throw new System.Exception("Illegal base64url string!");
}
var converted = Convert.FromBase64String(output); // Standard base64 decoder
return converted;
}
JavaScript
var jwkToPem = require('jwk-to-pem');
var jwt = require('jsonwebtoken');
var jwks = //jwk set file, which you can find at https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json.
//Decode token
var decoded = jwt.decode(token, {complete: true});
//Get the correct key from the jwks based on the kid
var jwk = jwks.keys.filter(function(v) {
return v.kid === decoded.header.kid;
})[0];
//Convert the key to pem
var pem = jwkToPem(jwk);
//Verify the token with the pem
jwt.verify(token, pem, function(err, decoded) {
//if decoded exists, its valid
});