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
相关产品推荐
相关产品推荐

