当前位置: 首页 > news >正文

安全攻防 - 04 GMSSL 工程介绍

文章目录

  • 04 工程全景:本项目每个类负责什么
  • 1. 工程一句话说明
  • 2. 目录结构
  • 3. 启动入口
  • 4. 配置层:`GmTlcpProperties`
    • 4.1 TLS 子配置
    • 4.2 客户端 KeyStore 子配置
  • 5. Provider 层:`KonaProviderInitializer`
  • 6. SSLContext 层:`TlcpSslContextFactory`
    • 6.1 证书路径解析
    • 6.2 TrustStore 构造
    • 6.3 SSLContext 构造
  • 7. Hostname 层:`ConfigurableHostnameVerifier`
  • 8. OkHttp 层:`TlcpOkHttpClientFactory`
    • 8.1 关卡 1:ConnectionSpec
    • 8.2 关卡 2:握手后协议名解析
  • 9. 诊断层:`TlcpHandshakeRecorder`
  • 10. 上传层:`DdFileUploadClient`
  • 11. Web 联调层
    • 11.1 Controller
    • 11.2 Request DTO
    • 11.3 Service
  • 12. 启动上传层:`StartupUploadRunner`
  • 13. 一张完整调用图
  • 14. 本篇小结

04 工程全景:本项目每个类负责什么

前面三篇讲概念。这一篇开始回到本工程。目标是让你打开代码时不迷路:哪个类负责配置,哪个类负责证书,哪个类负责 Provider,哪个类负责 OkHttp,哪个类负责发请求。


1. 工程一句话说明

本工程是一个:

Spring Boot 2.7.18 + Java 8 + Forest 1.8.0 + OkHttp 3.14.9 + Tencent Kona 1.0.20

组成的 TLCP 客户端示例。

它做两件事:

  1. 提供一个业务上传客户端DdFileUploadClient
  2. 提供一个 Web 联调页面,部署后从浏览器直接测试 TLCP 文件上传。

默认远端:

https://IP:port/context-path/v1/file/upload

默认本地调试页面:

http://<部署机>:9876/

2. 目录结构

核心文件:

src/main/java/com/artisan/gmssl/├──GmSslClientApplication.java ├── config/│ └──GmTlcpProperties.java ├── tlcp/│ ├──ConfigurableHostnameVerifier.java │ ├──KonaProviderInitializer.java │ ├──TlcpCertificateDetails.java │ ├──TlcpHandshakeRecorder.java │ ├──TlcpOkHttpClientFactory.java │ ├──TlcpProtocolMaskingSslSession.java │ ├──TlcpProtocolMaskingSslSocket.java │ ├──TlcpProtocolMaskingSslSocketFactory.java │ └──TlcpSslContextFactory.java └── upload/├──ContentMeta.java ├──DdFileUploadClient.java ├──InteractiveUploadRequest.java ├──InteractiveUploadService.java ├──StartupUploadRunner.java ├──UploadClientProperties.java └──UploadDebugController.java

资源文件:

src/main/resources/├── application.yml ├── certs/│ ├──ROOTCA.cer │ └──MiddleSM2CA.cer └──static/└── index.html

测试文件:

src/test/java/com/artisan/gmssl/├── tlcp/└── upload/

3. 启动入口

GmSslClientApplication.java是标准 Spring Boot 启动类:

@SpringBootApplication@EnableConfigurationProperties({GmTlcpProperties.class,UploadClientProperties.class})publicclassGmSslClientApplication{publicstaticvoidmain(String[]args){SpringApplication.run(GmSslClientApplication.class,args);}}

重点是启用了两个配置类:

配置类前缀作用
GmTlcpPropertiesgm.tlcp远端地址、TLCP 协议、证书、密码套件、hostname 校验
UploadClientPropertiesgm.upload启动时是否自动上传、默认文件路径、默认 Content-Meta

4. 配置层:GmTlcpProperties

这个类对应application.yml的:

gm:tlcp:scheme:httpshost:your-ipport:your-portupload-path:/context-path/v1/file/upload

它提供两个方便方法:

