Skip to content

内存证书集合模式 (TlsCertMemory)

概述

内存证书集合模式是最灵活的 TLS 配置方式。证书数据不来自磁盘文件或 Windows 证书存储,而是由程序在运行时动态构造并保存在 VB6 Collection 对象中,直接传入 TLS 引擎。

函数签名

vb
Public Function TlsCertMemory( _
    ByVal Certificates As Collection, _
    ByVal PrivateKey As Collection, _
    Optional ByVal AlpnProtocols As String = "...") As <组件类型>

参数

参数类型必要说明
CertificatesCollection证书链集合,每个元素为 PEM 格式证书文本
PrivateKeyCollection私钥集合,每个元素为 PEM 格式私钥文本
AlpnProtocolsStringALPN 协议协商。各组件默认值不同
组件AlpnProtocols 默认值
cHttpServer"http/1.1"
cWinsock"http/1.1"
cWebSocketServer""(空)

Collection 数据格式

Certificates 集合

每个元素是一段 PEM 编码的证书文本字符串:

vb
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 编码的私钥文本字符串:

vb
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 文件。

vb
' 从资源文件加载证书
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 协议自动申请证书,获取后直接存内存,无需写盘。

vb
' 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),从数据库或配置中心动态加载对应证书。

vb
' 概念示例:多域名 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 443

4. 测试场景

程序自签发临时证书用于开发和测试。

vb
' 使用 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 443

5. 从文件加载到内存

将文件内容读入 Collection,实现延迟加载或缓存控制。

vb
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)

vb
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 443

cWinsock(TLS TCP 服务端)

vb
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 443

cWebSocketServer(wss://)

vb
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 文本中的换行可以是 vbCrvbLfvbCrLf,底层 OpenSSL 均能识别。

底层处理流程

vb
' cTlsSocket.InitServerTls 内部
If pvCollectionCount(Certificates) > 0 And pvCollectionCount(PrivateKey) > 0 Then
    Set cCerts = Certificates
    Set cPrivKey = PrivateKey
    GoTo StartTls
End If
TlsCertMemory(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 加密存储的私钥文本
vb
' 使用后清除
Set certs = Nothing
Set pkeys = Nothing

2. 证书来源验证

从外部来源(数据库、API、配置中心)加载证书时,需验证数据的完整性和来源。

建议:

  • 使用 TLS 连接获取证书数据
  • 对配置数据进行签名验证
  • 限制数据库访问权限

3. 内存限制

大量证书或超大证书链会占用较多内存。

建议:

  • 只加载必要的证书链
  • 避免在 Collection 中存储冗余的中间证书
  • 及时释放不再使用的 Collection

与其他模式对比

特性TlsCertFileTlsCertSubjectTlsCertMemory
证书来源磁盘文件Windows 证书库内存 Collection
部署便利文件分发系统导入嵌入/动态加载
证书更新替换文件续期到存储重新加载
私钥保护文件系统权限操作系统保护进程内存
运行时生成不支持不支持支持
ACME 自动签发需写盘需导入天然支持
资源嵌入不支持不支持支持
部署复杂度
适用场景生产部署企业共享动态/嵌入式

证书来源优先级

cTlsSocket.InitServerTls 中,三种证书来源的检测优先级:

  1. 内存集合 (Certificates + PrivateKey) — 最高优先级
  2. 证书文件 (CertFile)
  3. Windows 证书存储 (CertSubject) — 最低优先级

如果同时传入多个来源参数,只有优先级最高的生效。

常见问题

1. PEM 格式错误

LastError: 证书文件不存在或格式错误

解决: 确保 Collection 中的每个元素是完整的 PEM 块,包含 -----BEGIN...----------END...----- 标记。

2. 证书与私钥不匹配

TLS 握手失败

解决: 确保证书和私钥是配对的。可以用 OpenSSL 验证:

bash
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 > 0PrivateKey.Count > 0,否则底层会跳过内存模式。

相关文档


最后更新: 2026-06-09

VB6及其LOGO版权为微软公司所有