我正在尝试实施签名验证端点 - 或ASP.net WebAPI操作过滤器,以验证令牌实际上来自AWS Cognito - 验证其签名。
我使用以下代码,但它始终返回无效。下面的Javascript代码示例与相同的键/令牌完美配合。
有人可以帮忙吗?
谢谢, 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
});
答案 0 :(得分:1)
替换
SHA512Managed sha512 = new SHA512Managed();
通过
SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider();
不要忘记正确设置哈希算法
rsaDeformatter.SetHashAlgorithm("SHA256");
答案 1 :(得分:0)
Flo 的答案有效,但现在已内置到 .net 中。使用 https://rafpe.ninja/2017/07/30/net-core-jwt-authentication-using-aws-cognito-user-pool/,其中包含有关如何将其构建到 .net 核心中间件的更多详细信息:
public RsaSecurityKey SigningKey(string Key, string Expo)
{
return new RsaSecurityKey(new RSAParameters()
{
Modulus = Base64UrlEncoder.DecodeBytes(Key),
Exponent = Base64UrlEncoder.DecodeBytes(Expo)
});
}
public TokenValidationParameters TokenValidationParameters()
{
// Basic settings - signing key to validate with, audience and issuer.
return new TokenValidationParameters
{
// Basic settings - signing key to validate with, IssuerSigningKey and issuer.
IssuerSigningKey = this.SigningKey(CognitoConstants.key,CognitoConstants.expo),
ValidIssuer = CognitoConstants.Issuer,
ValidAudience = CognitoConstants.clientid,//Same value you send in the cognito request url
// when receiving a token, check that the signing key
ValidateIssuerSigningKey = true,
// When receiving a token, check that we've signed it.
ValidateIssuer = true,
// When receiving a token, check that it is still valid.
ValidateLifetime = true,
// Do not validate Audience on the "access" token since Cognito does not supply it but it is on the "id"
ValidateAudience = true,
// This defines the maximum allowable clock skew - i.e. provides a tolerance on the token expiry time
// when validating the lifetime. As we're creating the tokens locally and validating them on the same
// machines which should have synchronised time, this can be set to zero. Where external tokens are
// used, some leeway here could be useful.
ClockSkew = TimeSpan.FromMinutes(0)
};
}
private bool ValidateToken(string token)
{
var tokenHandler = new JwtSecurityTokenHandler();
if (tokenHandler.CanReadToken(token))
{
var validationParams = TokenValidationParameters();
SecurityToken validatedToken;
//ValidateToken throws if it fails so if you want to return false this needs changing
var principal = tokenHandler.ValidateToken(token, validationParams, out validatedToken);
return validatedToken != null;
}
return false;
}