【问题标题】:AWS Cognito token verification c#AWS Cognito 令牌验证 c#
【发布时间】:2021-02-19 19:52:02
【问题描述】:

我正在尝试实施签名验证端点 - 或 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
});

【问题讨论】:

    标签: aws-cognito


    【解决方案1】:

    替换

    SHA512Managed sha512 = new SHA512Managed();
    

    通过

    SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider();
    

    不要忘记正确设置哈希算法

    rsaDeformatter.SetHashAlgorithm("SHA256");   
    

    【讨论】:

      【解决方案2】:

      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;
          }
      

      【讨论】:

      • 你是在验证观众还是不是你;代码和 cmets 似乎有冲突。 Do not validate Audience on the "access" token since Cognito does not supply it but it is on the "id" 是什么意思,为什么是ValidateAudience = true?我已经浪费了几个小时试图让观众验证工作,但我认为这是不可能的。我只能在 ValidateAudience = false 时验证令牌,因为 Cognito 的令牌中不包含受众。
      • 不要将此视为福音,但 IIRC:有不同的令牌类型:访问令牌旨在供其他服务器使用,并且不链接到用户。用户令牌自然与用户相关联。访问令牌不包括受众,因此您无法对其进行验证。用户令牌确实包括受众,因此您可以...
      • 我们正在验证观众 - 这是可能的。不过,我感到你很痛苦,Cognito 是那些有大量文档但没有告诉你如何使用它的 aws 服务之一......
      猜你喜欢
      • 2018-03-07
      • 1970-01-01
      • 2020-10-27
      • 2017-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-28
      • 2020-08-30
      相关资源
      最近更新 更多