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

Telegram WebApp的HMAC-SHA256认证数据验证Java代码问题排查

Telegram WebApp HMAC-SHA256 验证失败问题排查与修复

核心错误点分析

你的代码存在4个关键问题导致哈希匹配失败:

1. 依赖参数索引获取hash值

params.get(3).getValue()完全依赖参数顺序,但Telegram返回的initData参数顺序不固定,必须通过参数名称hash查找,否则会取到错误值。

2. HMAC密钥生成逻辑错误

Telegram验证流程要求:

  • 第一步:用bot token作为密钥,对字符串WebAppData计算HMAC-SHA256的原始字节数组
  • 第二步:用第一步得到的字节数组作为密钥,对data-check-string计算HMAC-SHA256并转Hex字符串

你用hmacHex生成Hex字符串作为下一次HMAC的密钥,相当于用Hex字符串的字节作为密钥,和Telegram要求的原始HMAC字节完全不符。

3. 参数值被错误解码

URLEncodedUtils.parse会自动对参数值做URL解码,但Telegram要求生成data-check-string时,必须使用原始未解码的参数值(和initData中收到的完全一致),解码后的值会改变原始内容导致哈希计算错误。

4. 换行符语法错误

代码中String.join(" ", ...)是非法Java语法,需明确使用"\n"作为分隔符。

修正后的代码

@Test
public void hmacTest() {
    String initData = "[initData]"; // 替换为实际initData(若带开头?需先去掉)
    String botToken = "[botToken]"; // 替换为你的机器人token

    // 1. 拆分initData为键值对,保留原始URL编码值
    Map<String, String> params = new HashMap<>();
    // 处理开头的?
    String cleanInitData = initData.startsWith("?") ? initData.substring(1) : initData;
    for (String pair : cleanInitData.split("&")) {
        int eqIndex = pair.indexOf('=');
        if (eqIndex != -1) {
            String key = pair.substring(0, eqIndex);
            String value = pair.substring(eqIndex + 1);
            params.put(key, value);
        }
    }

    // 2. 获取传入的hash值
    String presentedHash = params.get("hash");
    if (presentedHash == null) {
        assert false;
        return;
    }

    // 3. 生成data-check-string:排除hash,按键名升序排序,拼接key=value并用\n分隔
    List<String> sortedKeys = new ArrayList<>(params.keySet());
    sortedKeys.remove("hash");
    Collections.sort(sortedKeys);

    StringBuilder dataCheckString = new StringBuilder();
    for (int i = 0; i < sortedKeys.size(); i++) {
        String key = sortedKeys.get(i);
        dataCheckString.append(key).append("=").append(params.get(key));
        if (i != sortedKeys.size() - 1) {
            dataCheckString.append("\n");
        }
    }

    try {
        // 4. 生成HMAC密钥:用bot token对"WebAppData"计算原始HMAC字节
        Mac hmacSha256 = Mac.getInstance("HmacSHA256");
        SecretKeySpec botTokenKey = new SecretKeySpec(botToken.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
        hmacSha256.init(botTokenKey);
        byte[] secretKeyBytes = hmacSha256.doFinal("WebAppData".getBytes(StandardCharsets.UTF_8));

        // 5. 计算data-check-string的HMAC-SHA256并转Hex字符串
        SecretKeySpec secretKey = new SecretKeySpec(secretKeyBytes, "HmacSHA256");
        hmacSha256.init(secretKey);
        byte[] calculatedHashBytes = hmacSha256.doFinal(dataCheckString.toString().getBytes(StandardCharsets.UTF_8));
        String calculatedHash = Hex.encodeHexString(calculatedHashBytes);

        // 6. 对比哈希(Telegram返回的hash为小写,兼容大小写)
        boolean result = calculatedHash.equalsIgnoreCase(presentedHash);
        assert result;
    } catch (NoSuchAlgorithmException | InvalidKeyException e) {
        e.printStackTrace();
        assert false;
    }
}

额外注意事项

  • 确保initData是window.Telegram.WebApp.initData返回的完整内容,若带开头?需先剔除
  • Telegram返回的hash为小写十六进制字符串,用equalsIgnoreCase避免大小写匹配问题
  • 使用JDK原生Mac类可更精准控制字节流,避免第三方库封装导致的隐藏问题

内容的提问来源于stack exchange,提问作者Jamon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 11:58:35