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.pem与bad_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 验证分支。下表完整映射了身份、证书/密钥文件与用途(文件实际名称以仓库目录为准,原文档个别条目中的文件名笔误已按实际文件订正):
| 身份 | 证书 / 私钥 | 核心用途 |
|---|---|---|
| CA | ca_cert.pem/ca_key.pem | 根证书颁发机构,自签名;同时产出吊销san_dns_cert.pem的 CRL(ca_cert.crl) |
| Intermediate CA | intermediate_ca_cert.pem/intermediate_ca_key.pem | 由 CA 签发的中间 CA,用于构造多级链 |
| Fake CA | fake_ca_cert.pem/fake_ca_key.pem | 伪造 CA,专门用于验证"证书链验证逻辑能正确拒绝未知签发者" |
| No SAN | no_san_cert.pem/no_san_key.pem | 证书不含 SAN 字段;另有no_san_chain.pem(由 No SAN 叶子 + Intermediate CA 拼接) |
| SAN With DNS | san_dns_cert.pem/san_dns_key.pem | SAN 为 DNS 类型(server1.example.com);同一配置还派生出san_dns2_cert(换私钥)、san_dns3_cert(由 Intermediate CA 签发,链为san_dns3_chain.pem) |
| SAN With Multiple DNS | san_multiple_dns_cert.pem/san_multiple_dns_key.pem | 多个 DNS SAN,包含通配符域名*.example.com |
| SAN only | san_only_dns_cert.pem/san_only_dns_key.pem | 不设置 CommonName、仅含 DNS SAN,验证纯 SAN 匹配路径 |
| SAN With URI | san_uri_cert.pem/san_uri_key.pem | SAN 为 URI 类型(SPIFFE URIspiffe://lyft.com/test-team) |
| SAN With OtherName | san_othername_cert.pem/san_othername_key.pem | SAN 为 OtherName(UPN,OID1.3.6.1.4.1.311.20.2.3)类型 |
| SAN With OtherName and DNS | san_dns_and_othername_cert.pem/san_dns_and_othername_key.pem | 同时含一个 DNS SAN 与一个 OtherName(UPN) SAN |
| SAN With Multiple Othername | san_multiple_othername_cert.pem/san_multiple_othername_key.pem | 5 个 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 Point | san_dns_cert_with_single_crl_dp_cert.pem/san_dns_cert_with_single_crl_dp_key.pem | 证书带 1 个 CRLDP 扩展 |
| SAN With Multiple CRL Distribution Point | san_dns_cert_with_multiple_crl_dps_cert.pem/san_dns_cert_with_multiple_crl_dps_key.pem | 证书带 2 个 CRLDP 扩展 |
| Password-protected | password_protected_cert.pem/password_protected_key.pem | 私钥使用password_protected_password.txt中的口令(p4ssw0rd)加密 |
| Self-signed | selfsigned_cert.pem/selfsigned_key.pem | 自签名证书,复用同一selfsigned_cert.cfg生成多种密钥算法变体 |
| Expired | expired_cert.pem/expired_key.pem | 自签名但已过期 |
| Expired With URI | expired_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列表),srcs由glob(["*.cfg", "*_key.pem", "password_protected_password.txt"])组成,static_srcs则声明了不参与生成、原样拷贝的输入(含aes_128_key、bad_rsa_key_usage_*、no_extension_cert.pem、not_a_crl.crl、ticket_key_*)。这里值得注意not_a_crl.crl与bad_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, caCRL 与链拼接的组合使用体现了吊销场景的覆盖思路:ca_cert.crl吊销san_dns,而intermediate_ca_cert.crl吊销san_dns3;intermediate_ca_cert_chain_with_crl_chain.pem甚至把两条 CRL 都拼进链中,用来测试"链中携带多级吊销列表"的解析路径(见 certs.spec 的Revocation lists节)。
有趣而实用的细节夹具
除 README 列出的身份外,spec 中还隐藏着若干专门为边界场景设计的夹具:
- 四层深链:
i1→i2→i3→i4逐级签发,test_long_cert_chain.pem拼接前三层,test_random_cert.pem输出第四层,用于测试深层链构建; - Decoy 链:
san_dns3_with_decoy_chain.pem在链中混入一个并未签署叶子的 CA,验证 Envoy 仍能正确识别真实签发者; - 重复证书不同密钥:
selfsigned2_cert.pem与selfsigned使用同一私钥和 subject 但生成不同的证书;san_dns2则是同一配置换私钥; - 长有效期:
long_validity的过期时间超出 32 位time_t表示范围,用于测试时间解析的边界。
确定性设计:为何证书永不"过期"
README 揭示了两个保证夹具长期可用的关键设计:
- 有效期窗口跟随构建年份:证书的有效期从构建运行当年的 1 月 1 日开始,年份来自
STABLE_CERT_EPOCH_YEAR。该值由 bazel/get_workspace_status 中的echo "STABLE_CERT_EPOCH_YEAR $(date -u +%Y)"产生(工作区状态脚本),因此每次构建都会把证书有效期"滚动"到当前年份,夹具永远不会因为时间推移而自然失效。 - 显式过期/超长策略:需要构造过期场景的夹具使用
validity = expired将有效期钉死在 2020 年;validity = long则生成超过 32 位time_t的极端有效期。 - 序列号确定性:序列号由夹具名称确定性推导(除非用
serial = <hex>钉死),保证跨构建稳定——这正是测试可复现性的基石。
OpenSSL 配置文件解剖:身份与扩展从何而来
每张证书的 subject 与 v3 扩展都由独立的*.cfg文件定义。以根 CA 的 ca_cert.cfg 为例,它通过basicConstraints = critical, CA:TRUE与keyUsage = 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.comURI 类型(SPIFFE)见 san_uri_cert.cfg:
[alt_names] URI.1 = spiffe://lyft.com/test-teamOtherName(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_names、verify_certificate_hash、SPIFFE 校验等 TLS 下游/上游配置在单测中的"素材库"。
测试专用密钥:刻意公开、严禁他用
KEYS.md 对目录中所有*_key.pem给出了明确的安全声明:它们刻意公开、仅供测试,检查入库的目的是让@envoy_toolshed//certs:gen生成夹具可复现——生成器只创建证书、从不生成密钥,因此同一 spec 永远产出同一公钥材料。这些密钥:
- 从未保护过任何真实资源;
- 任何拿到仓库副本的人都知晓其内容;
- 严禁在 Envoy 测试套件之外使用。
password_protected_key.pem用password_protected_password.txt中的p4ssw0rd加密,同样公开。KEYS.md 甚至提醒:密钥扫描器会标记这些文件,这是预期行为。从源码安全角度看,这一"显式公开"策略避免了误用测试密钥引发的安全事故。
扩展指南:如何新增一个证书夹具
按 README 与 BUILD 的约定,新增夹具只需三步:
- 在 certs.spec 添加
[cert <name>]小节:指明私钥(key)、提供 subject 与 v3 扩展的.cfg文件(cfg)、签发者(issuer = self或另一夹具名),如需生成 C++ 头文件则加info_header,需要链/CRL/PKCS#12/信任包则追加[concat]、[crl]、[p12]、[trust_bundle]小节; - 在 BUILD 的
CERTS列表加入产出的文件名——spec 中出现的每个输出文件都必须出现在certsgenrule 的outs中,否则构建会失败; - 将新私钥与
.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.h、ca_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),仅供参考