publicStringbaseUrl(){returnscheme+"://"+host+":"+port;}publicStringuploadUrl(){...returnbaseUrl()+path;}

也就是说业务代码不用到处拼 URL。

4.1 TLS 子配置

GmTlcpProperties.Tls里放 TLCP 相关参数:

privateStringprotocol="TLCPv1.1";privateList<String>enabledProtocols=["TLCPv1.1"];privateList<String>enabledCipherSuites=["TLCP_ECC_SM4_GCM_SM3","TLCP_ECC_SM4_CBC_SM3"];privateList<String>clientSignatureSchemes=["sm2sig_sm3"];privateList<String>trustCertificates=["classpath:certs/ROOTCA.cer","classpath:certs/MiddleSM2CA.cer"];privatebooleanhostnameVerificationEnabled=false;

这就是你接入不同服务端时最常改的地方。

4.2 客户端 KeyStore 子配置

privatefinalKeyStoreConfigclientKeyStore=newKeyStoreConfig();

默认没有 path,也就不启用客户端证书。

一旦你填了:

client-key-store:path:file:/secure/client.p12

TlcpSslContextFactory就会把它加载成 KeyManager。


5. Provider 层:KonaProviderInitializer

这个类负责两件事。

第一,设置 Kona 客户端签名算法:

com.tencent.kona.ssl.client.signatureSchemes=sm2sig_sm3

第二,注册三个 Provider:

KonaCryptoProvider.instance()KonaPKIXProvider.instance()KonaSSLProvider.instance()

它的设计细节:

  • @PostConstruct启动时执行;
  • initialize()是幂等的,重复调用不会重复注册;
  • TlcpSslContextFactory每次构造 SSLContext 前也会调用一次,防止单测或 Bean 顺序导致 Provider 没注册。

新手理解:没有这个类,后面SSLContext.getInstance("TLCPv1.1", "KonaSSL")很可能找不到 Provider。


6. SSLContext 层:TlcpSslContextFactory

这是 TLCP 接入核心类。

它做四件事:

1.读取信任证书2.生成TrustManager3.可选读取客户端KeyStore并生成KeyManager4.创建SSLContext("TLCPv1.1","KonaSSL")

6.1 证书路径解析

支持多种路径:

classpath:certs/ROOTCA.cer file:/secure/ROOTCA.cer/absolute/path/ROOTCA.cer relative/path/ROOTCA.cer

实现靠 Spring 的ResourceLoader,找不到时再回退到FileSystemResource

6.2 TrustStore 构造

核心逻辑:

KeyStoretrustStore=KeyStore.getInstance("JKS","KonaPKIX");CertificateFactorycf=CertificateFactory.getInstance("X.509","KonaPKIX");TrustManagerFactorytmf=TrustManagerFactory.getInstance("PKIX","KonaSSL");

每个 Provider 都有意义:

APIProvider为什么
KeyStoreKonaPKIX正确处理国密证书/密钥材料
CertificateFactoryKonaPKIX正确解析 SM2 X.509 证书
TrustManagerFactoryKonaSSL正确进行 TLCP/SM2 证书链校验
SSLContextKonaSSL创建 TLCP 协议机

6.3 SSLContext 构造

最终:

SSLContextsslContext=SSLContext.getInstance(contextProtocol,"KonaSSL");sslContext.init(keyManagers,trustManagers,newSecureRandom());

如果只是普通 HTTPS,你可能会写TLS。这里必须是TLCPv1.1+KonaSSL


7. Hostname 层:ConfigurableHostnameVerifier

这个类很小:

if(!properties.getTls().isHostnameVerificationEnabled()){returntrue;}returndelegate.verify(hostname,session);

它解决的是联调环境问题:很多内网证书没有 IP SAN,直接启用 hostname verification 会失败。

但你要记住:

  • 关闭 hostname verification 不等于关闭证书链校验;
  • 生产环境更推荐让证书 SAN 正确,然后开启它。

8. OkHttp 层:TlcpOkHttpClientFactory

普通 TLCP SSLContext 已经能握手,但 OkHttp 3.x 还会卡两道关。

8.1 关卡 1:ConnectionSpec

OkHttp 默认MODERN_TLS不认识TLCPv1.1TLCP_ECC_SM4_*

