Envoy TLS 测试证书夹具全解析:test/common/tls/test_data 的身份体系、生成机制与扩展指南
2026/9/15 11:01:19 网站建设 项目流程

Envoy TLS 测试证书夹具全解析:test/common/tls/test_data 的身份体系、生成机制与扩展指南

【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy

导读

本文以 test/common/tls/test_data/README.md 为骨架,系统梳理 Envoy 源码仓库中 TLS/SSL 测试证书夹具(fixtures)的完整身份体系、文件布局、构建生成流程与扩展方法。你将掌握:目录中每一张证书、私钥、CRL、PKCS#12 包分别代表何种 TLS 验证场景;certs.spec声明式规格如何驱动构建期自动化生成;以及如何在 Envoy 的 TLS 单测(test/common/tls及其扩展测试)中复用这些夹具来验证证书链、SAN 匹配、吊销、密码保护密钥等核心逻辑。

目录概览:一套服务于 TLS 测试的"证书动物园"

test/common/tls/test_data是 Envoy 测试体系中专用于 TLS/SSL 的测试数据目录,包含了 CA、中间 CA、伪造 CA、各种 SAN 类型、自签名、过期、密码保护等证书、密钥与配套 OpenSSL 配置。目录内的输入文件大致分为三类:

  • 检查入库的静态输入:所有*_key.pem私钥、所有*.cfgOpenSSL 配置、会话票据密钥(ticket_key_*)、password_protected_password.txt
  • 构建期生成的产物:证书本体(*_cert.pem)、证书链(*_chain.pem)、CRL、PKCS#12 包、*_cert_info.h头文件与trust_bundles.json
  • 手工定制的例外no_extension_cert.pembad_rsa_key_usage_*系列夹具不做生成,原样检查入库。

目录的 BUILD 目标组织清晰:generated_certs(name = "certs", ...)负责产出全部证书类夹具,而envoy_cc_test_library(name = "cert_infos")将各*_cert_info.h聚合为可供 C++ 测试直接引用的库目标(见 BUILD)。这意味着测试代码只需要链接cert_infos即可在编译期拿到证书的 PEM 字符串、密钥等元数据,而无需在运行时读取文件。

身份体系:15+ 类证书各自服务的验证场景

原 README 将夹具归纳为 15 类身份,每类身份都对应一种需要被单测覆盖的 TLS 验证分支。下表完整映射了身份、证书/密钥文件与用途(文件实际名称以仓库目录为准,原文档个别条目中的文件名笔误已按实际文件订正):

