- 后端
- 内容协同
【免费下载链接】core
:cloud: ownCloud web server core (Files, DAV, etc.)
ownCloud web server core 通过resources/codesigning/目录承载应用代码签名的信任锚点、中间证书与证书吊销列表(CRL),支撑 Integrity Check 验证器对 first-party 与第三方应用的签名校验。本文以该目录的 README 为核心,结合lib/private/IntegrityCheck/Verifier/下的TrustStore、CrlProvider实现与对应测试,讲解 G1 遗留验证路径与 G2 新验证路径如何共存、证书与 CRL 如何按代际加载,以及 G1 过渡期的兼容语义。读完本文,你将掌握 ownCloud 代码签名信任库的目录布局、代际标记规则、CRL 获取的三级降级策略,并能依据源码与测试用例理解 G1 迁移与 G2 上线的完整行为。
信任库的目录布局
resources/codesigning/是 ownCloud 代码签名验证的信任存储根目录,采用"roots / intermediates / crl"三级分层结构,与 G1、G2 两个证书代际一一对应:
resources/codesigning/ ├── roots/ # Trust anchors(信任锚点) │ ├── root-g1.crt # 遗留 G1 根(实际为中间权威),为过渡期保留 │ └── root-g2.crt # G2 根锚点(CA ceremony 产物) ├── intermediates/ # 中间证书 │ └── intermediate-g2.crt # G2 中间签发者(CA ceremony 产物) ├── crl/ # 证书吊销列表 │ ├── legacy.crl # 冻结的 G1 CRL(面向遗留应用) │ └── developers.crl # G2 叶子 CRL(CA ceremony 产物,通过 CRL_URL 刷新) └── core.crt # 核心叶子证书(first-party 签名用,验证器不使用)上述文件均已在本仓库确认存在。实际证书信息可通过openssl x509查看:root-g1.crt的主体为CN = ownCloud Code Signing Intermediate Authority(有效期 2016-02-03 至 2026-01-31),root-g2.crt的主体为CN = ownCloud Code Signing Root CA G2(有效期 2026-07-15 至 2051-07-09),intermediate-g2.crt的主体为CN = ownCloud Code Signing Intermediate CA G2(有效期 2026-07-15 至 2031-07-14),core.crt的 CN 为core(有效期 2016-02-03 至 2026-01-31)。
TrustStore 的加载行为:按文件名标记代际
OC\IntegrityCheck\Verifier\TrustStore(见 TrustStore.php)是加载信任锚点与中间证书的核心类,其行为与 README 描述完全一致:
- roots/ 全部加载为信任锚点:
getRoots()通过EnvironmentHelper::getServerRoot()拼接出resources/codesigning/roots路径,逐文件读取 PEM 内容;读取失败(file_get_contents返回false)的文件会被跳过。 - 按 basename 前缀打代际标签:文件名以
root-g1开头则标记为'g1'(遗留验证路径),否则标记为'g2'(新验证路径)。对应源码为:
$generation = \strpos($filename, 'root-g1') === 0 ? 'g1' : 'g2';- intermediates/ 作为非可信线索加载:
getBundledIntermediates()读取该目录下所有证书,作为"可能帮助叶子证书链接到可信根的提示",不直接作为信任锚点。
因为root-g1.crt存在,roots/ 目录非空,遗留 G1 应用可以正常走验证流程。TrustStoreMigrationTest(见 TrustStoreMigrationTest.php)对此做了显式约束:断言roots/、intermediates/、crl/三个目录存在、roots/root-g1.crt与crl/legacy.crl存在、旧的扁平文件(root.crt、intermediate.crl.pem)已被移除,并验证TrustStore至少加载到一个标记为g1的根。
CrlProvider 的 CRL 获取策略:G1 冻结、G2 三级降级
OC\IntegrityCheck\Verifier\CrlProvider(见 CrlProvider.php)实现了 README 所描述的按代际区分 CRL 来源的逻辑,getCurrentCrl(bool $isG1Chain)是入口:
- G1 链(
$isG1Chain === true):跳过网络请求,只读取捆绑的crl/legacy.crl(冻结版本),经CrlValidator::parseAndValidate校验签名后返回;若捆绑 CRL 缺失或校验失败,直接抛出CrlUnavailableException("No valid G1 legacy CRL available")。 - G2 链(
$isG1Chain === false):执行三级降级——- 先用
CrlFetcher从网络获取VerifierConstants::CRL_URL指向的 CRL,校验通过即返回; - 网络失败或校验失败,回退到捆绑的
crl/developers.crl; - 两者均不可用时fail-closed(关闭式失败),抛出
CrlUnavailableException("No valid CRL available (fetch and bundled both failed or invalid)")。
- 先用
CRL 的远端地址定义在 VerifierConstants.php:CRL_URL = 'https://owncloud.dev/developer-certificates/crl/developers.crl'。该常量特意选择 owncloud.dev 而非 owncloud.github.io 路径——后者会 301 跳转而CrlFetcher不跟随重定向(详见设计文档 §13)。
CrlProviderTest(见 CrlProviderTest.php)对这条降级链做了逐一验证:testFetchedValidCrl覆盖"网络 CRL 有效即采用",testFallbackToBundledCrl覆盖"fetch 返回 null 时回退捆绑 CRL",testFetchedInvalidFallbackToBundled覆盖"网络 CRL 签名无效(wrong-issuer)时回退捆绑 CRL",testFailClosedNoCrl与testFailClosedExceptionReasonCode覆盖"双源都不可用时抛CRL_UNAVAILABLE原因码",testG1SkipsFetch则断言 G1 路径下CrlFetcher::fetch永远不会被调用。
G1 过渡:从扁平根迁移到roots/root-g1.crt
生产环境的 ownCloud 10.x 生态此前依赖单一扁平信任锚点resources/codesigning/root.crt。README 明确指出:该文件实际是G1 中间权威(subject 为 "ownCloud Code Signing Intermediate Authority"),它曾是遗留loadCA()验证器事实上的信任锚点。
"Task 13" 将其迁移为roots/root-g1.crt,目的是在建立新的多代际目录布局的同时,保留遗留验证语义的完全一致性——使用 G1 证书签名的应用继续通过验证。迁移测试(见 TrustStoreMigrationTest.php)专门断言旧的扁平文件必须"消失而非重复保留":
- 旧
root.crt必须不存在(已迁移至roots/root-g1.crt); - 旧
intermediate.crl.pem必须不存在(已迁移至crl/legacy.crl)。
这保证了迁移是"移动"而非"复制",避免新旧两份文件在语义上产生分叉。
遗留 G1 应用在过渡期与日落后的行为差异
LegacyTransitionTest(见 LegacyTransitionTest.php)以参考时间注入的方式,精确刻画了 G1 过渡期的行为矩阵,与VerifierConstants::LEGACY_SUNSET = '2026-12-31T23:59:59Z'呼应:
| 场景 | 参考时间 | 期望行为 |
|---|---|---|
| 过期 G1 + 日落前 | 2026-06-01 | isLegacyWarn=true且isPassed=true(warn+allow,输出['LEGACY_ACCEPTED_WARN' => true]) |
| 过期 G1 + 日落后 | 2027-06-01 | 抛出BadAlgorithmException(算法门禁拒绝遗留算法,硬性阻止) |
| 过期 G1 但证书被吊销 | 2026-06-01 | 仍抛出RevokedException(吊销检查优先于 warn) |
| 过期 G1 但文件被篡改 | 2026-06-01 | 返回INVALID_HASHdiff,isPassed=false(篡改绝不 warn) |
| 未生效 G1(now < notBefore) | 2019-01-01 | 抛出BadChainException(硬性阻止,不进入 warn 分支) |
| 无扩展、大写 CN 的遗留 G1 叶子 | 2026-06-01 | 完整管线通过,isPassed=true且非 legacy-warn(保持旧验证器语义) |
| 正常 G2 应用 | 2026-07-12 | isPassed=true、isLegacyWarn=false(G2 不受影响) |
其中"无扩展、大写 CN"用例(testLegacyG1ExtensionlessUppercaseCnPasses)明确以resources/codesigning/core.crt与tests/data/integritycheck/SomeApp.crt的真实形态作为回归基准,证明新验证器对旧格式叶子证书的向后兼容。
G2 可用性:真实 ceremony 产物与测试 PKI 的严格隔离
README 强调 G2 代码签名已是live状态:CA ceremony 已产生真实、离线持有的锚点,并捆绑在仓库中:
roots/root-g2.crt—— G2 根锚点(CN "ownCloud Code Signing Root CA G2");intermediates/intermediate-g2.crt—— G2 中间签发者;crl/developers.crl—— G2 叶子吊销列表(由 G2 中间签发)。
G2 在运行时能否通过验证,取决于捆绑 CRL 能否用随附根证书验证通过,而非目录槽位是否为空的静态条件。这些是真实的 ceremony 产物,不是tests/data/integritycheck/verifier/pki/下的测试 PKI 证书。README 明确记录了一个安全教训(Task 10):把测试证书当作生产信任锚点下发是安全错误,因此两套证书严格分离。VerifierTest等测试在临时 server root 中复制测试 PKI 与 CRL fixture(如developers-empty.crl、developers-revoked.crl、intermediate-revoked.crl),验证器在真实运行时路径上绝不清空此隔离。
验证流程全景:从验证器到信任库的调用链
将上述组件串联起来,ownCloud 的签名验证管线为:Verifier::verify()接收签名文件(signature.json)、应用目录树与应用 ID,依次经过ChainValidator(借助TrustStore的根与中间线索构链)、AlgorithmAllowlist(G2 仅允许ecdsa-p384-sha384与rsa-pss-sha384,见 VerifierConstants.php,遗留rsa-pss-sha1仅限 G1 过渡期)、ManifestVerifier(用OnDiskHasher计算文件哈希比对清单)、CrlProvider(按代际获取并校验 CRL)、AppIdResolver(核对 CN 与应用 ID 一致)与IntegrityDiffer。VerifierTest(见 VerifierTest.php)覆盖了完整链路的通过、篡改(INVALID_HASH)、叶子吊销(RevokedException)、中间吊销、坏签名(BadSignatureException)、CN 不匹配(CnMismatchException)与缺失签名(MissingSignatureException)等分支。
参考与进一步阅读
README 末尾给出的两条参考线索:设计文档为 ownCloud G2 code-signing verifier(design/core-g2-code-signing.md的 §2 与 §19,涵盖链验证与 CRL 设计要点),CA ceremony 材料位于 developer-certificates 仓库。在本文所讨论的代码库内,可直接深入阅读的对照材料包括:信任库加载实现 TrustStore.php、CRL 降级实现 CrlProvider.php、常量定义 VerifierConstants.php,以及迁移合规测试 TrustStoreMigrationTest.php 与过渡期行为测试 LegacyTransitionTest.php。需要说明的是,证书与 CRL 均为真实签名材料,仅用于 ownCloud 服务端验证自身签名的应用包,读者如需自行构建测试环境,应使用tests/data/integritycheck/verifier/pki/下的测试 PKI 生成脚本(如gen_g1_pki.sh、gen_g2_pki.sh、gen_crls.sh),而不得将测试证书混入生产信任库。
- 后端
- 内容协同
【免费下载链接】core
:cloud: ownCloud web server core (Files, DAV, etc.)
相关推荐
RabbitMQ 证书信任存储(Certificate Trust Store)插件深度指南:白名单式 TLS 对端证书验证
RabbitMQ 证书信任存储(Certificate Trust Store)插件深度指南:白名单式 TLS 对端证书验证 本指南围绕 RabbitMQ 官方
后端消息队列消息路由Asterinas数字签名:代码签名与验证
Asterinas数字签名:代码签名与验证 概述 在当今数字化时代,软件安全已成为系统开发的核心关注点。Asterinas作为一个用Rust编写的安全、快速、通
操作系统内核驱动系统编程Zero Trust 架构深度解析:从“信任但验证”到“永不信任,始终验证”
Zero Trust 架构深度解析:从“信任但验证”到“永不信任,始终验证” 导读 :本文基于 Security 101 课程的第 1.5 课(translat
网络安全教程文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考