本工程手动构造:

ConnectionSpectlcpSpec=newConnectionSpec.Builder(ConnectionSpec.MODERN_TLS).tlsVersions(enabledProtocols.toArray(newString[0])).cipherSuites(enabledCipherSuites.toArray(newString[0])).build();

然后:

.connectionSpecs(Arrays.asList(tlcpSpec,ConnectionSpec.CLEARTEXT))

8.2 关卡 2:握手后协议名解析

OkHttp 3.x 握手完成后会读取:

session.getProtocol()

如果返回TLCPv1.1,它的枚举不认识,可能抛:

Unexpected TLS version: TLCPv1.1

本工程用三件套解决:

TlcpProtocolMaskingSslSocketFactory TlcpProtocolMaskingSslSocket TlcpProtocolMaskingSslSession

只把给 OkHttp 看的SSLSession.getProtocol()改成TLSv1.2,底层真实 TLCP 握手字节流不变。

这是本项目最关键的兼容设计之一。


9. 诊断层:TlcpHandshakeRecorder

联调页面需要告诉你发生了什么,而不是只给一个异常。

TlcpHandshakeRecorder记录:

字段含义
socketPreparationssocket 创建后实际启用的协议、密码套件
handshakes握手成功后的协议、密码套件、证书、session
hostnameCheckshostname verification 是否执行、结果是什么

它只用于诊断路径,也就是InteractiveUploadService


10. 上传层:DdFileUploadClient

这是业务代码真正可以注入使用的服务。

调用方式:

Stringresponse=uploadClient.upload(newFile("/root/a.txt"),newContentMeta("temp.txt",1140L,"/root/testfile","userxxxx"));

它内部:

校验文件和 Content-Meta ↓ 构造 TLCP OkHttpClient ↓ ForestConfiguration.post(properties.uploadUrl()) ↓ backendClient(okHttpClient) ↓ contentTypeMultipartFormData() ↓ addHeader("Content-Meta", contentMetaJson) ↓ addFile("file", file) ↓ executeAsString()

重点:它没有走 Forest 默认 SSL 管道,而是通过backendClient(okHttpClient)注入自定义 OkHttpClient。

这是绕过 OkHttp/Forest 默认 TLS 假设的关键。


11. Web 联调层

11.1 Controller

UploadDebugController提供:

GET / → static/index.html GET /api/tlcp/defaults → 返回默认表单参数 POST /api/tlcp/upload → 接收 multipart 文件并转发

11.2 Request DTO

InteractiveUploadRequest做三类事:

  1. 从默认配置生成表单默认值;
  2. 把逗号/换行分隔的协议、密码套件、证书路径拆成 List;
  3. 把表单参数转换成临时GmTlcpProperties.Tls

这让你可以在页面上临时改证书路径、密码套件、目标地址,而不需要改 jar 里的配置。

11.3 Service

InteractiveUploadService是 Web 联调核心:

应用默认值 ↓ 校验请求 ↓ 解析额外请求头 ↓ 构造临时TLS配置 ↓ 构造SSLContext/TrustManager/OkHttpClient↓ 把上传文件保存为临时文件 ↓Forest发 multipart 请求 ↓ 返回 request/tlcp/response/error/tlcpTrace

它返回的是一个结构化 JSON。排障时优先看这个 JSON,而不是只看控制台异常。


12. 启动上传层:StartupUploadRunner

默认不会启动就上传:

gm:upload:run-on-startup:false

这是安全设计,避免你本地mvn test或启动服务时自动请求内网远端。

如果确实要启动时发一次:

mvn spring-boot:run\-Dspring-boot.run.arguments="--gm.upload.run-on-startup=true --gm.upload.file=/root/a.txt"

13. 一张完整调用图

浏览器 index.html ↓POST/api/tlcp/uploadUploadDebugControllerInteractiveUploadService├─InteractiveUploadRequest.applyDefaults()├─TlcpSslContextFactory.buildSslContext()│ ├─KonaProviderInitializer.initialize()│ ├─buildTrustManagers()│ └─buildKeyManagers()├─TlcpHandshakeRecorder.socketFactory()├─TlcpOkHttpClientFactory.build()│ └─TlcpProtocolMaskingSslSocketFactory└─ForestRequest.backendClient(okHttpClient).executeAsResponse()↓ 远端TLCP服务

