Files
ykxmz/_build/gen_tls_compat.py
T
xmz7891 b705216e4a ykxmz 1.5.0锛氫慨澶嶆墜琛ㄧ TLS 璇佷功鏍¢獙 + MTU 淇缃戠粶绋冲畾鎬?
琚帶绔?- 淇 MTU 闂锛氬す MSS + 璺緞 MTU 鎺㈡祴锛堝叕缃戝彲鐢ㄧ巼 48% -> 100%锛?- 澶嶇敤 HTTP 杩炴帴锛堝厤鎺夋瘡娆℃柊寤?TLS 鐨?30% 鎻℃墜澶辫触锛?- 姣忓垎閽熷洖澶存壘灞€鍩熺綉锛岃嚜鍔ㄥ垏鍥炲揩 400 鍊嶇殑璺緞
- 鐢婚潰蹇冭烦甯э細闈欐鏃剁渷娴侀噺锛屽鎴风浠嶇煡閬撴祦鏄椿鐨?- 閿佸睆鏃剁粰鍑哄彲鎿嶄綔鐨勬彁绀?
鎵嬭〃绔?- 鍐呯疆 GTS Root R4 / GlobalSign Root CA锛屼慨 SSLHandshakeException
- 杩炴帴閿欒缈昏瘧鎴愪汉璇濓紙璇佷功/鏃堕棿/瑙f瀽/瓒呮椂鍒嗗埆鎻愮ず锛?- 缁忎腑杞繛涓婂悗鐢ㄦ湇鍔″櫒鎶ョ殑鍦板潃鍒锋柊宸查厤瀵硅褰?- 鏂板灞€鍩熺綉杩炴帴椤甸潰銆佹湇鍔″櫒寤惰繜妫€娴?
鏈嶅姟鍣ㄧ
- 鏀寔 chunked 璇锋眰浣擄紙淇瘡 16 绉掍竴娆$殑杩炴帴閿欎贡涓庡ぇ闈㈢Н 502锛?- 缃戠粶璋冧紭鑴氭湰 net-tune.sh锛堝紑鏈鸿嚜鍔ㄥ簲鐢級
- 绠$悊椤碉細鎵归噺鍒犻櫎銆佺綉缁滀綋妫€銆佷笅杞藉尯
- 闅ч亾鏀?HTTP/2 + 鍙岃繛鎺?
鏂囨。
- 璁稿彲鍗忚 / 瀹夎鍓嶅繀璇?/ 瀹夎鍚庨』鐭ワ紙浣滆€咃細灏忛害瀛愶級
2026-10-06 19:01:05 +08:00

