You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Telegram WebApp initDataUnsafe哈希验证Java实现结果不匹配问题求助

Telegram WebApp initDataUnsafe哈希验证Java实现结果不匹配问题求助

我刚接触Telegram开发,现在想用Java验证Telegram WebApp(不是登录组件)提供的initData哈希值,已经严格按照官方文档和Spring Boot相关的Telegram认证指南来操作了,但计算出来的哈希值始终和Telegram返回的对不上,实在卡壳了,来求助各位大佬!

根据Telegram官方要求,验证用的data-check字符串需要把除了hash之外的所有字段按字母顺序排序,每个字段以key=value的形式用换行符\n拼接——这一步我确认已经照做了。

我的场景和数据

我通过Telegram Mini App的window.Telegram.WebApp.initDataUnsafe拿到的原始数据结构大概是这样的:

{
  "id": 785xxxxxx,
  "first_name": "xxxxxxx",
  "last_name": "xxxxxx",
  "language_code": "en",
  "allows_write_to_pm": true,
  "photo_url": "https://t.me/i/userpic/320/abc123.svg",
  "auth_date": "1750072455",
  "hash": "a55cdfdexxxxxx"
}

我的Java验证代码

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;

public class TelegramAuthValidator {
    public static boolean isValid(Map<String, String> authData, String botToken) {
        String receivedHash = authData.get("hash");
        authData.remove("hash");

        // Step 1: 构建data_check_string
        Map<String, String> sorted = new TreeMap<>(authData);
        String dataCheckString = sorted.entrySet().stream()
                .map(e -> e.getKey() + "=" + e.getValue())
                .collect(Collectors.joining("\n"));

        try {
            // Step 2: 生成密钥(用"WebAppData"作为密钥,对botToken做HMAC_SHA256)
            Mac mac1 = Mac.getInstance("HmacSHA256");
            mac1.init(new SecretKeySpec("WebAppData".getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
            byte[] secretKey = mac1.doFinal(botToken.getBytes(StandardCharsets.UTF_8));

            // Step 3: 用生成的密钥对dataCheckString做HMAC_SHA256
            Mac mac2 = Mac.getInstance("HmacSHA256");
            mac2.init(new SecretKeySpec(secretKey, "HmacSHA256"));
            byte[] hmac = mac2.doFinal(dataCheckString.getBytes(StandardCharsets.UTF_8));

            String calculatedHash = bytesToHex(hmac);
            System.out.println("DataCheckString: " + dataCheckString);
            System.out.println("预期Hash: " + receivedHash);
            System.out.println("计算Hash: " + calculatedHash);

            return calculatedHash.equalsIgnoreCase(receivedHash);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    // 补充我用的bytes转hex方法(避免是这里出问题)
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }
}

我已经尝试过的排查步骤

  • 反复检查dataCheckString,确保没有多余的换行、空格或特殊字符
  • 对比过能正常工作的PHP实现逻辑,步骤完全对齐
  • 试过两种密钥生成方式:直接对botToken做SHA256,以及官方要求的HMAC_SHA256(botToken, "WebAppData")
  • 确认字段排序是严格按字母顺序,值的格式(比如布尔值true的大小写、日期字符串)和Telegram返回的完全一致

但不管怎么调,计算出来的哈希值和Telegram返回的就是对不上,实在找不到问题出在哪了。有没有大佬能帮我分析下,到底哪里错了?怎么才能在Java里成功验证Telegram WebApp的initDataUnsafe哈希值?

内容来源于stack exchange

火山引擎 最新活动