如何在dotnet中验证JWS?

问题描述 投票:0回答:1

遇到一个困扰我很久的问题,查了很多资料,但大多只是Java和PHP的。然而,事实证明,将此代码移植到 .NET 非常具有挑战性。

要求

我正在开发一个iOS应用程序,需要与Apple应用内购买(IAP)系统集成。用户成功购买后,Apple服务器会生成回调并向我发送一串信息,我需要验证这些信息的真实性。但我不知道该怎么做。 (虽然我很想分享这个JWS的内容,但它包含敏感信息,不能公开披露。)

目前进展

  1. 回调信息验证:

    • 回调中发送的信息是 JSON 字符串,其中负载是 JWS。这个JWS可以直接在https://jwt.io进行验证,确认JWS是有效的并且可以自我验证。
  2. 证书链验证:

    • 我可以使用
      X509Chain
      类来验证 JWS
      x5c
      部分中的证书链。这是我的验证码:
    // AppleRootCA-G3.cer is downloaded from apple.com manually 
    var AppleRootCA = new X509Certificate2("AppleRootCA-G3.cer");
    var handler = new JwtSecurityTokenHandler();
    var jsonToken = handler.ReadToken(dto.signedPayload) as JwtSecurityToken; // dto is the json that the apple sent to me, dto.signedPayload is a jws.
    var itor = jsonToken.Header["x5c"] as IReadOnlyList<object>;
    
    // certificate2.Thumbprint is same as AppleRootCA.Thumbprint
    X509Certificate2 certificate2 = new X509Certificate2(Convert.FromBase64String((string)itor[2]));
    X509Certificate2 certificate1 = new X509Certificate2(Convert.FromBase64String((string)itor[1]));
    X509Certificate2 certificate0 = new X509Certificate2(Convert.FromBase64String((string)itor[0]));
    
    // I have trusted the AppleRootCA-G3.cer before, so I don't need to call them again.
    // X509Store store = new X509Store(StoreName.Root, StoreLocation.CurrentUser);
    // store.Open(OpenFlags.ReadWrite);
    // store.Add(AppleRootCA);
    
    using var chain = new X509Chain();
    // chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
    // chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
    chain.ChainPolicy.ExtraStore.Add(certificate2);
    chain.ChainPolicy.ExtraStore.Add(certificate1);
    chain.ChainPolicy.ExtraStore.Add(certificate0);
    var isValid = chain.Build(AppleRootCA);
    Console.WriteLine(isValid); // true
    
  3. 令牌验证尝试

    • 我尝试使用以下代码来验证令牌:
    var validationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new X509SecurityKey(AppleRootCA),
        ValidateIssuer = false,
        ValidateAudience = false,
        // RequireExpirationTime = true,
        ValidateLifetime = true,
        // TryAllIssuerSigningKeys = true
    };
    try
    {
        handler.ValidateToken(dto.signedPayload, validationParameters, out var st);
        // await handler.ValidateTokenAsync(jsonToken, validationParameters);
        Console.WriteLine("success");
        // return true;
    }
    catch (Exception ex)
    {
        Console.WriteLine($"false: {ex.Message}");
        // return false;
    }
    
    • 但是,我不断遇到以下错误:
    IDX10517: Signature validation failed. The token's kid is missing. Keys tried: 'Microsoft.IdentityModel.Tokens.X509SecurityKey, KeyId: 'B52CB02FD567E0359FE8FA4D4C41037970FE01B0', InternalId: 'tSywL9Vn4DWf6PpNTEEDeXD-AbA'. , KeyId: B52CB02FD567E0359FE8FA4D4C41037970FE01B0
    '. Number of keys in TokenValidationParameters: '1'.
    Number of keys in Configuration: '0'.
    Exceptions caught:
    '[PII of type 'System.Text.StringBuilder' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.
    token: '[PII of type 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. See https://aka.ms/IDX10503 for details.
    
    • ChatGPT 建议我输入正确的
      IssuerSigningKey
      。我尝试将输入参数
      AppleRootCA
      更改为
      certificate0
      certificate1
      certificate2
      ,但同样的错误仍然存在。我目前很茫然,不知道如何确保这个JWS是有效的。我非常渴望获得一些帮助!
ios .net jwt in-app-purchase
1个回答
0
投票

您可以先尝试从证书中获取公钥吗?

// Load the certificate
var certificate = new X509Certificate2(cerFilePath);
    
// Extract the public key
var securityKey = new RsaSecurityKey(certificate.GetRSAPublicKey());

 var tokenHandler = new JwtSecurityTokenHandler();
    var validationParameters = new TokenValidationParameters
    {
        // ValidateIssuer = true,
        // ValidIssuer = issuer,
        // ValidateAudience = true,
        // ValidAudience = audience,
        ValidateLifetime = true,
        IssuerSigningKey = securityKey,
        ValidateIssuerSigningKey = true
    };

    try
    {
       // token - you are getting at the time of callback
        var principal = tokenHandler.ValidateToken(token, validationParameters, out var validatedToken);
        return principal;
    }
    catch (SecurityTokenException)
    {
        // Token validation failed
        return null;
    }
© www.soinside.com 2019 - 2024. All rights reserved.