恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
apt-key 命令详解与弃用替代:从 trusted.gpg 迁移到 signed-by 完整指南
首页
资讯中心
/
apt-key 命令详解与弃用替代:从 trusted.gpg 迁移到 signed-by 完整指南
apt-key 命令详解与弃用替代:从 trusted.gpg 迁移到 signed-by 完整指南
发布时间:2026/10/17 15:15:21
apt-key 这个命令在 Debian 和 Ubuntu 的软件源管理里存在感一直很高但这两年它的处境有点尴尬官方早就把它标记为 deprecated弃用可网上大量教程、脚本、甚至某些软件官方文档里还在继续用它。我自己的服务器上就踩过这种坑一条 apt-key add 把密钥塞进了全局信任区后来想单独撤销某个源的权限发现它和其他源的密钥混在一起根本摘不清。所以这篇文档我打算讲透两件事一是 apt-key 到底有哪些命令、每条命令的准确用法和典型输出二是既然它已经弃用新服务器上应该怎么用更正确的方式管理 APT 密钥以及从旧方案迁移时要注意哪些坑。这篇内容适合谁刚接触 Linux 软件源管理的新手可以把它当命令参考慢慢看遇到 NO_PUBKEY、EXPKEYSIG 这类报错能找到对应解法被老脚本坑过的老手也能从中找到清理 trusted.gpg、切换到 signed-by 方案的思路。我会尽量用实际会话和报错原话来还原现场不搞抽象的理论堆砌毕竟密钥管理这种活出问题的时候最需要的是一份能照着敲的命令清单。1. apt-key 是什么它到底在管什么1.1 APT 验签机制里的关键角色先理清楚一个底层逻辑APT 下载软件包时默认不会无条件信任任何来源。它先要拿到仓库的 Release 文件这个文件带有一个由仓库私钥生成的 GPG 签名APT 会用系统里预先存好的公钥去验证这个签名。验证通过下载的 Packages 索引和被它索引的 .deb 包才会被接受验证不通过apt update 就会报错并把该仓库标记为不可信。这里有个很容易被忽略的细节APT 不只是验证 Release 文件本身还要验证里面的各个哈希条目是否和实际下载的文件一致。换句话说验签是整条信任链的入口公钥一旦被信任就等于完全信任了这家仓库发布的所有内容。所以公钥的存放位置和管理方式直接决定了系统的软件供应链安全等级。apt-key 在旧体系里就是管理这些公钥的主要工具。它的核心职责是维护 APT 的密钥环文件默认写入 /etc/apt/trusted.gpg并在 /etc/apt/trusted.gpg.d/ 目录下支持额外密钥环。这两个位置的密钥对 APT 来说是全局信任的不管 sources.list 里配的是哪个源只要签名匹配就能通过。1.2 全局信任区为什么让官方决定弃用它apt-key 被标记弃用的原因并不复杂它把所有仓库的密钥一股脑放进同一个信任区违背了“最小权限”原则。打个比方这相当于小区门口只有一个万能门禁卡任何访客只要能匹配这张卡就能进入任意楼栋。即使某家仓库只发布一个工具用它添加的公钥也拥有验证其他仓库签名的能力权限范围明显过大。另外还有一层现实问题apt-key add 接受从管道传入的密钥内容但完全不验证密钥的来源和用途。很多教程写的是 wget 下载密钥后直接管道给 apt-key add这中间如果下载被劫持或源本身发布了一个恶意密钥APT 就会在后续更新中无条件信任该源攻击面非常大。所以新版本 Debian 和 Ubuntu 开始默认输出警告“Key is stored in legacy trusted.gpg keyring”部分版本甚至在 apt update 时直接给出明显提示。这不是吓唬人而是引导大家切换到后面要讲的 signed-by 模式。2. apt-key 命令逐个拆解从 list 到 adv2.1 apt-key list查看当前信任的密钥最基本的用法是直接执行 apt-key list。这个命令会列出默认信任区里所有密钥的摘要信息包括每个密钥的指纹、创建时间、过期状态和 UID 标识。它在排查 NO_PUBKEY 或确认密钥是否已导入时非常常用。我自己在服务器上执行后的输出结构大致是这样的/etc/apt/trusted.gpg -------------------- pub rsa4096 2020-01-01 [SC] 1A2B 3C4D 5E6F 7A8B 9C0D E1F2 3A4B 5C6D 7E8F 9A0B uid [ unknown] Example Archive Signing Key sub rsa4096 2020-01-01 [E]对比一下 /etc/apt/trusted.gpg.d/ 目录里的独立密钥环条目会显示为/etc/apt/trusted.gpg.d/example.gpg ----------------------------------- pub rsa4096 2021-06-15 [SC] ... uid [ unknown] Example Repository (Signing Only) sub rsa4096 2021-06-15 [E]列出的 [SC] 表示主密钥同时具备签名Sign和认证Certify能力[E] 表示子密钥用于加密。平时我们用 apt-key list 主要看三件事密钥是否存在、指纹是否和源提供方公布的一致、密钥是否过期过期会显示 [expired]。2.2 apt-key add添加密钥但强烈不推荐再用旧式添加流程通常是这样wget -qO- https://repo.example.com/example.asc | sudo apt-key add -执行后如果成功会输出 OK表示密钥已经写入默认信任区。表面看这个命令很简洁问题却不少。第一它没有指定写入哪个 keyring一律落到全局的 trusted.gpg第二它不校验来源只负责把内容解析成公钥并导入第三它把第三方源和官方源的密钥混在一起后续无法针对单个仓库做权限回收。如果只是临时在一个实验环境里验证某个源用 apt-key add 倒不至于立刻出事但生产环境我建议尽量不用。后面我会给出替代命令和迁移方案。2.3 apt-key del删除指定密钥语法是sudo apt-key del 1A2B 3C4D 5E6F 7A8B ...密钥 ID 可以写完整指纹也可以写短 ID。注意这里不区分大小写但短 ID 存在碰撞风险尽量用完整指纹。删除时如果看到 OK 表示删除成功。有一个容易踩的坑apt-key del 只能操作默认信任区也就是 /etc/apt/trusted.gpg 和它默认管理的部分。如果密钥是放在 /etc/apt/trusted.gpg.d/ 下的独立文件里直接执行 apt-key del 并不会删除那个文件你可能要手动移除文件或重新生成 keyring。判断方式很简单先 apt-key list 看一下密钥所在文件再去对应路径做处理。2.4 apt-key export / export-all备份和迁移用export 和 export-all 可以把密钥导出为文件用途主要有两个备份、迁移到新的 keyring。比如你要把某个密钥单独导出成一个文件方便后续配合 signed-by 使用sudo apt-key export Example Archive Signing Key | sudo tee /etc/apt/keyrings/example.gpg但这里有个很关键的坑apt-key export 输出的是 ASCII armor 格式而不是二进制的 GPG keyring 格式。如果你把它直接当作 signed-by 指向的文件部分 APT 版本会拒绝加载。更可靠的做法是用 gpg 命令做转换sudo apt-key export Example Archive Signing Key | gpg --dearmor | sudo tee /etc/apt/keyrings/example.gpg /dev/nullexport-all 则会把整个默认信任区导出适合做完整备份。它的输出同样需要 dearmor 处理才能作为标准 keyring 使用。2.5 apt-key finger查看密钥指纹finger 子命令用来查看每个密钥的完整指纹格式比 list 更紧凑适合在做安全核对时快速引用sudo apt-key finger输出会列出密钥指纹和 UID。我在确认一个源是否真实时一般会拿这个输出和源官网页面上公布的指纹字符逐个比对这是防止中间人攻击非常实用的习惯。2.6 apt-key adv直接调用 GPG 的高级参数apt-key adv 本质上是把参数透传给 GPG用来执行一些 apt-key 没有专门封装的密钥管理操作。比如向密钥服务器拉取密钥可以写成sudo apt-key adv --keyserver keys.example.net --recv-keys 1A2B3C4D虽然技术上可行但在新系统上我不建议依赖它因为密钥服务器请求本身可能被劫持或返回额外信息而且它同样会把密钥导入全局信任区。更好的做法是用 gpg 单独拉取再转换到仓库专属 keyring。3. 实操场景用 apt-key 完成一次真实的第三方源接入3.1 场景描述与准备工作我以一台全新安装的 Ubuntu 服务器为例目标是接入一个外部仓库 repo.example.com这个仓库以 .deb 包形式分发某个命令行工具。仓库官方提供两种密钥文件格式example.ascASCII armor和 example.gpg二进制 keyring。这套流程虽然用的还是 apt-key但我特意把关键步骤按比较小心的方式演示避免直接复制旧教程犯错误。整个流程分三块验证密钥指纹、导入密钥、检查导入结果。3.2 验证源提供方密钥指纹在添加任何密钥之前先看仓库官方页面公布的指纹假设它写的是1A2B 3C4D 5E6F 7A8B 9C0D E1F2 3A4B 5C6D 7E8F 9A0B下载密钥文件后用它来查看指纹wget -qO- https://repo.example.com/example.asc | gpg --show-keys --with-fingerprint输出中会有一行 pub 指纹。逐段和官方公布的指纹比对确认一致后再继续。这一步不是可有可无的仪式而是防止密钥文件在传输中被替换的关键防线。3.3 传统 apt-key 导入流程确认指纹无误后把这个密钥导入系统并验证是否生效wget -qO- https://repo.example.com/example.asc | sudo apt-key add - sudo apt-key fingerprint Example Archive Signing Key第一条命令返回 OK第二条命令列出该密钥的指纹用来复核导入后的完整性。之后在 /etc/apt/sources.list.d/ 下新建文件写入仓库配置例如deb https://repo.example.com/ubuntu jammy main再执行 apt update理论上仓库应该能被正常识别。如果在 update 时收到 NO_PUBKEY 报错多半是导入的密钥指纹和 Release 文件签名者不匹配这时需要回到验证环节重新核对。3.4 为什么我在实际环境里会立刻切换到 signed-by上面的流程虽然能跑通但它把密钥放进了全局信任区后续会出现一个比较麻烦的局面只要这个密钥匹配任何仓库的 Release 签名APT 都会认可。哪怕这个仓库只用于安装一个小工具它理论上也能在系统里释放符合其他仓库签名的包。所以同一次服务器初始化里我通常会直接改走 signed-by 路线也就是把密钥单独存放在 /etc/apt/keyrings/ 目录并在 sources 配置里用 signed-by 参数指定该文件。这个思路不是不用 apt-key 了而是在用它完成“查看”“导出”这类辅助动作后把最终信任关系精确限制到单个仓库。具体做法我在第 4 节展开。4. 从 apt-key 到 signed-by密钥管理的现代迁移方案4.1 新方案的目录约定与权限要求现代 APT 推荐的做法是把每个仓库的密钥放到独立文件里并且在仓库源配置中通过 signed-by 明确指定。这样即使某个第三方源被攻破或密钥泄露攻击者也只能影响该源自身的内容无法伪造其他仓库的更新。目录方面旧的习惯是放在 /etc/apt/trusted.gpg.d/但严格来说这个目录下的密钥依然会被 APT 当作全局信任。Linux 社区比较认可的位置是 /etc/apt/keyrings/它不在全局信任区只是作为一个普通存储目录真正起信任作用的是 sources 配置里 signed-by 指向关系。新建这个目录并设置权限sudo install -d -m 0755 /etc/apt/keyrings权限设置成 0755 足够密钥文件本身建议用 root:root 和 0644不要让普通用户能修改。4.2 把 ASCII armor 密钥转换成二进制 keyring仓库可能提供的是 .asc 文件而 APT 的 signed-by 参数更希望指向二进制的 keyring 文件。转换命令是wget -qO- https://repo.example.com/example.asc | gpg --dearmor | sudo tee /etc/apt/keyrings/example.gpg /dev/null验证文件类型file /etc/apt/keyrings/example.gpg如果输出显示 “PGP/GPG keyring public key ring”说明格式正确。如果文件是文本或空文件说明 dearmor 步骤有问题需要检查下载内容。4.3 修改 sources 配置加入 signed-by在 /etc/apt/sources.list.d/ 下新建或修改仓库源文件比如 example.sourcesdeb [signed-by/etc/apt/keyrings/example.gpg] https://repo.example.com/ubuntu jammy main对于 deb822 格式的 .sources 文件写法是Types: deb URIs: https://repo.example.com/ubuntu Suites: jammy Components: main Signed-By: /etc/apt/keyrings/example.gpg配置完成后必须做一次干净的 updatesudo apt clean sudo apt update同样的仓库现在只有在 example.gpg 里存在的密钥才能通过签名验证其他密钥即使和 Release 签名匹配也无法生效。4.4 从 trusted.gpg 迁移已有密钥如果服务器以前大量使用 apt-key add现在要把信任关系收归到 signed-by 模式不能简单地直接删除全局密钥否则正在使用的仓库会集体报 NO_PUBKEY。正确顺序是第一步导出需要迁移的密钥为二进制 keyringgpg --no-default-keyring --keyring /etc/apt/trusted.gpg --export Example Archive Signing Key /etc/apt/keyrings/example.gpg第二步更新对应仓库的源配置加上 signed-by 指向新文件。第三步确认所有仓库都工作正常后再删除全局信任区里的旧密钥。删除时优先用 apt-key del如果它删不动再考虑直接操作 trusted.gpg 文件。我特别提醒一点不要把整个 trusted.gpg 一股脑复制成某个 keyring 文件然后到处 signed-by这样所有仓库仍然共享密钥等于没迁移。4.5 新服务器上彻底绕过 apt-key新服务器没有历史包袱可以直接跳过 apt-key。流程变得很短下载密钥、转换、放到 keyrings 目录、在 sources 配置里引用 signed-by。全程不需要调用 apt-key 命令也不会有 trusted.gpg 里的遗留垃圾。这期间如果遇到“Key is stored in legacy trusted.gpg keyring”这类提示可以先检查 /etc/apt/trusted.gpg 是否为空。如果确实空了可以直接把文件重命名备份或者删除后创建空文件避免 APT 每次更新都弹出警告。5. 常见问题与排查技巧实录5.1 apt update 报 NO_PUBKEY这是最常见的错误字面意思是 APT 找到了一个不认识的公钥它无法验证当前 Release 文件。报错通常会带一个 8 位或 16 位的密钥 ID比如W: GPG error: https://repo.example.com/ubuntu jammy InRelease: The following signatures couldnt be verified because the public key is not available: NO_PUBKEY 1A2B3C4D处理步骤分两步先向该仓库官方确认这个密钥 ID 对应的指纹然后用 gpg 单独拉取转换后放到 keyrings 目录最后在源配置中加入 signed-by。如果你还想快速验证密钥是否已在系统内可以先用 gpg --list-keys 查看但别把 NO_PUBKEY 当成一定要用 apt-key adv 解决的信号那只是旧习惯。5.2 EXPKEYSIG 或 REVKEYSIG 报错EXPKEYSIG 表示签名密钥已过期REVKEYSIG 表示密钥已被撤销。遇到这类错误直接续期或重新导入同一指纹的密钥不一定有用因为过期和撤销是密钥自身状态的变化。正确做法是联系仓库维护方获取新密钥然后替换仓库配置里的 keyring 文件再执行 apt update。删除旧密钥时注意如果你已经切换到 signed-by 模式只需替换 /etc/apt/keyrings/ 下的文件如果还停留在 trusted.gpg 里则要先用 apt-key list 找出旧密钥 ID再用 apt-key del 删除避免新旧密钥并存产生验证歧义。5.3 apt-key del 删不掉提示密钥不在 trusted.gpg 中这个情况我也遇过很多次。原因通常是密钥位于 /etc/apt/trusted.gpg.d/ 的某个独立 keyring 文件里apt-key del 只能处理默认 keyring。解决办法有两种一是在 apt-key del 后面加 keyring 参数sudo apt-key del 1A2B...但如果系统版本的 apt-key 已经简化可能不支持指定 keyring。那就直接手动管理文件sudo gpg --no-default-keyring --keyring /etc/apt/trusted.gpg.d/example.gpg --delete-keys 1A2B...或者干脆删除这个 keyring 文件本身再检查其他仓库是否还需要它。5.4 如何反查密钥来自哪个文件排查时经常需要知道某个密钥到底是哪个文件提供的。用以下命令可以精确查sudo gpg --no-default-keyring --keyring /etc/apt/trusted.gpg.d/example.gpg --list-keys还可以遍历目录里的每个文件for keyring in /etc/apt/trusted.gpg.d/*.gpg; do echo $keyring ; gpg --no-default-keyring --keyring $keyring --list-keys; done这套命令同样适用于 /etc/apt/trusted.gpg。配合 apt-key list 的输出就能理清每个仓库密钥的位置和状态。5.5 警告“Key is stored in legacy trusted.gpg keyring”新版本 APT 会在检测到 trusted.gpg 或 trusted.gpg.d 里存在密钥时打印这条警告。它本身不阻断软件更新但属于官方提示要清理的信号。如果你想保留旧密钥又不想看到警告可以尝试在 sources 配置中加入 signed-by把信任关系迁移出来然后清空全局信任区。如果确认所有仓库都已经使用 signed-by可以备份并移除 trusted.gpgsudo mv /etc/apt/trusted.gpg /etc/apt/trusted.gpg.bak sudo touch /etc/apt/trusted.gpg sudo chmod 0644 /etc/apt/trusted.gpg这之后 apt update 的警告会消失。需要注意的是不要随意改动 trusted.gpg.d 下的其他文件除非你清楚每个文件的来源。5.6 扩展技巧用 gpg 直接验证 Release 签名在排查签名类问题时除了依赖 APT还可以手动验证。先下载 Release 和 Release.gpgwget https://repo.example.com/ubuntu/dists/jammy/Release wget https://repo.example.com/ubuntu/dists/jammy/Release.gpg然后用 gpg 验证gpg --verify Release.gpg Release如果报错或显示签名者不匹配就能快速判断是不是 keyring 文件放错了。这一步非常实用尤其在多个源共用同一个 keyring 文件时可以快速定位是哪个仓库签名不匹配。写在最后的一点个人体会我把 apt-key 从“默认使用”改成“仅用于排查和导出”大概是在某次线上事故之后。那次事故的起因就是全局信任区里混入了某个废弃仓库的密钥导致另一家仓库的 Release 签名验证行为变得很难追溯排查花了将近两个小时。从那以后我在任何一台 Linux 服务器上都会先确认 /etc/apt/keyrings 和 sources 配置里的 signed-by 关系并坚持用 gpg 命令替代 apt-key 完成密钥导入和验证。最后再分享一个我自己用着很顺的小技巧在 /etc/apt/keyrings 下管理密钥文件时文件名尽量包含来源和日期比如 example-2025-01.gpg。这样仓库更新密钥时你只需要新增一个带新日期的 keyring 文件再修改 sources 配置而不必覆盖旧文件。万一那次密钥轮换有问题还能秒级回滚到旧配置这个习惯帮我省过不止一次紧急修复的时间。