EDK II固件签名工具:使用OpenSSL生成UEFI兼容签名
【免费下载链接】edk2 EDK II 项目地址: https://gitcode.com/gh_mirrors/ed/edk2
引言:UEFI固件签名的重要性与挑战
在现代计算机系统中,固件(Firmware)作为硬件与操作系统之间的桥梁,其安全性直接关系到整个系统的可信启动(Trusted Boot)流程。UEFI(Unified Extensible Firmware Interface,统一可扩展固件接口)规范引入了严格的固件签名验证机制,以防止恶意代码在系统启动早期被执行。然而,许多开发者在实际操作中仍面临签名格式不兼容、工具链配置复杂等问题。本文将系统讲解如何使用OpenSSL工具生成符合UEFI规范的固件签名,并集成到EDK II(EFI Development Kit II)项目中,帮助开发者解决固件签名验证失败的痛点。
读完本文后,您将能够:
- 理解UEFI固件签名的核心原理与PKCS#7格式要求
- 使用OpenSSL生成符合UEFI规范的密钥对与证书链
- 掌握EDK II中固件签名验证的实现逻辑
- 完成自定义固件镜像的签名与验证全流程
UEFI固件签名基础
UEFI安全启动架构
UEFI安全启动(Secure Boot)是一种确保系统仅加载经过签名的可信固件和操作系统组件的机制。其核心依赖于公钥基础设施(Public Key Infrastructure,PKI),通过验证固件镜像的数字签名来判断其合法性。
UEFI签名格式要求
UEFI规范要求固件签名必须采用PKCS#7(Public-Key Cryptography Standards #7)格式,也称为CMS(Cryptographic Message Syntax)。这种格式支持对固件镜像进行数字签名,并包含必要的证书信息,以便验证者确认签名者的身份。
在EDK II中,与PKCS#7签名验证相关的核心函数包括:
BOOLEAN
Pkcs7Verify (
IN CONST UINT8 *Pkcs7Data,
IN UINTN Pkcs7DataSize,
IN CONST UINT8 *TrustedCert,
IN UINTN TrustedCertSize,
IN CONST UINT8 *Data,
IN UINTN DataSize
);
该函数实现于SecurityPkg中,用于验证给定数据的PKCS#7签名是否有效。其参数包括PKCS#7签名数据、可信证书、待验证数据及其大小。
使用OpenSSL生成UEFI兼容签名
环境准备
在开始之前,请确保您的系统中已安装以下工具:
- OpenSSL 1.1.1或更高版本
- EDK II开发环境
- Git(用于获取EDK II源代码)
首先,克隆EDK II仓库:
git clone https://gitcode.com/gh_mirrors/ed/edk2.git
cd edk2
git submodule update --init
生成密钥对与证书
步骤1:生成私钥
使用以下命令生成2048位RSA私钥:
openssl genrsa -out uefi_private.key 2048
步骤2:创建证书签名请求(CSR)
openssl req -new -key uefi_private.key -out uefi_cert.csr \
-subj "/CN=UEFI Firmware Signing Key/O=Your Organization/OU=Firmware Team/L=Your City/ST=Your State/C=Your Country"
步骤3:自签名证书
由于UEFI安全启动通常使用自签名证书(在开发环境中),我们可以直接用私钥对CSR进行签名,生成自签名证书:
openssl x509 -req -days 3650 -in uefi_cert.csr -signkey uefi_private.key -out uefi_cert.pem
步骤4:转换证书格式
UEFI固件通常期望证书以DER(Distinguished Encoding Rules)格式存储,而非PEM(Privacy-Enhanced Mail)格式。使用以下命令进行转换:
openssl x509 -in uefi_cert.pem -out uefi_cert.der -outform DER
对固件镜像进行签名
假设我们有一个名为firmware.bin的固件镜像需要签名,使用以下步骤生成PKCS#7签名:
步骤1:生成固件镜像的哈希值
UEFI签名通常对固件镜像的哈希值进行签名,而非直接对整个镜像签名。计算SHA256哈希:
openssl dgst -sha256 -binary -out firmware.sha256 firmware.bin
步骤2:生成PKCS#7签名
openssl cms -sign -in firmware.sha256 -inkey uefi_private.key -signer uefi_cert.pem \
-out firmware.sig -outform DER -nosmimecap -binary -digest sha256
这里:
-sign: 指定进行签名操作-in: 输入文件(固件哈希值)-inkey: 私钥文件-signer: 签名者证书-out: 输出签名文件-outform DER: 指定输出格式为DER-nosmimecap: 不添加S/MIME功能扩展-binary: 处理二进制数据-digest sha256: 使用SHA256哈希算法
步骤3:组合固件镜像与签名
在UEFI中,签名通常附加在固件镜像之后,形成一个包含镜像数据和签名的完整文件。可以使用以下命令将固件镜像和签名组合:
cat firmware.bin firmware.sig > firmware_signed.bin
EDK II中的签名验证实现
EDK II签名验证核心代码分析
在EDK II中,SecurityPkg提供了PKCS#7签名验证的实现。以下是FmpAuthenticatedHandlerPkcs7函数的核心代码片段,该函数用于处理FMP(Firmware Management Protocol)胶囊镜像的PKCS#7签名验证:
RETURN_STATUS
FmpAuthenticatedHandlerPkcs7 (
IN EFI_FIRMWARE_IMAGE_AUTHENTICATION *Image,
IN UINTN ImageSize,
IN CONST UINT8 *PublicKeyData,
IN UINTN PublicKeyDataLength
)
{
RETURN_STATUS Status;
BOOLEAN CryptoStatus;
VOID *P7Data;
UINTN P7Length;
VOID *TempBuffer;
P7Length = Image->AuthInfo.Hdr.dwLength - (OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData));
P7Data = Image->AuthInfo.CertData;
// 分配临时缓冲区,用于组合待验证数据和单调计数器
TempBuffer = AllocatePool (ImageSize - Image->AuthInfo.Hdr.dwLength);
if (TempBuffer == NULL) {
return RETURN_OUT_OF_RESOURCES;
}
// 复制固件镜像数据
CopyMem (
TempBuffer,
(UINT8 *)Image + sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength,
ImageSize - sizeof (Image->MonotonicCount) - Image->AuthInfo.Hdr.dwLength
);
// 附加单调计数器(UEFI规范要求)
CopyMem (
(UINT8 *)TempBuffer + (ImageSize - sizeof (Image->MonotonicCount) - Image->AuthInfo.Hdr.dwLength),
&Image->MonotonicCount,
sizeof (Image->MonotonicCount)
);
// 执行PKCS#7签名验证
CryptoStatus = Pkcs7Verify (
P7Data,
P7Length,
PublicKeyData,
PublicKeyDataLength,
(UINT8 *)TempBuffer,
ImageSize - Image->AuthInfo.Hdr.dwLength
);
FreePool (TempBuffer);
if (!CryptoStatus) {
return RETURN_SECURITY_VIOLATION;
}
return RETURN_SUCCESS;
}
关键数据结构
UEFI固件签名验证涉及以下关键数据结构:
- EFI_FIRMWARE_IMAGE_AUTHENTICATION:包含固件镜像的认证信息,定义于
MdePkg/Include/Guid/FirmwareAuthentication.h。
typedef struct {
UINT64 MonotonicCount;
WIN_CERTIFICATE_UEFI_GUID AuthInfo;
} EFI_FIRMWARE_IMAGE_AUTHENTICATION;
- WIN_CERTIFICATE_UEFI_GUID:UEFI扩展的Windows证书格式,用于携带PKCS#7签名数据。
typedef struct {
WIN_CERTIFICATE Hdr;
EFI_GUID CertType;
UINT8 CertData[1];
} WIN_CERTIFICATE_UEFI_GUID;
其中,CertType字段通常设置为gEfiCertPkcs7Guid,表示证书数据为PKCS#7格式。
集成签名验证到EDK II项目
配置EDK II项目
要在EDK II项目中启用签名验证,需要在平台的DSC(Distribution Package Description)文件中包含SecurityPkg并配置相关PCD(Platform Configuration Database):
[Components]
SecurityPkg/Library/FmpAuthenticationLibPkcs7/FmpAuthenticationLibPkcs7.inf
[PcdsFixedAtBuild]
gEfiSecurityPkgTokenSpaceGuid.PcdOptionRomImageVerificationPolicy|0x02
gEfiSecurityPkgTokenSpaceGuid.PcdDriverSignatureVerificationPolicy|0x02
添加可信证书
将之前生成的uefi_cert.der证书添加到EDK II项目中,通常放置在SecurityPkg/Library/PlatformPKProtectionLib/目录下,并在INF文件中引用:
[Binaries.common]
FILE|uefi_cert.der|RAW|$(PLATFORM_PACKAGE)/Certificates/
实现证书验证逻辑
在平台初始化代码中,注册可信证书并启用签名验证:
EFI_STATUS
EFIAPI
PlatformInitializeSecurity (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UINT8 *TrustedCert;
UINTN TrustedCertSize;
// 从固件中读取可信证书
Status = ReadTrustedCertificate (&TrustedCert, &TrustedCertSize);
if (EFI_ERROR (Status)) {
return Status;
}
// 注册可信证书到安全管理器
Status = SystemTable->RuntimeServices->SetVariable (
L"PK",
&gEfiGlobalVariableGuid,
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS,
TrustedCertSize,
TrustedCert
);
FreePool (TrustedCert);
return Status;
}
常见问题与解决方案
签名验证失败的排查流程
当固件签名验证失败时,可按以下步骤排查:
常见错误及解决方法
-
错误:PKCS7Verify返回FALSE
- 原因:签名数据损坏或证书不匹配
- 解决:重新生成签名,确保使用正确的私钥和证书
-
错误:证书格式不受支持
- 原因:证书不是DER格式或使用了不受支持的加密算法
- 解决:使用
openssl x509命令将证书转换为DER格式,确保使用RSA算法
-
错误:单调计数器值不匹配
- 原因:固件镜像中的单调计数器值与签名时使用的值不一致
- 解决:确保在签名时包含正确的单调计数器值
总结与展望
本文详细介绍了UEFI固件签名的原理、使用OpenSSL生成符合UEFI规范签名的方法,以及EDK II中签名验证的实现。通过遵循这些步骤,开发者可以确保其固件镜像能够通过UEFI安全启动验证,从而提高系统的安全性。
随着UEFI规范的不断演进,固件签名机制也在不断发展。未来,我们可以期待更先进的签名算法(如ECC椭圆曲线加密)和更严格的安全策略在UEFI中的应用。作为开发者,持续关注UEFI规范更新和安全最佳实践至关重要。
参考资料
- UEFI Specification Version 2.9, Intel Corporation, 2021
- EDK II Developer's Guide, TianoCore Project
- OpenSSL Cryptography and TLS Toolkit, OpenSSL Project
- "Trusted Boot in UEFI" by Michael Brown, 2018
- TianoCore SecurityPkg Documentation, https://github.com/tianocore/edk2/tree/master/SecurityPkg
【免费下载链接】edk2 EDK II 项目地址: https://gitcode.com/gh_mirrors/ed/edk2
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



