using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
namespace TxProxy.Services;
public static class Program
public static void Main()
foreach (var (region, queryString) in GetTestData())
var request = HttpUtility.ParseQueryString(queryString);
var requestDict = request.AllKeys.ToDictionary(k => k!, k => request[k]);
var sign = requestDict["sign"]!;
var verifier = new PollCallbackAppSecretVerifier2("jdkCjfV9");
if (verifier.Verify(sign, requestDict!))
Console.WriteLine($"test success, region: {region}");
Console.WriteLine($"test fail, region: {region}");
private static IEnumerable<(string region, string queryString)> GetTestData()
"aid=6757f99093fd5d8f892c1df6&callback=3&channelid=6&distributeId=6673fc28a95311efee0b4710%3Fopenid%3D11518457902938980215&effective=true&encodeparam=F029CE48046791FDF20CE0D4FAD6B71FD581AAF221DFDFFF448A014E0184CEF7AD67280619763D563F5E47F486799A675964143D8C08D727F08161E5A4DF9E4A84CD87943B0C134FC86E7509410EF6AADA4D05DC9EB7BCC44F6C2C5A1BE62BD5956D10F1F7CA07AF7DFCA4CD689E02FE&gameid=29080&info=%7B%7D&os=1&sdk_version=1.24.00.960&sid=6756baba5ac13275e20eaa72&sign=469b336ec67c6e670374bce622a48d1e×tamp=1733818768&ts=1733715691&uid=11518457902938980215&user_name=%E3%82%82%E3%82%84%E3%81%97%E3%81%8A%E3%81%84%E3%81%97%E3%81%84&user_type=intl"
"http://us-txproxy.nikke-kr.com/pollreward/callback?aid=6759a820835f1455803f9583&callback=4&channelid=131&distributeId=6673fc28a95311efee0b4710%3Fopenid%3D336021485036362028&effective=true&encodeparam=18E87FC9D6A01B128CA38C4D75A2BB76469AF035D35A943FA32A07A00620786A809BC21F831995D7242BD95DFD256FCC14E399AE10ACF780C56A58123E60B3ADBD392FD0BF38D017F9DE7875450BC5908DBD4B81CF0C91A5F2575E77A6D6D485&gameid=29080&info=%7B%7D&os=1&sdk_version=1.24.00.960&sid=6673fb50dedcc466880edca9&sign=d6f8136e517b9508668fed7de0679c66×tamp=1733928993&ts=1733928904&uid=336021485036362028&user_name=Player_3xyCQoMv&user_type=intl"
public sealed class PollCallbackAppSecretVerifier
private static readonly HashSet<string> EncryptList =
private readonly string appSecret;
public PollCallbackAppSecretVerifier(string appSecret)
this.appSecret = appSecret;
public bool Verify(string sign, string info, Dictionary<string, string> queryParams)
.Where(data => EncryptList.Contains(data.Key))
.Append(new KeyValuePair<string, string>("appSecret", appSecret))
if (queryParams["user_type"] != "intl")
pairs.Add(new KeyValuePair<string, string>("info", info));
var sortedPairs = pairs.OrderBy(pair => pair.Key);
var signBytes = Convert.FromHexString(sign);
var sb = new StringBuilder();
foreach (var (k, v) in sortedPairs)
var serialized = sb.ToString();
#pragma warning disable CA5351
var result = MD5.HashData(Encoding.ASCII.GetBytes(serialized));
#pragma warning restore CA5351
return StructuralComparisons.StructuralEqualityComparer.Equals(result, signBytes);
public sealed class PollCallbackAppSecretVerifier2
private static readonly HashSet<string> EncryptList =
private readonly string appSecret;
public PollCallbackAppSecretVerifier2(string appSecret)
this.appSecret = appSecret;
public bool Verify(string sign, Dictionary<string, string> queryParams)
.Where(data => EncryptList.Contains(data.Key))
.Append(new KeyValuePair<string, string>("appSecret", appSecret))
if (queryParams["user_type"] == "intl")
pairs.Add(new KeyValuePair<string, string>("info", queryParams["info"]));
var sortedPairs = pairs.OrderBy(pair => pair.Key);
var signBytes = Convert.FromHexString(sign);
var sb = new StringBuilder();
foreach (var (k, v) in sortedPairs)
var serialized = sb.ToString();
#pragma warning disable CA5351
var result = MD5.HashData(Encoding.ASCII.GetBytes(serialized));
#pragma warning restore CA5351
return StructuralComparisons.StructuralEqualityComparer.Equals(result, signBytes);