307 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""生成 TlsCompat.java —— 修手表端 "SSLHandshakeException: Chain validation failed"。
问题:
手表连中转报 Chain validation failed。
证书链是:xxmxxs.top <- GTS WE1 <- GTS Root R4 <- GlobalSign Root CA
廉价手表的根证书库常常缺 GTS Root R4(2021 年才加进 AOSP),
于是找不到信任锚 -> 握手直接失败。
修法:
把这两个根证书**内嵌进 APK**,校验时"系统库 或 内置库"任一通过即可。
这两个都是公开的合法根证书,不是"跳过校验" ——
安全性仍然完整:证书必须由可信 CA 签发、且域名匹配。
只是把可信 CA 集合从"手表自带的那一份"扩大到"手表自带的 + 这两个"。
另外把错误信息翻译成人话:
* 如果报的是时间相关(not yet valid / expired)-> 提示手表时间不对
* 如果还是校验失败 -> 提示可能被中间人劫持
顺带还把 "HTTP 错误码" 和 "网络不通" 区分开,便于用户自查。
"""
from __future__ import annotations
import sys
from pathlib import Path
ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote")
CERTS = ROOT / "android" / "certs"
OUT = (ROOT / "android" / "src" / "com" / "lanremote" / "watch"
/ "TlsCompat.java")
def pem_to_java(text: str) -> str:
"""把 PEM 变成 Java 字符串拼接(每行一个常量,避免超长行)。"""
lines = [line.strip() for line in text.strip().splitlines()
if line.strip()]
parts = " +\n ".join(f'"{line}\\n"' for line in lines)
return parts
def main() -> int:
gts = (CERTS / "gts_root_r4.pem").read_text(encoding="ascii")
globalsign = (CERTS / "globalsign_root_ca.pem").read_text(encoding="ascii")
source = '''package com.lanremote.watch;
import java.io.ByteArrayInputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
/**
* TLS 兼容层:让老手表也能连上中转。
*
* <p>为什么需要:
* 手表端报 {@code SSLHandshakeException: Chain validation failed}。
* 中转的证书链是
* {@code relay.xxmxxs.top <- GTS WE1 <- GTS Root R4 <- GlobalSign Root CA},
* 而很多廉价手表的系统根证书库缺 GTS Root R4(AOSP 2021 年才加),
* 于是找不到信任锚,握手直接失败。
*
* <p>做法:
* 把这两个根证书内嵌进 APK,校验时<b>系统库或内置库任一通过即可</b>。
* 这两个都是公开的合法根证书,<b>不是跳过校验</b>:
* 证书仍必须由可信 CA 签发、且域名匹配,只是把可信 CA 的集合
* 从"手表自带的那一份"扩大到"手表自带的 + 这两个"。
*
* <p>同时把难懂的异常翻译成用户能看懂的话。
*/
public final class TlsCompat {
private TlsCompat() {
}
// ---------------- 内置的根证书 ----------------
/** Google Trust Services Root R4(自签名根,EC P-384)。 */
private static final String GTS_ROOT_R4 =
%GTS%;
/** GlobalSign Root CA(自签名根,RSA 2048,1998 年起就在各系统里)。 */
private static final String GLOBALSIGN_ROOT_CA =
%GS%;
private static SSLSocketFactory factory;
private static X509TrustManager bundled;
private static boolean ready;
private static boolean initTried;
// ---------------- 对外 ----------------
/**
* 打开一个连接,HTTPS 时挂上兼容的 SSLSocketFactory。
*
* <p>HTTP(局域网中转)不走这里,原样返回。
*/
public static HttpURLConnection open(String urlText, int timeoutMs)
throws Exception {
HttpURLConnection connection =
(HttpURLConnection) new URL(urlText).openConnection();
connection.setConnectTimeout(timeoutMs);
connection.setReadTimeout(timeoutMs);
connection.setRequestProperty("User-Agent", "LanRemote/1.4.0");
connection.setRequestProperty("Accept-Encoding", "identity");
if (connection instanceof HttpsURLConnection) {
SSLSocketFactory ssl = factory();
if (ssl != null) {
((HttpsURLConnection) connection).setSSLSocketFactory(ssl);
}
}
return connection;
}
/** 拿到兼容的 SSLSocketFactory;准备失败就返回 null(退回系统默认)。 */
public static synchronized SSLSocketFactory factory() {
if (!initTried) {
initTried = true;
try {
KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
store.load(null, null);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
int index = 0;
for (String pem : new String[]{GTS_ROOT_R4, GLOBALSIGN_ROOT_CA}) {
X509Certificate cert = (X509Certificate) cf.generateCertificate(
new ByteArrayInputStream(pem.getBytes("UTF-8")));
store.setCertificateEntry("lanremote-ca-" + (index++), cert);
}
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
tmf.init(store);
for (TrustManager tm : tmf.getTrustManagers()) {
if (tm instanceof X509TrustManager) {
bundled = (X509TrustManager) tm;
break;
}
}
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new TrustManager[]{new CompatTrustManager()}, null);
factory = context.getSocketFactory();
ready = true;
} catch (Throwable error) {
// 准备失败就退回系统默认,不影响正常设备
factory = null;
ready = false;
}
}
return ready ? factory : null;
}
/**
* 把 TLS / 网络异常翻译成用户能看懂的一句话。
*
* <p>手表上原本只会显示一长串英文类名,用户完全不知道该怎么办。
*/
public static String explain(Throwable error) {
if (error == null) {
return "未知错误";
}
String text = String.valueOf(error);
String lower = text.toLowerCase();
if (lower.contains("chain validation failed")
|| lower.contains("trust anchor")
|| lower.contains("certpathvalidator")) {
return "证书校验失败(中转的根证书手表不认识)。"
+ "可能是手表系统太旧,或网络被劫持。";
}
if (lower.contains("not yet valid") || lower.contains("expired")
|| lower.contains("certificate expired")) {
return "证书时间不对 —— 把手表的【日期和时间】调成自动获取,"
+ "再试一次。";
}
if (lower.contains("timeout") || lower.contains("timed out")) {
return "连接超时(网络太慢或被挡住)";
}
if (lower.contains("unable to resolve host")
|| lower.contains("unknownhost")) {
return "域名解析不了(检查手表能不能上网)";
}
if (lower.contains("connection refused")) {
return "对方拒绝了连接(电脑上 ykxmz 没开?)";
}
if (lower.contains("network is unreachable")
|| lower.contains("no route")) {
return "手表没联网(检查 WiFi)";
}
if (lower.contains("eof") || lower.contains("reset")) {
return "连接被中断(网络不稳)";
}
if (text.length() > 80) {
return text.substring(0, 80);
}
return text;
}
/**
* 组合信任:系统默认 或 内置根证书,任一通过即认可。
*
* <p>这是修这个问题的关键 ——
* 老手表缺 GTS Root R4,但内置的那份有,于是校验通过;
* 新设备走系统默认照样通过。两边都不削弱安全性。
*/
private static final class CompatTrustManager implements X509TrustManager {
private final X509TrustManager system = findSystem();
private static X509TrustManager findSystem() {
try {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore) null); // null = 系统默认
for (TrustManager tm : tmf.getTrustManagers()) {
if (tm instanceof X509TrustManager) {
return (X509TrustManager) tm;
}
}
} catch (Throwable ignored) {
// 拿不到就算了,下面用内置的
}
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws java.security.cert.CertificateException {
if (system != null) {
system.checkClientTrusted(chain, authType);
return;
}
if (bundled != null) {
bundled.checkClientTrusted(chain, authType);
}
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws java.security.cert.CertificateException {
java.security.cert.CertificateException first = null;
if (system != null) {
try {
system.checkServerTrusted(chain, authType);
return;
} catch (java.security.cert.CertificateException error) {
first = error;
}
}
if (bundled != null) {
try {
bundled.checkServerTrusted(chain, authType);
return;
} catch (java.security.cert.CertificateException error) {
if (first == null) {
first = error;
}
}
}
throw (first != null ? first
: new java.security.cert.CertificateException(
"证书校验失败"));
}
@Override
public X509Certificate[] getAcceptedIssuers() {
X509Certificate[] a = system == null
? new X509Certificate[0] : system.getAcceptedIssuers();
X509Certificate[] b = bundled == null
? new X509Certificate[0] : bundled.getAcceptedIssuers();
X509Certificate[] all = new X509Certificate[a.length + b.length];
System.arraycopy(a, 0, all, 0, a.length);
System.arraycopy(b, 0, all, a.length, b.length);
return all;
}
}
}
'''
source = source.replace("%GTS%", pem_to_java(gts))
source = source.replace("%GS%", pem_to_java(globalsign))
OUT.parent.mkdir(parents=True, exist_ok=True)
OUT.write_text(source, encoding="utf-8", newline="\n")
print(f" 已生成 {OUT.name}({OUT.stat().st_size} 字节)")
print(f" 内置 GTS Root R4: {len(gts)} 字节 PEM")
print(f" 内置 GlobalSign Root CA: {len(globalsign)} 字节 PEM")
# 语法自检:括号配平 + 关键结构在
for needle in ("class TlsCompat", "CompatTrustManager",
"checkServerTrusted", "setSSLSocketFactory", "explain("):
print(f" 含 {needle}: {'✓' if needle in source else '✗'}")
return 0
if __name__ == "__main__":
sys.exit(main())