内存证书集合模式 (TlsCertMemory)
概述
内存证书集合模式是最灵活的 TLS 配置方式。证书数据不来自磁盘文件或 Windows 证书存储,而是由程序在运行时动态构造并保存在 VB6 Collection 对象中,直接传入 TLS 引擎。
函数签名
Public Function TlsCertMemory( _
ByVal Certificates As Collection, _
ByVal PrivateKey As Collection, _
Optional ByVal AlpnProtocols As String = "...") As <组件类型>参数
| 参数 | 类型 | 必要 | 说明 |
|---|---|---|---|
Certificates | Collection | 是 | 证书链集合,每个元素为 PEM 格式证书文本 |
PrivateKey | Collection | 是 | 私钥集合,每个元素为 PEM 格式私钥文本 |
AlpnProtocols | String | 否 | ALPN 协议协商。各组件默认值不同 |
| 组件 | AlpnProtocols 默认值 |
|---|---|
| cHttpServer | "http/1.1" |
| cWinsock | "http/1.1" |
| cWebSocketServer | ""(空) |
Collection 数据格式
Certificates 集合
每个元素是一段 PEM 编码的证书文本字符串:
Dim certs As New Collection
certs.Add "-----BEGIN CERTIFICATE-----" & vbCrLf & _
"MIIFazCCBFOgAwIBAgISA2Q3p..." & vbCrLf & _
"-----END CERTIFICATE-----"
' 中间证书(如有)
certs.Add "-----BEGIN CERTIFICATE-----" & vbCrLf & _
"MIIFazCCBFOgAwIBAgISA3B4q..." & vbCrLf & _
"-----END CERTIFICATE-----"顺序要求: 第一个元素必须是服务器证书(叶证书),后续为中间证书。
PrivateKey 集合
每个元素是一段 PEM 编码的私钥文本字符串:
Dim pkeys As New Collection
pkeys.Add "-----BEGIN PRIVATE KEY-----" & vbCrLf & _
"MIIEvgIBADANBgkqhkiG9w0BAQ..." & vbCrLf & _
"-----END PRIVATE KEY-----"支持的私钥格式:
-----BEGIN PRIVATE KEY-----(PKCS#8 无加密)-----BEGIN RSA PRIVATE KEY-----(PKCS#1 RSA)-----BEGIN EC PRIVATE KEY-----(EC)-----BEGIN ENCRYPTED PRIVATE KEY-----(PKCS#8 加密)
典型应用场景
1. 证书嵌入可执行文件
将证书作为资源编译进 EXE/DLL,运行时提取到 Collection,无需分发独立的 .pfx 文件。
' 从资源文件加载证书
Dim certs As New Collection
Dim pkeys As New Collection
certs.Add LoadResData(101, "CERT") ' 自定义资源类型
pkeys.Add LoadResData(102, "KEY")
Server.TlsCertMemory(certs, pkeys).Start 443优势:
- 部署简单,只需一个 EXE 文件
- 证书不暴露在磁盘上(一定程度上增加安全性)
- 不依赖外部文件路径
局限:
- 证书更新需要重新编译
- EXE 体积增大
2. ACME / Let's Encrypt 自动签发
程序运行时通过 ACME 协议自动申请证书,获取后直接存内存,无需写盘。
' ACME 签发流程(伪代码)
Dim certs As New Collection
Dim pkeys As New Collection
' 1. 创建账户
AcmeClient.Register "admin@example.com"
' 2. 验证域名所有权(HTTP-01 或 DNS-01)
AcmeClient.VerifyDomain "www.example.com"
' 3. 签发证书
Dim pemCert As String
Dim pemKey As String
pemCert = AcmeClient.IssueCertificate("www.example.com", pemKey)
' 4. 加载到 Collection
certs.Add pemCert
pkeys.Add pemKey
' 5. 启动 HTTPS
Server.TlsCertMemory(certs, pkeys).Start 443优势:
- 全自动,无需人工干预
- 证书不落盘,减少泄露风险
- 可实现定时自动续期
3. 动态 SNI 路由
根据客户端请求的域名(SNI),从数据库或配置中心动态加载对应证书。
' 概念示例:多域名 HTTPS 服务器
' 注意:当前 cTlsSocket 在 Listen 阶段初始化证书,不支持单端口多证书
' 多域名场景建议使用通配符证书或为每个域名创建独立的 cHttpServer 实例
Dim certs As New Collection
Dim pkeys As New Collection
' 从配置数据库加载指定域名的证书
Dim rs As Recordset
Set rs = DB.Execute("SELECT cert_pem, key_pem FROM certs WHERE domain='www.example.com'")
certs.Add rs!cert_pem
pkeys.Add rs!key_pem
Server.TlsCertMemory(certs, pkeys).Start 4434. 测试场景
程序自签发临时证书用于开发和测试。
' 使用 OpenSSL 命令行预生成测试证书,或用 COM 组件在运行时生成
Dim certs As New Collection
Dim pkeys As New Collection
' 假设 TestCertGenerator 是自定义的证书生成类
Dim gen As New TestCertGenerator
gen.CommonName = "localhost"
gen.ValidDays = 1
certs.Add gen.GenerateCertificate()
pkeys.Add gen.GeneratePrivateKey()
Server.TlsCertMemory(certs, pkeys).Start 4435. 从文件加载到内存
将文件内容读入 Collection,实现延迟加载或缓存控制。
Private Function LoadPemToCollections( _
ByVal CertPaths As String, _
ByVal KeyPath As String) As Boolean
Dim certs As New Collection
Dim pkeys As New Collection
' 加载证书(支持 | 分隔多文件)
Dim arrCerts() As String
arrCerts = Split(CertPaths, "|")
Dim i As Long
For i = 0 To UBound(arrCerts)
certs.Add ReadTextFile(Trim(arrCerts(i)))
Next
' 加载私钥
pkeys.Add ReadTextFile(KeyPath)
' 使用
Server.TlsCertMemory certs, pkeys
LoadPemToCollections = True
End Function
Private Function ReadTextFile(ByVal Path As String) As String
Dim f As Integer
f = FreeFile
Open Path For Input As #f
ReadTextFile = Input$(LOF(f), f)
Close #f
End Function各组件使用示例
cHttpServer(HTTPS)
Dim certs As New Collection
Dim pkeys As New Collection
' 从数据库加载
certs.Add DB_GetCertPem("www.example.com")
pkeys.Add DB_GetKeyPem("www.example.com")
Server.TlsCertMemory(certs, pkeys).WebRoot("C:\www").Start 443cWinsock(TLS TCP 服务端)
Dim certs As New Collection
Dim pkeys As New Collection
certs.Add LoadFromResource(101)
pkeys.Add LoadFromResource(102)
Dim svr As New cWinsock
svr.TlsCertMemory(certs, pkeys).Listen 443cWebSocketServer(wss://)
Dim certs As New Collection
Dim pkeys As New Collection
certs.Add GetCertFromConfig()
pkeys.Add GetKeyFromConfig()
Dim wsSvr As New cWebSocketServer
wsSvr.TlsCertMemory(certs, pkeys).Listen 443内存中的 PEM 文本格式
Collection 中的每个元素必须是完整的 PEM 块,包含起止标记:
✅ 正确:完整的 PEM 块
-----BEGIN CERTIFICATE-----
MIIFazCCBFOgAwIBAgISA2Q3p...
(Base64 内容,可含换行)
-----END CERTIFICATE-----
❌ 错误:仅 Base64 内容,无 PEM 标记
MIIFazCCBFOgAwIBAgISA2Q3p...
❌ 错误:DER 二进制数据(字节数组)换行符: PEM 文本中的换行可以是 vbCr、vbLf 或 vbCrLf,底层 OpenSSL 均能识别。
底层处理流程
' cTlsSocket.InitServerTls 内部
If pvCollectionCount(Certificates) > 0 And pvCollectionCount(PrivateKey) > 0 Then
Set cCerts = Certificates
Set cPrivKey = PrivateKey
GoTo StartTls
End IfTlsCertMemory(certs, pkeys)
│
└─ InitServerTls(Certificates:=certs, PrivateKey:=pkeys)
│
├─ 检查 Certificates.Count > 0 And PrivateKey.Count > 0
│
└─ TlsInitServer(ctx, hostname, cCerts, cPrivKey, alpn, features)
│
├─ 遍历 Certificates 集合
│ └─ PEM_read_bio_X509() → 逐一解析证书
│
├─ 遍历 PrivateKey 集合
│ └─ PEM_read_bio_PrivateKey() → 解析私钥
│
└─ SSL_CTX_use_certificate() + SSL_CTX_use_PrivateKey()安全注意事项
1. 内存中的私钥保护
私钥以明文 PEM 文本形式存储在 Collection 中,在进程内存中可被调试器读取。
建议:
- 使用后尽快清除 Collection 和字符串变量
- 避免在日志中输出私钥内容
- 考虑使用 Windows DPAPI 加密存储的私钥文本
' 使用后清除
Set certs = Nothing
Set pkeys = Nothing2. 证书来源验证
从外部来源(数据库、API、配置中心)加载证书时,需验证数据的完整性和来源。
建议:
- 使用 TLS 连接获取证书数据
- 对配置数据进行签名验证
- 限制数据库访问权限
3. 内存限制
大量证书或超大证书链会占用较多内存。
建议:
- 只加载必要的证书链
- 避免在 Collection 中存储冗余的中间证书
- 及时释放不再使用的 Collection
与其他模式对比
| 特性 | TlsCertFile | TlsCertSubject | TlsCertMemory |
|---|---|---|---|
| 证书来源 | 磁盘文件 | Windows 证书库 | 内存 Collection |
| 部署便利 | 文件分发 | 系统导入 | 嵌入/动态加载 |
| 证书更新 | 替换文件 | 续期到存储 | 重新加载 |
| 私钥保护 | 文件系统权限 | 操作系统保护 | 进程内存 |
| 运行时生成 | 不支持 | 不支持 | 支持 |
| ACME 自动签发 | 需写盘 | 需导入 | 天然支持 |
| 资源嵌入 | 不支持 | 不支持 | 支持 |
| 部署复杂度 | 低 | 中 | 高 |
| 适用场景 | 生产部署 | 企业共享 | 动态/嵌入式 |
证书来源优先级
在 cTlsSocket.InitServerTls 中,三种证书来源的检测优先级:
- 内存集合 (
Certificates+PrivateKey) — 最高优先级 - 证书文件 (
CertFile) - Windows 证书存储 (
CertSubject) — 最低优先级
如果同时传入多个来源参数,只有优先级最高的生效。
常见问题
1. PEM 格式错误
LastError: 证书文件不存在或格式错误解决: 确保 Collection 中的每个元素是完整的 PEM 块,包含 -----BEGIN...----- 和 -----END...----- 标记。
2. 证书与私钥不匹配
TLS 握手失败解决: 确保证书和私钥是配对的。可以用 OpenSSL 验证:
openssl x509 -noout -modulus -in cert.pem | openssl md5
openssl rsa -noout -modulus -in key.pem | openssl md5
# 两个 MD5 值应相同3. 证书顺序错误
客户端报证书链不完整解决: Certificates 集合中第一个元素必须是服务器证书(叶证书),后续是中间证书。
4. Collection 为空
InitServerTls 跳过内存证书,尝试其他来源解决: 确保 Certificates.Count > 0 且 PrivateKey.Count > 0,否则底层会跳过内存模式。
相关文档
最后更新: 2026-06-09