如何用C#验证Telegram Bot哈希?排查验证失败问题及疑问
处理Telegram登录哈希验证失败问题及相关疑问
问题背景
我处理Telegram登录回调URL时,执行以下步骤:
- 排除
hash参数 - 按字母顺序排序剩余参数
- 将参数格式化为
key=value形式并以\n分隔 - 用SHA256处理Bot密钥,再用该密钥对生成的字符串进行HMAC加密
但生成的哈希始终和URL中的hash不一致。同时有两个疑问:
- 是否需要解码
photo_url的值(原格式类似https%3A%2F%2Ft.me)? - 处理前是否需要将字符串转为小写?
相关代码
public IActionResult GetTelegram([FromQuery] Dictionary<string, string> queryParams) { if (!queryParams.TryGetValue("hash", out var hash) || string.IsNullOrEmpty(hash)) { return BadRequest("Missing hash parameter"); } queryParams.Remove("hash"); if (queryParams.ContainsKey("photo_url")) { queryParams["photo_url"] = WebUtility.UrlDecode(queryParams["photo_url"]); } var dataCheckString = string.Join("\n", queryParams.OrderBy(kv => kv.Key) .Select(kv => $"{kv.Key}={kv.Value}")); var secretKey = ShaHash("Bot_Token"); var myHash = HashHmac(secretKey, Encoding.UTF8.GetBytes(dataCheckString)); var myHashStr = string.Concat(myHash.Select(i => i.ToString("x2"))); if (myHashStr == hash) { return Ok("success。"); } else { return Unauthorized("fail。"); } } private static byte[] ShaHash(string value) { using (SHA256 hash = SHA256.Create()) { return hash.ComputeHash(Encoding.UTF8.GetBytes(value)); } } private static byte[] HashHmac(byte[] key, byte[] message) { using (var hash = new HMACSHA256(key)) { return hash.ComputeHash(message); } }
参数示例
{ "id": "xxxxx", "first_name": "xxxxx", "username": "xxxxxx", "photo_url": "https%3A%2F%2Ft.me%xxxxxx.jpg", "auth_date": "xxxxx", "hash": "xxxxxxxxx" }
问题排查与解答
1. 核心错误点
(1)Bot Token未替换为实际值
代码中ShaHash("Bot_Token")使用的是占位符,必须替换为你实际的Telegram Bot Token(格式类似123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11),这是导致哈希不一致的最常见原因。
(2)错误解码了photo_url
Telegram生成哈希时使用的是原始URL编码后的photo_url值,你现在的解码操作会导致生成的dataCheckString与Telegram计算时的字符串不一致,必须删除这部分解码逻辑。
2. 疑问解答
- 是否需要解码
photo_url?:不需要,保持原始URL编码值即可。 - 是否需要转小写?:不需要,保持参数的原始大小写(包括键和值),Telegram的哈希计算基于原始字符串。
修正后的代码
public IActionResult GetTelegram([FromQuery] Dictionary<string, string> queryParams) { if (!queryParams.TryGetValue("hash", out var hash) || string.IsNullOrEmpty(hash)) { return BadRequest("Missing hash parameter"); } queryParams.Remove("hash"); // 移除photo_url解码操作 var dataCheckString = string.Join("\n", queryParams.OrderBy(kv => kv.Key) .Select(kv => $"{kv.Key}={kv.Value}")); // 替换为实际Bot Token var secretKey = ShaHash("YOUR_ACTUAL_TELEGRAM_BOT_TOKEN"); var myHash = HashHmac(secretKey, Encoding.UTF8.GetBytes(dataCheckString)); var myHashStr = string.Concat(myHash.Select(i => i.ToString("x2"))); // 使用Ordinal比较避免大小写问题(虽然Telegram哈希是小写,但严谨起见) if (myHashStr.Equals(hash, StringComparison.Ordinal)) { return Ok("success。"); } else { // 可以添加日志输出方便排查 return Unauthorized($"fail。预期哈希:{hash},生成哈希:{myHashStr}"); } } private static byte[] ShaHash(string value) { using (SHA256 hash = SHA256.Create()) { return hash.ComputeHash(Encoding.UTF8.GetBytes(value)); } } private static byte[] HashHmac(byte[] key, byte[] message) { using (var hash = new HMACSHA256(key)) { return hash.ComputeHash(message); } }
额外排查建议
- 打印生成的
dataCheckString,确认参数排序正确、格式为key=value且用\n分隔,无多余空格或换行。 - 验证Bot Token完全正确,无拼写错误或多余字符。
内容的提问来源于stack exchange,提问作者ZHAN RUI ZHANG
相关产品推荐
相关产品推荐

