You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用C#验证Telegram Bot哈希?排查验证失败问题及疑问

处理Telegram登录哈希验证失败问题及相关疑问

问题背景

我处理Telegram登录回调URL时,执行以下步骤:

  1. 排除hash参数
  2. 按字母顺序排序剩余参数
  3. 将参数格式化为key=value形式并以\n分隔
  4. 用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 19:37:27