双向tls
认证
定义
双向tls
是指在进行https认证的时候服务器和客户端都会彼此进行证书认证, 需要使用一个ca对客户端和服务端进行证书签发. 本次项目实现会使用Flask来进行部署
后端代码
1 | from flask import Flask, request, g, jsonify |
证书生成
常规部署
- 必须共享的文件:
- CA公钥证书(ca.crt) - 需要分发给所有验证方
- 签发好的终端证书(server.crt/client.crt) - 给对应设备使用
- 必须保密的内容:
- CA私钥(
ca.key
) - 一旦泄露整个信任体系崩溃 - 所有终端私钥(
server.key
/client.key
)
- CA私钥(
多设备部署实例
假设有三台机器:
- CA服务器(安全环境)
- 生成:
ca.key
+ca.crt
- 只分发ca.crt给其他机器
- 生成:
- Web服务器
- 生成:
server.key
+server.csr
- 将
server.csr
发送给CA签发 - 获得:
server.crt
- 最终保留:
server.key
+server.crt
+ca.crt
- 生成:
- 客户端设备
- 生成:
client.key
+client.csr
- 将
client.csr
发送给CA签发 - 获得:
client.crt
- 最终保留:
client.key
+client.crt
+ca.crt
- 生成:
概念分类
扩展名 | 类型 | 格式 | 包含内容 | 典型用途 |
---|---|---|---|---|
.key |
私钥 | PEM | 私钥 | 服务器配置 |
.crt/.cer |
证书 | PEM/DER | 公钥+CA签名 | HTTPS 服务端证书 |
.csr |
证书签名请求 | PEM | 公钥+身份信息 | 向 CA 申请证书 |
.pem |
通用容器 | PEM | 证书/私钥/公钥 | Unix 系统通用格式 |
.der |
二进制证书/密钥 | DER | 二进制编码数据 | Windows 系统 |
.pfx |
密钥库 | PKCS#12 | 私钥+证书+证书链 | IIS/Java 应用 |
.jks |
Java 密钥库 | 二进制 | 私钥+证书(Java 专用) | Tomcat/Java 应用 |
.crl |
证书吊销列表 | PEM/DER | 吊销的证书序列号 | 安全策略实施 |
key 私钥文件
格式通常为: key
/pem
/der
, key
和pem
的区别较小,但是仍然存在区别
特性 | .key 文件 |
.pem 文件 |
---|---|---|
主要用途 | 通常专门存储私钥 | 通用容器,可存储证书/私钥/公钥等 |
内容范围 | 仅私钥(多数情况) | 可能包含证书、私钥、公钥或组合 |
文件结构 | 无强制要求,但通常为 PEM 格式 | 必须有 BEGIN/END 标签 |
典型内容 | -----BEGIN PRIVATE KEY----- |
可能是私钥、证书或两者 |
特性 | PEM 格式 | DER 格式 | PKCS#12 (.pfx ) |
---|---|---|---|
编码方式 | Base64 文本(同key) + ASCII 标签 | 二进制 ASN.1 | 二进制 PKCS#12 结构 |
可读性 | 可用文本编辑器查看 | 二进制乱码 | 二进制乱码 |
文件大小 | 较大(Base64 膨胀约 33%) | 较小 | 中等(含多个对象) |
密码保护 | 可加密(单独私钥) | 不支持 | 必须设置密码 |
系统兼容性 | Unix/Linux/Web 服务器 | Windows/Java | Windows IIS/Java Keystore |
典型扩展名 | .pem , .crt , .key |
.der , .cer |
.pfx , .p12 |
多对象支持 | 可拼接多个证书/密钥 | 单对象文件 | 支持多证书+私钥打包 |
证书签发
1 | 生成 CA 私钥和根证书 |
1 | 生成服务器证书 |
1 | 生成客户端证书 |
生成 CA 私钥和根证书
生成 CA 私钥
1 | openssl genrsa -out ca.key 2048 |
参数 | 作用 |
---|---|
genrsa |
生成 RSA 私钥的命令 |
-out |
指定输出文件名(此处为 ca.key ) |
2048 |
密钥长度(位),2048 是当前安全标准的最小推荐值 |
生成自签名根证书
1 | openssl req -new -x509 -days 365 -key ca.key -out ca.crt -subj "/CN=MyCA" |
参数 | 作用 |
---|---|
req |
证书请求命令 |
-new |
创建新请求 |
-x509 |
直接输出 X.509 证书(用于创建自签名证书) |
-days 365 |
证书有效期(天) |
-key |
指定私钥文件(用于签名证书) |
-out |
输出证书文件 |
-subj |
指定证书主题(Subject),/CN=MyCA 设置通用名(CA 的标识名) |
生成服务器证书
生成服务器私钥
1 | openssl genrsa -out server.key 2048 |
(参数同 CA 私钥生成)
创建证书签名请求 (CSR)
1 | openssl req -new -key server.key -out server.csr -subj "/CN=localhost" |
参数 | 作用 |
---|---|
-key |
指定服务器私钥文件 |
-out |
输出 CSR 文件 |
-subj |
设置服务器标识,/CN=localhost 表示该证书用于 localhost 域名 |
用 CA 签发服务器证书
1 | openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365 |
参数 | 作用 |
---|---|
x509 |
证书操作命令 |
-req |
输入是 CSR 文件 |
-in |
指定输入的 CSR 文件 |
-CA |
指定 CA 的证书文件 |
-CAkey |
指定 CA 的私钥文件 |
-CAcreateserial |
自动创建/更新序列号文件(ca.srl ,用于避免证书序列号重复) |
-out |
输出签发的证书文件 |
-days 365 |
证书有效期 |
生成客户端证书
生成客户端私钥
1 | openssl genrsa -out client.key 2048 |
创建客户端 CSR
1 | openssl req -new -key client.key -out client.csr -subj "/CN=client" |
参数 | 关键区别 |
---|---|
-subj |
此处 /CN=client 表示这是客户端证书,标识客户端身份(不同于服务器证书) |
用 CA 签发客户端证书
1 | openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365 |
(参数与服务器证书签发完全相同)
关键参数深度解析
关于 -subj
字段
完整的 Subject 可包含更多信息(用 /
分隔):
1 | -subj "/C=CN/ST=Beijing/L=Haidian/O=My Company/OU=Dev/CN=example.com" |
字段 | 含义 | 示例值 |
---|---|---|
C |
国家(Country) | CN |
ST |
州/省(State) | Beijing |
L |
城市(Locality) | Haidian |
O |
组织(Organization) | My Company |
OU |
部门(Organizational Unit) | Dev |
CN |
通用名(Common Name) | example.com |
关于 -CAcreateserial
首次运行时会创建
ca.srl
文件(包含十六进制序列号,如01
)后续签发证书时序列号会自动递增,OpenSSL 会读取并更新其中的值。
.srl
文件是 证书序列号文件(Serial Number File),主要用于 OpenSSL 在签发证书时跟踪已使用的序列号,避免重复。以下是详细解析:.srl
的作用- 记录序列号:存储一个十六进制数值(如
00
或随机数),作为证书的唯一标识。 - 防重复:每次签发新证书时,OpenSSL 会读取
.srl
中的值并递增,确保每个证书的序列号唯一。 - 场景:在 CA(证书颁发机构) 或自签名证书过程中自动生成。
- 记录序列号:存储一个十六进制数值(如
也可手动指定序列号文件:
1
-CAserial ca.srl
注意事项
文件位置:
通常与 CA 证书(
ca.crt
)和私钥(ca.key
)放在同一目录。可通过
-CAserial
指定自定义路径。
手动修改风险:
- 直接编辑
.srl
可能导致序列号冲突(如重复或跳跃),建议由 OpenSSL 自动管理。
- 直接编辑
非必要文件:
- 如果仅签发少量证书,可用
-CAcreateserial
替代,无需长期维护.srl
。
- 如果仅签发少量证书,可用
关于密钥长度(2048)
- 2048-bit RSA:当前最低安全标准(有效期建议 ≤ 3 年)
- 3072/4096-bit:更高安全性(但性能略有下降)
- 过时的 1024-bit:已不安全,应避免使用
常见问题解答
Q1: 为什么需要 -x509
参数生成 CA 证书?
A1: -x509
表示直接生成自签名证书(CA 证书不需要被其他机构签名)。
Q2: -CAcreateserial
不写会怎样?
A2: 必须手动通过 -set_serial
指定序列号,否则报错(自动管理更安全方便)。
Q3: 证书有效期可以超过 1 年吗?
A3: 可以(如 -days 3650
表示 10 年),但超过 825 天的证书会被 Chrome/Firefox 标记为不安全。
Q4: 如何查看生成的证书内容?
1 | openssl x509 -in server.crt -text -noout |
掌握这些参数后,您可以根据实际需求灵活调整证书生成流程!
证书链签发模拟
在 HTTPS 中签发证书链(Certificate Chain),类似于学校层级(校长 → 学院 → 导员 → 学生),本质是构建一个**层级化的 PKI(公钥基础设施)**体系。以下是具体步骤和实现方法:
1. 证书链的核心逻辑
层级关系:上级证书(CA)签发下级证书,形成信任链。
示例结构
:
1 | 根CA(自签名,校长) → 中间CA(学院) → 中间CA(导员) → 终端证书(学生) |
- 验证流程:浏览器会从终端证书逐级回溯验证,直到信任的根CA。
2. 具体操作步骤(以 OpenSSL 为例)
步骤 1:创建根CA(校长)
bash
复制
1 | # 生成根CA私钥 |
- 关键参数:
-x509
表示自签名,根CA无需被其他证书签发。
步骤 2:创建中间CA(学院)
bash
复制
1 | # 生成中间CA私钥 |
步骤 3:创建二级中间CA(导员)
bash
复制
1 | # 生成导员CA私钥 |
步骤 4:签发终端证书(学生)
1 | # 生成学生私钥 |
3. 证书链的合并与部署
合并证书链(PEM 格式)
1 | cat student.crt teacherCA.crt collegeCA.crt > chain.crt |
- 作用:将终端证书和所有中间CA证书按顺序合并,供服务器(如Nginx)使用。
Nginx 配置示例
1 | server { |
验证证书链有效性
1 | openssl verify -CAfile rootCA.crt -untrusted chain.crt student.crt |
- 输出
OK
表示链完整且可被信任。
4. 关键注意事项
根CA的保密性
- 根CA私钥(
rootCA.key
)必须离线存储,仅用于签发中间CA。
- 根CA私钥(
中间CA的生命周期
- 中间CA证书的有效期应短于根CA(如根CA10年,中间CA5年)。
证书吊销列表(CRL/OCSP)
层级化CA需配置吊销机制,防止下级证书被滥用:
1
2# 生成CRL(证书吊销列表)
openssl ca -gencrl -keyfile rootCA.key -cert rootCA.crt -out rootCA.crl
浏览器信任问题
- 自签名的根CA需手动导入到操作系统/浏览器的信任存储中,否则会显示警告。
5. 实际应用场景
学校内网HTTPS
- 校长(根CA):签发学院CA证书。
- 学院(中间CA):签发各系/导员的子CA证书。
- 导员(终端CA):为学生或设备签发终端证书。
- 学生:使用证书登录内网系统或加密通信。
企业级PKI
- 类似逻辑可扩展至企业部门、云服务多租户等场景。
总结
- 层级化签发:通过根CA → 中间CA → 终端证书构建信任链。
- 核心工具:OpenSSL 的
req
、x509
和ca
命令。 - 安全要点:保护根CA私钥、控制中间CA权限、合并完整证书链。
通过这种结构,可以模拟学校层级的证书签发体系,同时满足HTTPS对证书链的验证需求。
通过命令生成的证书导入不同平台
1 | 生成客户端证书 |
明白了!你只有通过以下命令生成的 client.key
(私钥) 和 client.crt
(证书),没有 .p12
或 .pfx
文件。以下是直接使用 .crt
+ .key
在不同平台导入的方法:
1. 通用准备
将证书和私钥合并为 PEM 格式(部分平台需要):
1 | cat client.crt client.key > client.pem |
2. 各平台导入方法
Windows
方法1:手动导入 .crt
和 .key
- 导入证书(
.crt
)- 双击
client.crt
→ 选择 本地计算机 或 当前用户 → 存储位置选 个人。
- 双击
- 关联私钥(
.key
)- 需要将
.key
转换为.pfx
(见下文),或通过代码(如C#)直接加载文件。
- 需要将
方法2:转换为 .pfx
再导入(推荐)
bash
复制
1 | openssl pkcs12 -export -in client.crt -inkey client.key -out client.pfx -password pass:123456 |
- 双击生成的
client.pfx
,按向导导入即可。
Linux
方法1:直接使用文件(适用于命令行工具)
curl/wget
1
curl --cert client.crt --key client.key https://your-server.com
Nginx/Apache:在配置中指定路径:
1
2ssl_certificate /path/to/client.crt;
ssl_certificate_key /path/to/client.key;
方法2:系统级信任(可选)
1 | # 将CA证书(ca.crt)加入系统信任 |
Android
方法1:通过代码加载(适用于App开发)
kotlin
1 | // 加载 PEM 格式的证书和私钥 |
方法2:转换为 .p12
后导入
1 | openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 -password pass:123456 |
- 将
client.p12
传输到手机 → 进入 设置 → 安全 → 从存储设备安装。
iOS/macOS
方法1:邮件或AirDrop发送 .pem
- 将
client.pem
发送到设备 → 点击附件安装。 - 输入密码(如果有)→ 信任证书(需在 设置 → 通用 → 关于 → 证书信任设置 中启用)。
方法2:转换为 .p12
后导入
1 | openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 -password pass:123456 |
- 通过邮件发送
.p12
→ 在iOS上点击安装。
3. 关键注意事项
- 私钥保护:
.key
文件必须严格保密,建议转换为密码保护的.p12
后再分发。
- 格式兼容性:
- Windows/iOS 更依赖
.pfx
/.p12
,Linux/Android 可直接使用.crt
+.key
。
- Windows/iOS 更依赖
- CA信任:
- 确保设备的信任存储中已导入你的
ca.crt
(根证书),否则会报错。
- 确保设备的信任存储中已导入你的
4. 验证是否导入成功
Windows:运行
certmgr.msc
,查看 个人 证书列表。Linux:使用
1
openssl
命令测试:
1
openssl s_client -connect your-server:443 -cert client.crt -key client.key -CAfile ca.crt
iOS/Android:尝试访问HTTPS服务,检查是否弹出证书选择框。
总结
- 最佳实践:优先转换为
.p12
/.pfx
格式(密码保护),兼容所有平台。 - 直接使用场景:Linux/Android 可直接操作
.crt
+.key
文件。 - 安全提醒:私钥(
.key
)绝不能明文传输!
burp导入的证书
** 根证书(CA Certificate)**
- 文件格式:
.cer
、.pem
、.der
(Burp Suite 默认导出.cer
或.pem
) - 作用:
- Burp Suite 会生成一个 自签名的根CA证书(如
PortSwigger CA
)。 - 设备信任该证书后,Burp Suite 可以动态签发任意域名的 伪造证书(用于解密HTTPS流量)。
- Burp Suite 会生成一个 自签名的根CA证书(如
- 如何获取:
- Burp Suite:访问
http://burpsuite
→ 下载cacert.der
或cacert.pem
。 - Fiddler:访问
http://127.0.0.1:8888
→ 下载FiddlerRoot.cer
。
- Burp Suite:访问
** 动态生成的站点证书**
生成方式:
- 当你访问
https://example.com
时,Burp Suite 会用它的根CA签发一个伪造的example.com
证书。 - 该证书的 公钥 由 Burp Suite 控制,因此可以解密流量。
- 当你访问
证书链:
1
伪造的 example.com 证书 ← Burp Suite 根CA证书
如何绕过服务器对burp等mitm代理检测
1. 证书固定(Certificate Pinning)
原理
- 服务器或客户端(如App)会预先存储合法的证书公钥或CA信息,仅信任特定的证书链。
- 当你使用Burp Suite代理时,Burp会动态生成伪造的证书(由
PortSwigger CA
签发),但客户端/服务器发现证书不匹配,就会拒绝连接。
如何检测
HTTP公钥固定(HPKP)(已弃用,但部分系统仍支持):
1
Public-Key-Pins: pin-sha256="base64=="; max-age=5184000
Android/iOS App 内置证书:
- 许多App(如银行、支付类)硬编码了合法证书的指纹,Burp的伪造证书无法通过验证。
绕过方法(仅限授权测试)
- Android:使用
Frida
或Objection
钩子(Hook)证书检查逻辑。 - iOS:越狱后使用
SSL Kill Switch
禁用证书固定。 - 浏览器:手动删除HPKP头或修改HSTS策略(Chrome DevTools)。
2. TLS指纹检测(JA3/JA3S)
原理
- Burp Suite 的TLS握手行为(如支持的加密套件、扩展)与正常浏览器/客户端不同,服务器可以通过 JA3指纹 识别异常。
- 例如:
- Burp默认使用
TLS_RSA_WITH_AES_128_CBC_SHA
(较弱的套件)。 - 现代浏览器使用
TLS_AES_128_GCM_SHA256
(TLS 1.3)。
- Burp默认使用
如何检测
- 服务器分析客户端的TLS握手包,匹配已知代理工具的指纹库(如Burp、Fiddler、Charles)。
绕过方法
修改Burp的TLS指纹:
- 使用插件(如
Bypass TLS Fingerprinting
)模拟浏览器指纹。 - 手动配置Burp的TLS套件(需修改Burp源码)。
- 使用插件(如
使用真实浏览器代理:
- 通过
Chrome + SwitchyOmega
转发流量到Burp,保留浏览器原生TLS行为。
- 通过
3. HTTP头检测
原理
- Burp Suite 默认会在请求中添加或修改某些HTTP头,例如:
Via: 1.1 burp
(显式暴露代理)。X-Forwarded-For: 127.0.0.1
(可能被服务器标记)。
- 服务器检查这些头字段,发现异常后拦截请求。
如何检测
1 | GET / |
绕过方法
Burp Suite 配置:
- 进入
Proxy → Options → Match and Replace
,删除或修改敏感头字段。
- 进入
禁用
Support invisible proxying
避免自动添加头。
4. IP/行为异常检测
原理
- IP信誉库:服务器发现请求来自已知代理/VPN IP(如Burp默认的
127.0.0.1:8080
)。 - 请求频率异常:Burp的自动化扫描工具(如Intruder)会发送大量请求,触发风控。
如何检测
- 云服务商(如Cloudflare)会标记代理IP并返回
403 Forbidden
。 - 服务器监控请求速率,异常流量触发CAPTCHA或封禁。
绕过方法
- 使用真实IP:关闭Burp的全局代理,仅针对特定进程代理(如
Proxifier
)。 - 降低请求频率:在Burp的Intruder中设置延迟(
Options → Request Throttle
)。
5. HSTS(HTTP严格传输安全)
原理
- 服务器响应头包含
Strict-Transport-Security
,强制浏览器仅通过HTTPS访问。 - 如果Burp的证书不受信任,浏览器会直接拒绝连接(无法降级到HTTP)。
如何检测
1 | Strict-Transport-Security: max-age=31536000; includeSubDomains |
绕过方法
清除浏览器HSTS缓存:
- Chrome访问
chrome://net-internals/#hsts
,删除域名记录。
- Chrome访问
使用自定义Hosts文件:
- 将目标域名解析到本地,强制HTTP访问(仅限测试环境)。
总结:为什么服务器能发现Burp代理?
检测手段 | 原理 | 绕过方法 |
---|---|---|
证书固定 | 客户端/服务器校验证书指纹 | 修改客户端代码(Frida/Objection) |
TLS指纹 | 分析TLS握手行为(JA3/JA3S) | 模拟浏览器指纹或使用真实浏览器代理 |
HTTP头标记 | 检测 Via 、X-Forwarded-For 等头 |
删除或替换敏感头字段 |
IP/行为风控 | 代理IP或异常请求频率触发拦截 | 降低扫描速率或使用真实IP |
HSTS | 强制HTTPS,阻止MITM降级攻击 | 清除HSTS缓存或本地Hosts劫持 |
防御MITM代理
1. 检测方法
(1) 检查客户端证书链(Certificate Chain)
原理
- 如果客户端信任了Burp的根证书(如
PortSwigger CA
),它在TLS握手时会接受Burp签发的伪造证书。 - 钓鱼服务器可以检查客户端提交的证书链,如果发现证书由 Burp Suite 的CA签发(而非合法CA如 Let’s Encrypt、DigiCert),则可判定存在代理。
实现方式
服务端代码示例(Node.js):
1
2
3
4
5
6
7
8
9
10
11
12
13const tls = require('tls');
const server = tls.createServer({
key: serverKey,
cert: serverCert,
requestCert: true, // 要求客户端证书
rejectUnauthorized: false // 不强制验证客户端证书
}, (socket) => {
const clientCert = socket.getPeerCertificate();
if (clientCert.issuer.CN === 'PortSwigger CA') {
console.log('[!] 检测到Burp证书:', clientCert.subject.CN);
}
});
server.listen(443);
(2) 检查TLS指纹(JA3/JA3S)
原理
- Burp Suite 的TLS握手行为(如加密套件、扩展字段)与正常浏览器/客户端不同,可通过 JA3指纹 识别。
- 钓鱼服务器可以比对客户端的TLS指纹是否匹配已知代理工具(如Burp、Fiddler)。
实现方式
使用开源库(如
ja3
)计算指纹:1
2
3
4
5from ja3 import JA3
# 捕获客户端TLS握手包,计算JA3指纹
ja3_hash = JA3().calculate(packet)
if ja3_hash in KNOWN_BURP_JA3_HASHES:
print("[!] 检测到Burp代理")
(3) 检查HTTP请求头
原理
- Burp Suite 默认会在HTTP请求中添加某些头字段(如
Via: 1.1 burp
),或修改/删除正常浏览器的头(如User-Agent
)。 - 钓鱼服务器可检查这些异常标记。
检测逻辑
1 | if "Via" in request.headers and "burp" in request.headers["Via"]: |
(4) 证书透明度日志(Certificate Transparency, CT)
原理
- 合法CA签发的证书会公开记录在 CT日志(如 crt.sh)。
- Burp Suite 的伪造证书不会出现在CT日志中,服务器可通过查询证书的序列号或域名判断是否为伪造。
实现方式
1 | # 查询证书是否在CT日志中(例如使用 crt.sh API) |
(5) 证书公钥指纹比对
原理
- 钓鱼服务器可预先存储目标域名的 合法证书公钥指纹(如SHA-256)。
- 如果客户端提交的证书公钥指纹与合法证书不匹配,则可能被代理篡改。
代码示例
python
复制
1 | import hashlib |
2. 防御者的应对措施
如果攻击者(你)的钓鱼服务器检测到Burp证书,可以采取以下策略:
- 记录并阻断:记录IP或会话,后续请求直接返回错误(如
403 Forbidden
)。 - 返回虚假数据:向Burp用户返回误导性信息(如伪造的登录页面)。
- 触发警报:通知安全团队有测试人员/攻击者在探测系统。
3. 如何绕过检测?(攻击者视角)
如果攻击者希望避免被钓鱼服务器检测到Burp证书,可尝试:
禁用Burp的CA证书:
- 在Burp Suite 中关闭
Proxy → Options → TLS Pass Through
,避免拦截特定域名。
- 在Burp Suite 中关闭
使用真实浏览器代理:
- 通过
Chrome + SwitchyOmega
转发流量到Burp,保留原生TLS指纹。
- 通过
修改TLS指纹:
- 使用插件(如
Bypass TLS Fingerprinting
)模拟浏览器行为。
- 使用插件(如
删除敏感HTTP头:
- 在Burp的
Match and Replace
规则中删除Via
、X-Forwarded-For
等头。
- 在Burp的
总结
检测手段 | 攻击者如何绕过 |
---|---|
检查证书链(Burp CA签发) | 禁用Burp拦截或使用合法证书 |
TLS指纹(JA3/JA3S) | 模拟浏览器指纹或使用真实浏览器代理 |
HTTP头(如 Via: burp ) |
删除或修改敏感头字段 |
证书透明度(CT日志) | 无法绕过(需使用合法CA证书) |
公钥指纹比对 | 使用目标域名的真实证书(如SSL剥离攻击) |
关键结论
- Burp Suite 的证书容易被检测:因其CA名称、TLS指纹、HTTP头等特征明显。
- 高安全目标系统(如银行):通常会启用证书固定+JA3检测,Burp直接拦截会失败。
- 隐蔽性建议:尽量模拟合法流量,或使用更底层的流量劫持工具(如
mitmproxy
+ 自定义TLS栈)。