身份证书 / 私钥核心用途
CAca_cert.pem/ca_key.pem根证书颁发机构,自签名;同时产出吊销san_dns_cert.pem的 CRL(ca_cert.crl
Intermediate CAintermediate_ca_cert.pem/intermediate_ca_key.pem由 CA 签发的中间 CA,用于构造多级链
Fake CAfake_ca_cert.pem/fake_ca_key.pem伪造 CA,专门用于验证"证书链验证逻辑能正确拒绝未知签发者"
No SANno_san_cert.pem/no_san_key.pem证书不含 SAN 字段;另有no_san_chain.pem(由 No SAN 叶子 + Intermediate CA 拼接)
SAN With DNSsan_dns_cert.pem/san_dns_key.pemSAN 为 DNS 类型(server1.example.com);同一配置还派生出san_dns2_cert(换私钥)、san_dns3_cert(由 Intermediate CA 签发,链为san_dns3_chain.pem
SAN With Multiple DNSsan_multiple_dns_cert.pem/san_multiple_dns_key.pem多个 DNS SAN,包含通配符域名*.example.com
SAN onlysan_only_dns_cert.pem/san_only_dns_key.pem不设置 CommonName、仅含 DNS SAN,验证纯 SAN 匹配路径
SAN With URIsan_uri_cert.pem/san_uri_key.pemSAN 为 URI 类型(SPIFFE URIspiffe://lyft.com/test-team
SAN With OtherNamesan_othername_cert.pem/san_othername_key.pemSAN 为 OtherName(UPN,OID1.3.6.1.4.1.311.20.2.3)类型
SAN With OtherName and DNSsan_dns_and_othername_cert.pem/san_dns_and_othername_key.pem同时含一个 DNS SAN 与一个 OtherName(UPN) SAN
SAN With Multiple Othernamesan_multiple_othername_cert.pem/san_multiple_othername_key.pem5 个 OtherName SAN,分别覆盖 NULL、ENUMERATED、INTEGER、BOOLEAN、OBJECT 等 ASN.1 类型
SAN With Multiple Othername (String Type)san_multiple_othername_string_type_cert.pem/san_multiple_othername_string_type_key.pem字符串类 OtherName 变体
SAN With Single CRL Distribution Pointsan_dns_cert_with_single_crl_dp_cert.pem/san_dns_cert_with_single_crl_dp_key.pem证书带 1 个 CRLDP 扩展
SAN With Multiple CRL Distribution Pointsan_dns_cert_with_multiple_crl_dps_cert.pem/san_dns_cert_with_multiple_crl_dps_key.pem证书带 2 个 CRLDP 扩展
Password-protectedpassword_protected_cert.pem/password_protected_key.pem私钥使用password_protected_password.txt中的口令(p4ssw0rd)加密
Self-signedselfsigned_cert.pem/selfsigned_key.pem自签名证书,复用同一selfsigned_cert.cfg生成多种密钥算法变体
Expiredexpired_cert.pem/expired_key.pem自签名但已过期
Expired With URIexpired_san_uri_cert.pem/expired_san_uri_key.pem已过期且带 URI SAN

构建期自动生成:从 certs.spec 到测试夹具

README 明确指出:本目录中的证书、链、CRL、PKCS#12 包、*_cert_info.h头文件与trust_bundles.json全部在构建期@envoy_toolshed//certs:gen工具生成,而私钥、*.cfgOpenSSL 配置、会话票据密钥与password_protected_password.txt是唯一检查入库的输入。这种"输入入库、产物构建期产出"的设计,保证了夹具可复现、可审计,且随源码版本演进。

手动构建并查看全部产物:

$ bazel build //test/common/tls/test_data:certs $ ls bazel-bin/test/common/tls/test_data/

构建目标certs在 BUILD 中定义,其outs列出了全部产出文件(CERTS列表),srcsglob(["*.cfg", "*_key.pem", "password_protected_password.txt"])组成,static_srcs则声明了不参与生成、原样拷贝的输入(含aes_128_keybad_rsa_key_usage_*no_extension_cert.pemnot_a_crl.crlticket_key_*)。这里值得注意not_a_crl.crlbad_rsa_key_usage_*这类"负向夹具"——它们是刻意制造的错误输入,用于验证 Envoy 对损坏 CRL、非法 KeyUsage 的容错/报错路径。

certs.spec:声明式夹具规格

夹具集合由 certs.spec 声明式描述,采用 INI 风格的分节语法,各节类型如下:

  • [cert <name>]:声明一张证书。必须指定私钥(key)、提供 subject 与 v3 扩展的.cfg文件(cfg)以及签发者(issuer,取值为self或另一夹具名)。可选info_header指定生成的 C++ 头文件、out指定输出文件名(省略时默认以夹具名命名)、validity指定有效期策略、key_password指定私钥口令。
  • [concat <out>.pem]:按parts顺序拼接多个夹具,生成证书链(如intermediate_ca_cert_chain.pem)。
  • [crl <out>.crl]:由指定issuer生成吊销列表,revoke列出被吊销的证书夹具。
  • [p12 <out>.p12]:将证书与链打包为 PKCS#12(可选加密或指定口令文件)。
  • [trust_bundle <out>.json]:生成 SPIFFE 信任域捆绑包(domain行声明信任域与 CA 标识)。

以 CA 与中间 CA 的定义为例:

[cert ca] key = ca_key.pem cfg = ca_cert.cfg issuer = self info_header = ca_cert_info.h [cert intermediate_ca] key = intermediate_ca_key.pem cfg = intermediate_ca_cert.cfg issuer = ca info_header = intermediate_ca_cert_info.h [concat intermediate_ca_cert_chain.pem] parts = intermediate_ca, ca

CRL 与链拼接的组合使用体现了吊销场景的覆盖思路:ca_cert.crl吊销san_dns,而intermediate_ca_cert.crl吊销san_dns3intermediate_ca_cert_chain_with_crl_chain.pem甚至把两条 CRL 都拼进链中,用来测试"链中携带多级吊销列表"的解析路径(见 certs.spec 的Revocation lists节)。

有趣而实用的细节夹具

除 README 列出的身份外,spec 中还隐藏着若干专门为边界场景设计的夹具:

  • 四层深链i1i2i3i4逐级签发,test_long_cert_chain.pem拼接前三层,test_random_cert.pem输出第四层,用于测试深层链构建;
  • Decoy 链san_dns3_with_decoy_chain.pem在链中混入一个并未签署叶子的 CA,验证 Envoy 仍能正确识别真实签发者;
  • 重复证书不同密钥selfsigned2_cert.pemselfsigned使用同一私钥和 subject 但生成不同的证书;san_dns2则是同一配置换私钥;
  • 长有效期long_validity的过期时间超出 32 位time_t表示范围,用于测试时间解析的边界。

确定性设计:为何证书永不"过期"

README 揭示了两个保证夹具长期可用的关键设计:

  1. 有效期窗口跟随构建年份:证书的有效期从构建运行当年的 1 月 1 日开始,年份来自STABLE_CERT_EPOCH_YEAR。该值由 bazel/get_workspace_status 中的echo "STABLE_CERT_EPOCH_YEAR $(date -u +%Y)"产生(工作区状态脚本),因此每次构建都会把证书有效期"滚动"到当前年份,夹具永远不会因为时间推移而自然失效。
  2. 显式过期/超长策略:需要构造过期场景的夹具使用validity = expired将有效期钉死在 2020 年;validity = long则生成超过 32 位time_t的极端有效期。
  3. 序列号确定性:序列号由夹具名称确定性推导(除非用serial = <hex>钉死),保证跨构建稳定——这正是测试可复现性的基石。

OpenSSL 配置文件解剖:身份与扩展从何而来

每张证书的 subject 与 v3 扩展都由独立的*.cfg文件定义。以根 CA 的 ca_cert.cfg 为例,它通过basicConstraints = critical, CA:TRUEkeyUsage = critical, cRLSign, keyCertSign声明 CA 能力,并通过[CA_default]段配置 CRL 数据库与签名摘要(default_md = sha256)。

叶子证书的配置则展示了 SAN 注入方式。DNS 类型见 san_dns_cert.cfg 的[alt_names]

[alt_names] DNS.1 = server1.example.com

多 DNS(含通配符)见 san_multiple_dns_cert.cfg:

[alt_names] DNS.1 = *.example.com DNS.2 = server2.example.com

URI 类型(SPIFFE)见 san_uri_cert.cfg:

[alt_names] URI.1 = spiffe://lyft.com/test-team

OtherName(UPN) 类型见 san_othername_cert.cfg:

[alt_names] otherName.1 = 1.3.6.1.4.1.311.20.2.3;UTF8:server1.example.com

自签名证书则在[req]段同时声明x509_extensions = v3_ca,使openssl req -x509路径也能直接注入扩展(见 selfsigned_cert.cfg)。这些配置正是 Envoy 各类match_typed_subject_alt_namesverify_certificate_hash、SPIFFE 校验等 TLS 下游/上游配置在单测中的"素材库"。

测试专用密钥:刻意公开、严禁他用

KEYS.md 对目录中所有*_key.pem给出了明确的安全声明:它们刻意公开、仅供测试,检查入库的目的是让@envoy_toolshed//certs:gen生成夹具可复现——生成器只创建证书、从不生成密钥,因此同一 spec 永远产出同一公钥材料。这些密钥:

  • 从未保护过任何真实资源;
  • 任何拿到仓库副本的人都知晓其内容;
  • 严禁在 Envoy 测试套件之外使用。

password_protected_key.pempassword_protected_password.txt中的p4ssw0rd加密,同样公开。KEYS.md 甚至提醒:密钥扫描器会标记这些文件,这是预期行为。从源码安全角度看,这一"显式公开"策略避免了误用测试密钥引发的安全事故。

扩展指南:如何新增一个证书夹具

按 README 与 BUILD 的约定,新增夹具只需三步:

  1. 在 certs.spec 添加[cert <name>]小节:指明私钥(key)、提供 subject 与 v3 扩展的.cfg文件(cfg)、签发者(issuer = self或另一夹具名),如需生成 C++ 头文件则加info_header,需要链/CRL/PKCS#12/信任包则追加[concat][crl][p12][trust_bundle]小节;
  2. 在 BUILD 的CERTS列表加入产出的文件名——spec 中出现的每个输出文件都必须出现在certsgenrule 的outs中,否则构建会失败;
  3. 将新私钥与.cfg放入目录(若用新配置),并确认被srcs/static_srcs的 glob 覆盖。

若需生成新的*_cert_info.h,还需将其加入cert_infos库的hdrs(BUILD 中已用glob(["*info.h"])兜底)。手工定制夹具(如no_extension_cert.pem)则直接检查入库并列入static_srcs即可。

在测试中的使用方式

生成的头文件(如san_dns_cert_info.hca_cert_info.h)以 C++ 常量形式暴露 PEM 内容,测试通过链接//test/common/tls/test_data:cert_infos目标直接引用。这一机制让test/common/tls及其下游扩展测试(如test/extensions/transport_sockets下的 TLS 相关用例)能够在编译期获得证书材料,驱动以下验证场景:

  • 证书链构建与可信路径校验(CA / Intermediate CA / 四层链);
  • SAN 匹配逻辑(DNS、通配符、URI/SPIFFE、IP、OtherName 全类型覆盖);
  • 证书吊销检查(单/多 CRLDP、CRL 与链拼接);
  • 私钥保护与格式兼容(密码加密 PEM、PKCS#12、RSA/ECDSA 多算法);
  • 时间边界(过期证书、超长有效期、跟随构建年份滚动的有效期)。

从源码结构可以推断,cert_infos库目标正是测试代码与证书夹具之间的标准桥接层——这也是为什么该目录既包含 PEM 文件,又同时维护着大量*_cert_info.h的原因。


小结test/common/tls/test_data是 Envoy TLS 测试基础设施中最具代表性的"构建期生成型"测试数据目录。理解其身份映射、certs.spec规格语法与确定性设计,不仅能帮你快速定位任意验证场景的夹具文件,还能在你为 Envoy 贡献 TLS 相关单测时,以最少的样板代码构造出覆盖面完整的证书素材。

【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询