Featured image of post 關於Telegram Oauth 如何在Java中驗證Hash的正確性 (附上JavaCode)

關於Telegram Oauth 如何在Java中驗證Hash的正確性 (附上JavaCode)

什麼!?我做三方驗證

前言

最近不知道為什麼,突然被派去做三方驗證了,說起來這種比較高技術的事情不應該交給我這種菜雞來做的說,可能是當太久的米蟲終於被抓到了吧,ㄏㄏ

後來在做的時候發現Telegram送來的資料裡面有包含一些認證資訊,大概就像這樣,有個hashCode可以讓後端去校驗資料的完整性,我試著請ChatGPT做這部分的校驗,但跑了幾次都不太行。後來是從網路上看到有段golang的code,拿過來調整成Java相關的code才成功,具體的程式碼我附在下面

但下面那段程式碼我同事提醒有個問題,就是我沒有對auth_date去校驗,那個日期代表的是Oauth證書發給你的時間點,像這邊的1732760301轉成Date就是2024-11-28的日期,後端這邊其實還要去校驗說這個auth_date是否有過期,避免用戶一直拿舊的Oauth資料來登入

1
2
3
4
5
6
7
{
"id":6591452147,
"first_name":"Hoxton",
"username":"Hoxton1337",
"auth_date":1732760301,
"hash":"b8548f86c4646fbac42cd2e0ed35db2cb0ef25a058d7701135cd9492d03260d3"
}

Java Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
 /**
     * 透過tgAuthResult decode出包含的Hash,與Bot Token 進行驗證,具體步驟如下
     * 1. 將tgAuthResult進行Base64 decode
     * 2. 去除掉回傳的HashCode
     * 3. 按照字母順序去排序這些key-value
     * 4. 計算Bot Token的 sha256
     * 5. 計算HMAC_Sha256,明文是上面Bot Token的sha256,key是Bot Token,然後將結果轉換成Hex
     * 6. 比較上面得到的Hex和回傳的HashCode是否一致
     *
     * @param tgAuthResult
     * @return
     */
@Slf4j
public class TestMain {

    private static final String BOT_TOKEN = "Token放這邊";

    public static void main(String[] args) throws NoSuchAlgorithmException {
        String tgAuthResult = "oauth回傳的tgAuthResult放這邊,應該會是一段base64的編碼文字";

        // Step 1: 把Base64編碼轉成人類可以讀的文字
        String decodedData = new String(Base64.getDecoder().decode(tgAuthResult));
        JsonObject jsonObject = JsonParser.parseString(decodedData).getAsJsonObject();


        // Step 2: 取出hash值,並把Hash從jsonObject中移除
        String receivedHash = jsonObject.get("hash").getAsString();
        jsonObject.remove("hash"); // Remove hash from the map before verification



        // Step 3: 把資料去做處理,讓去除了hash的json字串,用英文字母大小的方式做排序
        TreeMap<String, String> sortedData = new TreeMap<>();
        for (Map.Entry<String, com.google.gson.JsonElement> entry : jsonObject.entrySet()) {
            if (!entry.getKey().equals("hash")) {  // Make sure "hash" is excluded from sortedData
                sortedData.put(entry.getKey(), entry.getValue().getAsString());
            }
        }

        StringBuilder dataString = new StringBuilder();
        for (Map.Entry<String, String> entry : sortedData.entrySet()) {
            if (dataString.length() > 0) dataString.append("\n");
            dataString.append(entry.getKey()).append("=").append(entry.getValue());
        }

        //計算HMAC
//        String secretKey = BOT_TOKEN;
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] secretKey = digest.digest(BOT_TOKEN.getBytes(StandardCharsets.UTF_8));
        String calculatedHash = calculateHMAC(dataString.toString(), secretKey);

        // Step 5:認證hash
        if (calculatedHash.equals(receivedHash)) {
            log.info("Verification successful! User authenticated.");
        } else {
            log.warn("Verification failed! Data may have been tampered with.");
        }
    }


    public static String calculateHMAC(String data, byte[] key) {
        try {
            SecretKeySpec keySpec = new SecretKeySpec(key, "HmacSHA256");
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(keySpec);
            byte[] hashBytes = mac.doFinal(data.toString().getBytes(StandardCharsets.UTF_8));

            // Convert bytes to hex format using Apache Commons Codec
            return bytesToHex(hashBytes);
        } catch (Exception e) {
            throw new RuntimeException("Failed to calculate HMAC", e);
        }

    }
    private static String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            hexString.append(String.format("%02x", b));
        }
        return hexString.toString();
    }

}

Trivial 如何查看按鈕所導向的WebApp是哪個

在開發Telegram Bot的時候,可能會想要查看目前這些按鈕背後對應的Url是什麼,但很明顯Telegram是無法查看這些內容的

(沒辦法右鍵也沒辦法叫出啥開發者工具)

image-20241204105515533

但其實我們可以透過網頁版的Telegram來查看這些按鈕的url喔!

image-20241204110038544

Licensed under CC BY-NC-SA 4.0