Java实现Telegram哈希验证异常,求原因及SDK可用方法
问题背景
我在Java中无法完成Telegram哈希验证,现有可正常运行的JS示例:
function main() { var k = CryptoJS.HmacSHA256('k', 'WebAppData'); console.log("k: " + k); var h = CryptoJS.HmacSHA256('data', k); console.log("h: " + h); } main();
输出结果:
"k: 3db03151d3fb2678c9fc0f24ee15817bfd1ff922a712a055f63066f875e43fc1" "h: 9158b0aeb1e882c075ea46a0d5302cffba1a9b66b0df40f5f01eef614f7d9f94"
但使用Apache Commons的HmacUtils实现时:
var secretKey = new HmacUtils("HmacSHA256", "WebAppData").hmacHex("k"); var hash = new HmacUtils("HmacSHA256", secretKey).hmacHex("data");
生成的secretKey与JS一致,但最终hash结果不符:
secretKey: 3db03151d3fb2678c9fc0f24ee15817bfd1ff922a712a055f63066f875e43fc1 hash: 2c73ad3290e04a382b29edea6cb0ef4b1454592429336c32e98ad611452581b2
请问问题出在哪里?另外,rubenlagus的Telegram SDK是否有现成方法可避免手动实现该验证?
问题原因分析
JS代码中,第二次调用CryptoJS.HmacSHA256('data', k)时,k是HMAC生成的原始二进制哈希值,CryptoJS会直接用这个二进制数据作为密钥。
而你的Java代码中,把第一次生成的十六进制字符串secretKey直接传给了HmacUtils,此时HMAC会把这个字符串的UTF-8字节(而非原始哈希的二进制字节)作为密钥,导致最终哈希结果不一致。
修正后的Java实现
需要将十六进制字符串解码为原始字节数组,再作为密钥传入HmacUtils。可以使用Apache Commons Codec的Hex类来完成解码:
import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.digest.HmacUtils; public class TelegramHashTest { public static void main(String[] args) { // 第一步生成密钥的十六进制字符串 String secretKeyHex = new HmacUtils("HmacSHA256", "WebAppData").hmacHex("k"); // 将十六进制字符串解码为原始字节数组 byte[] secretKeyBytes = Hex.decodeHex(secretKeyHex); // 使用原始字节数组作为密钥生成最终哈希 String hash = new HmacUtils("HmacSHA256", secretKeyBytes).hmacHex("data"); System.out.println("secretKey: " + secretKeyHex); System.out.println("hash: " + hash); // 此时结果会和JS一致 } }
rubenlagus的Telegram SDK相关支持
rubenlagus的TelegramBots库(即你说的Telegram SDK)提供了现成的工具类来处理Telegram WebApp的哈希验证,无需手动实现HMAC逻辑。
例如,org.telegram.telegrambots.meta.api.objects.WebAppData类配合TelegramWebAppUtils,可以直接传入initData和机器人令牌,完成哈希验证。示例大致如下:
import org.telegram.telegrambots.meta.api.objects.WebAppData; import org.telegram.telegrambots.meta.util.TelegramWebAppUtils; public class WebAppValidation { public static void main(String[] args) { String botToken = "你的机器人令牌"; String initData = "从WebApp获取的initData字符串"; // 验证哈希是否合法 boolean isValid = TelegramWebAppUtils.validateWebAppData(botToken, initData); System.out.println("验证结果:" + isValid); } }
这个方法会自动处理密钥生成、HMAC计算等步骤,完全避免手动实现可能出现的编码或字节处理错误。
内容的提问来源于stack exchange,提问作者luso

