尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

自定义Secure Boot密钥:从生成到导入的完整指南

自定义Secure Boot密钥:从生成到导入的完整指南 写这篇文章之前我先说个背景。我最早折腾Secure Boot不是因为看了某篇教程而是手里一台跑自编译内核的Linux工作站每次开机都被固件安全策略卡住GRUB没签名、内核没签名、驱动模块没签名只能被迫关闭Secure Boot。后来我花了整整两个周末把PK、KEK、db、dbx从生成到导入、再从固件到内核一层层打通又因为操作顺序不对翻了好几次车。这篇内容就是那两次翻车经历换来的完整流程你会看到密钥生成、固件导入、系统验证和一份足够真实的避坑指南。这套流程适合谁自己编译内核或定制EFI启动文件的Linux用户、需要给设备做供应链加固的运维、希望彻底搞懂Secure Boot细节而不是只会在BIOS里拨开关的人。如果你只是想在Windows 11上开个Secure Boot主板预置的微软密钥就够了不需要自己折腾。但只要你想把这个信任根攥在自己手里下面的每一步都要实打实走一遍。1. 为什么我要自己折腾一套Secure Boot密钥——先搞清楚再动手1.1 Secure Boot到底在防什么Secure Boot是UEFI固件启动阶段的一套强制校验机制它的核心思路很简单从固件加载第一个EFI程序开始每一个准备运行的启动组件都要先过数字签名验证。固件用db签名数据库里的证书去验证引导管理器引导管理器再验证下一个阶段的EFI程序、内核和驱动模块。任何一环的签名不对或镜像被篡改启动流程直接中止。用生活里的例子理解你进机场航站楼每一道闸口都要刷证件证件无效下一段路就过不去。Secure Boot就是计算机加电之后的第一道安检而且这道安检发生在操作系统还没起来的时候所以它的信任根只能放在固件层——也就是主板上的NVRAM变量里。很多人会把Secure Boot和TPM可信平台模块混为一谈这俩虽然常在BIOS的同一个设置页面里出现但作用完全不同。Secure Boot管的是“启动代码是不是可信的”TPM管的是“硬件环境和状态是不是被篡改过”它可以通过PCR度量值来记录启动过程的每一个状态。Windows 11同时强制要求这两个功能开启所以不少人在BIOS里找TPM选项时会顺手把Secure Boot相关设置也翻出来。理解这个区别后面排查启动问题会少走很多弯路。1.2 自己生成密钥和用主板预置密钥有什么区别大多数主板出厂时已经预置了一套Secure Boot密钥里面包含微软的KEK和db证书所以普通用户买回来直接在BIOS里打开Secure Boot开关就能正常用。这套模式下信任的源头是微软和主板厂商操作系统和引导程序只要是他们签过名的就会被放行。但如果你的需求是下面这几种预置密钥就不好用了自己从源码编译内核或者使用第三方EFI引导程序。公司内部有统一的代码签名证书希望所有设备只信任公司签名的启动镜像。对供应链安全比较敏感想把微软的信任根从自己设备上彻底挪走。这时候就需要生成一套完全由你掌控的密钥把自己的证书写进固件密钥库。信任根变了规则也变了只有被你私钥签名的GRUB、内核、驱动才能通过校验。代价是后续所有的启动组件都需要自己签名并管理好私钥工作量明显上去。1.3 完整流程概览与关键术语Secure Boot的密钥体系分四层我直接用一个表格把它的关系说明白密钥全称主要作用由谁授权更新PKPlatform Key平台密钥整个信任链的根控制KEK的更新和PK自身更新PK自己KEKKey Exchange Key密钥交换密钥授权对db和dbx的更新PKdbSignature Database签名数据库列出所有被允许执行的签名者证书或镜像哈希KEKdbxForbidden Signature Database撤销签名数据库列出所有被禁止的签名者证书或镜像哈希优先级高于dbKEK从验证路径来说固件用db判断一个EFI程序能不能跑用dbx做一票否决用KEK验证对db/dbx的更新请求用PK来做最顶层的授权。所以密钥导入顺序必须严格先db再KEK最后PK。一旦最后导入PK固件就会自动从“设置模式Setup Mode”切换到“用户模式User Mode”后面每次对密钥库的修改都必须携带对应层级的数字签名不能再随意覆盖。完整流程可以压缩成六步让固件进入Setup Mode并备份原始密钥。生成你的GUID和PK/KEK/db证书。把证书转换成EFI Signature List格式并生成带认证信息的.auth文件。通过KeyTool或固件界面导入密钥。签名你的GRUB、内核等启动组件。从固件、系统、签名三个层面验证是否真的生效。后面所有章节都是这六步的展开。2. 动手前的三道准备硬件固件检查、拆除默认密钥、准备工具链2.1 确认主板和固件支持情况先说最低门槛。Secure Boot是UEFI规范里的可选功能2012年以后的主流主板基本都带但一些老平台、平板、准系统它的实现非常残缺甚至根本没有这个选项。我在一台2010年左右的第一代Intel UEFI平台上试过固件里只有Secure Boot开关没有自定义密钥导入功能所谓“自定义”选项点进去是一片灰色。这种平台想自建信任根基本不可能建议直接放弃。在已运行的Linux系统里可以用这几个命令快速摸底mokutil --sb-state efivar -l | grep -i SecureBoot dmesg | grep -i -E secure boot|securebootmokutil --sb-state输出SecureBoot enabled代表是开启状态输出SecureBoot disabled则相反。如果你看到的是SecureBoot setup那说明固件正处在Setup Mode是最适合动手操作的状态。虚拟机场景要单独提一下因为这一两年我经常遇到有人在VMware里装Rocky Linux 9.8这类系统时找不到UEFI模式。VMware虚拟机默认可能使用传统BIOS引导而新版Linux和Windows 11安装程序会直接拒绝从BIOSMBR的磁盘布局安装。解决办法是创建虚拟机时在虚拟机设置里把固件类型明确设为“UEFI”而不是“BIOS”并且勾选“启用安全启动”。如果你已经把虚拟机建好了才发现没有UEFI选项最简单的方法是用同发行版新建一台虚拟机或者在现有虚拟机的.vmx文件里手动加一行firmware efi但改完通常要重新安装系统不如直接重建。2.2 进入Setup Mode并备份原始密钥要让固件接受你的新密钥第一步必须让它进入Setup Mode。不同品牌的入口不一样但逻辑大致相同在BIOS设置里找到Secure Boot相关菜单把模式从“Standard”切到“Custom”系统会提示你是否清除所有密钥确认后密钥库清空Secure Boot变成未激活状态这时候固件才允许写入新PK。有一个操作必须做在前面备份原始密钥。如果你后面还想启动Windows或者哪一天想恢复出厂状态这步能救命。在Linux下用efitools包里的efi-readvar就能把当前固件变量导出efi-readvar -v PK -o PK.old.auth efi-readvar -v KEK -o KEK.old.auth efi-readvar -v db -o db.old.auth efi-readvar -v dbx -o dbx.old.auth生成的.auth文件是固件层级可以直接导入的格式把它拷贝到FAT32的U盘里连同后续要用到的KeyTool一起保存。即便你现在不打算保留微软密钥我仍然建议你导出并留档因为以后装双系统或者换引导管理器时很可能用得到。2.3 准备好密钥生成工具链我建议在Linux环境下完成整个流程Windows下的操作体验太割裂而且命令行工具支持不全。推荐用Ubuntu或Fedora的Live USB启动自带内核不影响你当前系统操作也干净。需要安装的工具和对应用途openssl生成X.509证书和密钥。efitools提供cert-to-efi-sig-list、sign-efi-sig-list、efi-readvar、KeyTool这些核心工具。sbsigntool提供sbsign、sbverify用于给EFI二进制签名和验证签名。uuidgen生成GUID属于util-linux包一般系统自带。Debian/Ubuntu系安装命令sudo apt install efitools sbsigntool opensslFedora系sudo dnf install efitools sbsigntool openssl另外准备一个FAT32格式的U盘把KeyTool.efi复制进去。KeyTool.efi是efitools包编译出来的一个可执行EFI程序后面需要在固件启动菜单里直接运行它这是导入密钥最通用的方式比逐个厂商的BIOS菜单省心。3. 密钥生成全流程PK、KEK、db、dbx一个都不能少3.1 生成GUID和PK根密钥整个密钥体系的第一个步骤是生成一个UUID它会被写进每个EFI Signature List的头部相当于你这套信任链的命名空间。固件根据这个GUID区分和管理不同的密钥条目。uuidgen UUID.txt cat UUID.txt比如输出b6e2d5e8-8a13-4f3b-9f0f-1c2a3b4c5d6e。这个值全程都要用建议直接存在变量里GUID$(cat UUID.txt)然后生成平台密钥PK。我的经验是RSA-2048加SHA-256是兼容性最好的组合RSA-3072或4096在部分老固件里会直接导入失败没必要在这个环节追求更大的密钥。openssl req -newkey rsa:2048 -nodes -keyout PK.key -new -x509 -sha256 -days 3650 \ -subj /CNMy Secure Boot Platform Key/ -out PK.pem openssl x509 -outform DER -in PK.pem -out PK.cer-nodes表示私钥不加密方便后续命令行操作-days建议给10年PK一旦被替换整个信任链都要重新签发。实际项目中可以把CN改成你的组织名或设备名方便以后在BIOS里识别。3.2 生成KEK并保留微软密钥KEK的作用是管理db和dbx生成方式和PK几乎一样openssl req -newkey rsa:2048 -nodes -keyout KEK.key -new -x509 -sha256 -days 3650 \ -subj /CNMy Key Exchange Key/ -out KEK.pem openssl x509 -outform DER -in KEK.pem -out KEK.cer到这里有一个让很多人踩坑的决定要不要把微软的KEK一并加进去。如果你有哪怕万分之一的可能以后装Windows双系统我建议保留。虽然理论上Windows启动验证主要走db这一层但各家固件实现差异很大保留微软KEK和微软db是兼容性最稳的做法代价仅仅是在你的信任链上多了一个第三方证书。从当前系统导出微软证书的办法efi-readvar -v KEK -o KEK.ms.cer efi-readvar -v db -o db.ms.cer如果当前机器已经在Setup Mode导致变量被清空也可以从同型号正常机器上导出或者从发行版shim相关包里复制微软证书。这类证书文件名在不同发行版里叫Microsoft Corporation KEK CA 2011.cer、Microsoft Windows Production PCA 2011.cer等等找得到一份就行。3.3 生成db签名数据库及初始白名单db是整个体系里最关键的一层它决定哪些签名者被信任。你自己的引导程序和内核都用这里面的私钥签名所以db密钥也叫你的“代码签名密钥”。openssl req -newkey rsa:2048 -nodes -keyout db.key -new -x509 -sha256 -days 3650 \ -subj /CNMy Signature Database Key/ -out db.pem openssl x509 -outform DER -in db.pem -out db.cer生成完证书把它转成EFI Signature List格式同时把微软的db证书也追加进去cert-to-efi-sig-list -g $GUID db.pem db.esl cert-to-efi-sig-list -g $GUID db.ms.cer ms-db.esl cat ms-db.esl db.esl整理一下db.esl的顺序把自己db.pem的条目放前面微软的放在后面这样后面导入固件时如果条目数有限至少保证自己的第一个被写进去。某些老固件对db条目数量有上限可能是32条或64条这也是为什么我建议你适当清理无用证书不要一股脑全塞进去。3.4 dbx撤销数据库的创建dbx平时几乎用不到它是用来拉黑那些有安全漏洞的引导程序或证书的。千万不要在清空密钥后直接导入一个空的dbx因为固件出厂时往往已经内置了一份针对已知漏洞的撤销列表你清空再导入就等于把安全防线手动拆了。正确做法是先从原系统导出已有的dbxefi-readvar -v dbx -o dbx.old.auth如果你已经找不到原始固件里的dbx那至少保留为空不要随意添加内容。等以后真正需要撤销某个证书时再通过sign-efi-sig-list生成新的dbx.auth并用KEK签名导入。生成一个空dbx.esl的方式touch empty.esl实际操作中efi-updatevar工具可以用-e选项直接设置dbx为空但我更推荐保留旧dbx的备份文件作为基底来追加条目这样更稳妥。4. 固件导入与Secure Boot开启实操4.1 生成固件可识别的.auth认证文件上一节生成的.esl只是证书列表固件变量更新还需要一个带签名的.auth文件用来证明这次更新是被授权的。签名层级必须严格遵守PK用PK.key签KEK用PK.key签db和dbx用KEK.key签。sign-efi-sig-list -g $GUID -k PK.key -c PK.pem PK PK.esl PK.auth sign-efi-sig-list -g $GUID -k PK.key -c PK.pem KEK KEK.esl KEK.auth sign-efi-sig-list -g $GUID -k KEK.key -c KEK.pem db db.esl db.auth sign-efi-sig-list -g $GUID -k KEK.key -c KEK.pem dbx dbx.esl dbx.auth这里有个容易搞混的地方PK.auth虽然是用PK自己的私钥签出来的但在Setup Mode下固件实际上也不强制要求校验签名所以导入顺序才变得那么重要。一旦最后写入PK.auth固件立刻切成User Mode之后想再改db或KEK就必须携带对应私钥签名的更新文件否则会被拒绝。4.2 通过KeyTool导入密钥的完整步骤把KeyTool.efi和生成好的.auth文件全部放进FAT32 U盘开机进入启动菜单选择从U盘启动会自动加载KeyTool。进入KeyTool界面后操作路径如下选择Edit PK/KEK/db/dbx进入密钥管理。先选择db再选择db.auth文件确认追加到签名数据库。这里两个选项要分清Replace会覆盖原有db条目Append会把新条目追加到现有列表后面。如果是Setup Mode下导入选Append即可如果之前固件还有残留条目我建议选Replace避免残留的旧证书造成不稳定。同样的方式导入KEK.auth到KEK变量。最后导入PK.auth到PK变量。退出KeyTool重启机器。重启后进入BIOS设置在Secure Boot页面确认状态已经从“Setup Mode”切换成“User Mode”Secure Boot状态为“Enabled”并且密钥列表里能看到你刚才导入的证书名称。到这一步密钥层面的配置已经完成。4.3 通过固件内置配置界面导入不同厂商在BIOS界面上提供了不同的操作路径不一定要用KeyTool。以几个主流品牌为例华硕Boot→Secure Boot→Key Management里面有Append或Replace选项可以逐项导入db、KEK、PK。技嘉BIOS→Security→Secure Boot。微星Settings→Security→Secure Boot。戴尔/联想等商用机Secure Boot→Custom Mode进去之后可以导入或删除密钥。不管在哪家的界面里都要遵循同一条顺序先db再KEK最后PK。厂商界面里通常会把导入按钮分成Enroll db、Enroll KEK、Enroll PK。还有一个常见选项叫Restore Factory Keys这个千万别在自定义密钥模式下点它会把你导入的密钥清掉并恢复出厂默认。5. 系统验证从启动日志到命令行的三层确认5.1 固件层验证确认状态和密钥指纹密钥导入完成后第一层验证在固件层。进BIOS设置页面确认三个信息Secure Boot状态显示Enabled。当前模式从Setup变成了User。PK、KEK、db列表里有你设置的证书名称。但不能光看名字名字是可以编的。把固件里显示的证书指纹和本地证书的实际指纹对一下才叫真验证。在Linux下取本地证书的SHA-256指纹openssl x509 -in db.pem -noout -fingerprint -sha256然后在BIOS的Key Management页面里查看对应证书的指纹逐字节比对。别高估自己肉眼的细心程度我建议直接把固件页面拍张照用图像放大慢慢对。指纹不一致说明导入过程中出现的可能是厂商界面显示截断这种时候以系统层读取结果为准。5.2 系统层验证mokutil、bootctl与dmesg重启进入Linux桌面或服务器系统后用一组命令做系统层验证mokutil --sb-state返回SecureBoot enabled是符合预期的结果。dmesg | grep -i Secure bootLinux内核启动日志里通常会有类似Secure boot enabled的打印。不同内核版本措辞略有不同有的是SecureBoot enabled有的是Secure boot mode is enabled。如果用的是systemd-boot引导还可以看bootctl status其中Secure Boot: enabled会明确显示当前引导的Secure Boot状态。检测EFI变量的原始值是最底层也最可靠的方式od -An -t u1 /sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c这个变量文件名的GUID是UEFI规范里固定的输出结果里能看到一个1表示Secure Boot开启。再用efi-readvar重新导出当前固件里的密钥信息efi-readvar -v PK efi-readvar -v KEK efi-readvar -v db输出内容里会显示每个密钥条目的属主GUID和证书指纹。对比你本地生成的证书指纹如果完全一致说明固件里保存的就是你生成的密钥。5.3 二进制签名验证确认GRUB和内核确实被信任Secure Boot配置完成后最重要的不是看状态而是确认当前实际启动用的GRUB和内核都是被db里的私钥签名过的。如果你用的是发行版自带内核没有重新签名大概率会在重启后卡在GRUB界面。所以这一步既验证也补漏sbverify --cert db.pem /boot/efi/EFI/GRUB/grubx64.efi sbverify --cert db.pem /boot/vmlinuz-$(uname -r)如果输出类似Signature verification OK说明这个二进制确实由你的db.pem信任的私钥签名。如果输出Signature verification failed那就需要用db.key重新签名sbsign --key db.key --cert db.pem --output /boot/vmlinuz-$(uname -r).signed /boot/vmlinuz-$(uname -r)这里有一个特别容易忽略的细节GRUB的配置文件grub.cfg里linux那一行指向的内核路径必须替换成签名后的镜像文件名。我见过有人签名了新内核文件但GRUB还是加载旧的未签名文件导致Secure Boot校验失败机器反复重启。改完grub.cfg后还要重新生成一份GRUB配置sudo grub-mkconfig -o /boot/grub/grub.cfg如果使用systemd-boot可以直接用上面的sbsign生成签名的vmlinuz文件再修改/boot/loader/entries/下的配置指向新文件名。对于驱动模块和initramfs也需要同样处理。很多发行版在更新内核时会自动重新签名但如果是自己编译的模块或内核需要手动跑签名。一个稳妥的做法是写一个内核更新hook脚本在每次update-initramfs或kernel-install之后自动对/boot目录下所有vmlinuz和.efi文件批量执行sbsign。这不是必须的但能让你以后升级内核时少掉链子。6. 避坑指南我踩过的和你们大概率会踩的坑6.1 导入PK后系统无法启动的排查链路我经历过一次最典型的翻车导入PK后重启屏幕直接停在主板Logo界面然后自动关机。这是因为在Setup Mode下我把固件原有的db清空只导入了自己的db证书但GRUB和内核还是发行版官方签名用的是微软信任链里的证书而我的db里没有这个证书所以固件拒绝加载。排查思路按顺序来进BIOS先把Secure Boot设为Disabled让系统能正常启动。用Live USB启动把系统的/boot和/boot/efi挂载起来。检查当前GRUB和内核的签名者是谁sbverify --cert db.pem /boot/efi/EFI/GRUB/grubx64.efi sbverify --cert db.pem /boot/vmlinuz-$(uname -r)如果验证失败用db.key重新签名并更新grub.cfg指向新文件。重新启动再把Secure Boot打开。如果GRUB和内核都签名了还是无法启动下一步检查/boot里的GRUB模块。特别是用了LVM、LUKS或RAID的情况GRUB会动态加载对应模块这些模块也必须被签名。发行版官方包的模块和内核一般是同一套信任链你重签了内核但没重签模块一样会被卡住。省事的做法是把/boot/grub下所有.mod文件也用db.key全部签一遍或者直接用shim机制避免自己签GRUB模块。6.2 签名格式、算法与固件兼容性我发现很多教程都没强调一个问题固件对签名算法的挑剔程度远超想象。RSA-2048是最好的默认选择RSA-3072以上在部分固件上直接拒绝导入。摘要算法必须用SHA-256SHA-1已经不是安全选择很多固件也不认。证书的Subject字段不要留空或使用奇怪的复杂字符有些固件的界面在显示证书时对UTF-8支持非常差看到一个乱码证书名会让你怀疑自己导错了。导入.auth文件时确认KeyTool界面里选的是Auth File而不是EFI Signature List两种文件后缀都是.auth也可能混实际上sign-efi-sig-list生成的是带签名的认证更新是固件变量级别的而.esl只能作为原始列表被efi-updatevar -e之类的工具使用。另外很多品牌机的固件设置界面只有在“自定义模式”下才会显示密钥导入入口。如果你看到的Secure Boot菜单只有Enabled/Disabled没有Key Management先去把Secure Boot模式从Standard改成Custom选项才会出来。6.3 MOK机制和自编译内核的另一种选择自己编译内核时一个更省事的方式不是改PK/KEK/db而是走shim加MOKMachine Owner Key机制。流程是这样的生成一个专门用于内核签名的公钥对。把公钥证书导入MOKsudo mokutil --import my-kernel.cer重启后shim会进入蓝色MOK管理界面确认导入请求。以后用这个密钥签名自编译内核shim会通过MOK信任它并放行。这个方案的好处是你不需要动PK/KEK主板预置的微软信任链原样保留风险和回滚成本都低。我建议大多数想自己编译内核的用户先用MOK除非你有明确的安全合规要求必须把信任根握在自己手里才值得走完整的PK/KEK/db重建流程。MOK的坑在于每次导入新MOK都要重启一次而且如果MOK密钥丢了同样面临启动链验证失败的问题。6.4 老平台、老显卡、Win7 U盘和虚拟机的边界问题Secure Boot这类技术有一个共性越是老旧的硬件和系统越容易在边界上出问题。我遇到过的几类场景简单做个提示第一代UEFI平台比如2010年前后的H67/P67主板、初代i7系列Secure Boot实现往往不完整有的只有开关没有自定义密钥有的固件变量读写有bug不建议在这类平台上尝试本教程。没有UEFI GOP的老显卡典型像HD6450。Secure Boot开启后固件加载VBIOS环节也可能被校验策略拦截导致开机黑屏或无信号。解决方案是给显卡刷对应型号的UEFI VBIOS或者在BIOS里开启CSMCompatibility Support Module但开启CSM后Secure Boot有时会被强制关闭这点不同主板策略不同需要自己试。Win7的UEFI安装盘在Secure Boot环境下基本不可用因为Win7本身不支持Secure Boot签名验证。如果一定要装Win7建议关闭Secure Boot并启动CSMU盘用支持UEFILegacy双启动的工具制作比如FbinstTool导出的ISO或带EFI引导文件的PE镜像。虚拟机的Secure Boot和物理机逻辑基本一致但VMware里必须在虚拟机设置中手动勾选“启用安全启动”。如果你在VMware 17.6里给Rocky Linux 9.8建虚拟机时看不到UEFI选项大概率是虚拟机创建向导选了旧版兼容性或OS类型过老删除重建并选新版虚拟机配置即可。6.5 固件更新、CMOS清零与密钥备份最后但绝对不是次要的密钥备份和恢复策略这是整个Secure Boot自建方案里最容易被忽略的一环。固件更新BIOS升级时Secure Boot变量大部分情况会保留但确实有主板厂商的bug会在升级后恢复默认密钥导致你之前导入的密钥消失。CMOS清空则更不可控有的主板只是重置普通设置密钥库原样保留有的主板则会连同密钥库一起重置。我在华硕和微星的板子上分别遇到过这两种行为。所以我的建议是把PK.key、KEK.key、db.key这些私钥文件整理好加密压缩后存到至少两个不同的离线介质比如U盘和移动硬盘分别保管。把PK.auth、KEK.auth、db.auth、dbx.auth以及原始的*.cer、*.esl也复制一份放到随U盘里和KeyTool放一起。每次固件更新前用efi-readvar重新导出当前固件变量备份一次防止意外重置后无法恢复。如果哪天连PK私钥也丢了唯一恢复路径是清空固件密钥很多主板叫Clear All Secure Boot Keys或Restore Factory Keys回到Setup Mode重新来过。这意味着之前签名的所有启动组件都要重签一遍。最后再说点实际操作体会整套流程走下来之后我个人的体会是Secure Boot自建密钥并不难真正让人翻车的是对信任链顺序和签名范围的认知不到位。密钥生成和导入充其量半天就能搞定但要把发行版自动更新、内核重签、GRUB模块签名、固件升级这些环节都串起来才是真正的工程量。如果你是第一回折腾强烈建议先在虚拟机里完整跑一遍流程把每种报错都见一遍再上物理机操作。至少你要给自己准备两个问题的答案机器起不来怎么恢复密钥丢了怎么重新掌控想清楚了再动手成功率会高很多。
返回列表