业务代码路径类似,只是从DdFileUploadClient开始,没有诊断 recorder。


14. 本篇小结

  • 配置集中在GmTlcpPropertiesapplication.yml
  • Provider 注册由KonaProviderInitializer负责。
  • 证书加载、TrustManager、KeyManager、SSLContext 由TlcpSslContextFactory负责。
  • OkHttp 兼容由TlcpOkHttpClientFactoryTlcpProtocolMaskingSsl*负责。
  • 业务上传走DdFileUploadClient
  • Web 联调走UploadDebugController+InteractiveUploadService+index.html
  • 出问题时按层找,不要所有问题都归咎于证书。

下一篇开始手把手准备证书、配置和启动。

http://www.jsqmd.com/news/900804/

相关文章:

  • 从‘退化因子’到‘健康指标’:给你的机器人状态估计做个‘体检’
  • ChatGPT销售话术优化:今天不重构话术逻辑,明天就被AI增强型竞品碾压——来自17家已部署企业的紧急预警
  • 网站渗透实操!从getshell到CVE提权,Linux最新内核也可提权!
  • Ambari 3.0+Kafka安全认证
  • 告别3D卷积!RAFT-Stereo如何用GRU迭代优化在Middlebury拿下第一?
  • 架构师的底层重构逻辑:面部松弛、纹路加深?用3大核心参数选对高阶胶原饮
  • 语言脑机接口解码流程对比【脑机接口恢复语言2】
  • 别让天线罩毁了你的毫米波雷达!从材料选择到壁厚计算,一份给硬件工程师的避坑指南
  • 灰子学Ai: Token与字节
  • STM32L0 LPUART串口卡死?别慌,HAL库ORE溢出错误的保姆级排查与修复指南
  • 告别纸上谈兵:用Wireshark抓包实战解析5G N2/NGAP切换全流程(附pcap文件)
  • 索引设计 实操SQL + 案例 + 练习
  • k8s-Prometheus的manifests 清单部署
  • 别再乱试了!用Wireshark精准定位微信/QQ通话IP的保姆级教程(附过滤语法)
  • 研一开学别慌!用这套保姆级YOLOv5实战路线,从零到跑通代码只要三个月
  • 保姆级教程:用Grad-CAM可视化Swin Transformer,看看你的模型到底在“看”哪里
  • 手机变Linux开发机:用Termux和MT管理器打造移动端代码编辑与文件管理环境
  • .NET + 消息队列:稳稳扛住百亿流水,这才是企业级架构的真正底气
  • sd卡病毒格式化文件怎么恢复正常,只需4种方法和视频演示轻松恢复数据
  • 如何高效使用AutoDingding实现钉钉自动打卡:终极实用指南
  • S32K3xx低功耗实战:用LPUART串口唤醒Standby模式,保姆级配置流程(基于Platform SDK 2022.03)
  • 第 3 篇:把 MCP 接入 AI,以及生态里有什么
  • STM32F1用HAL库驱动42步进电机:CubeMX配置PWM定时器(TIM3)保姆级教程
  • 从野外数据到地下构造:手把手教你用地震时距曲线做一次‘虚拟勘探’
  • Cadence SPB17.4 CIS库添加新元件失败?手把手教你排查‘找不到元件’的5个常见坑
  • AI品牌命名避坑清单(含12个高危词根、6类语音陷阱、4种文化禁忌),错过本次更新将影响全球市场准入
  • AI 助手类应用通用安全漏洞:间接提示注入可窃取企业敏感数据
  • 告别65535行限制:用QGIS一键把大型SHP文件导出为Excel表格
  • RK3566开发板GT911触屏调试避坑指南:从I2C检测到DTS配置的完整流程
  • 2026年 宝钢镀锌HC550/980DPD+Z双相钢厂家/供应商推荐榜:高强度与卓越成型性能的行业优选品牌 - 品牌企业推荐师(官方)