1. 引言
在当今物联网和嵌入式设备普及的时代,设备安全已成为开发者必须面对的重要挑战。恶意固件、未授权代码执行等安全威胁时刻威胁着嵌入式设备的安全运行。安全启动(Secure Boot)作为设备安全的第一道防线,通过建立完整的信任链,确保设备从启动开始就运行可信的代码。
技术背景
安全启动基于密码学原理,构建从硬件到应用层的完整信任链。在嵌入式Linux系统中,这通常涉及Bootloader、内核和根文件系统三个关键组件的验证。
本文要解决的具体问题
- 如何实现基于数字签名的启动验证
- Linux内核中的完整性验证机制
- 实际项目中的安全启动配置方法
2. 技术原理
核心概念和工作原理
安全启动的核心是建立信任链(Chain of Trust),其基本流程如下:
- ROM Bootloader:硬件固化的可信根,验证第一级Bootloader
- Bootloader(如U-Boot):验证Linux内核和设备树
- Linux内核:验证内核模块和initramfs
- 用户空间:验证应用程序和配置文件
相关的Linux内核机制
IMA(完整性度量架构)
c
// IMA的核心机制在内核中通过安全子系统实现
struct integrity_iint_cache {
struct rb_node rb_node;
struct inode *inode;
struct ima_template_desc *ima_template;
u64 version;
unsigned long flags;
enum integrity_status ima_status:4;
enum evm_status evm_status:4;
};
EVM(扩展验证模块)
EVM为文件系统提供完整性保护,通过HMAC或数字签名验证文件元数据。
3. 实战实现
具体的实现步骤和方法
步骤1:准备密钥对
bash
# 生成RSA密钥对
openssl genrsa -out private_key.pem 2048
openssl rsa -in private_key.pem -pubout -out public_key.pem
# 将公钥转换为U-Boot格式
openssl rsa -in private_key.pem -outform DER -pubout -out public_key.der
步骤2:配置U-Boot支持安全启动
makefile
# U-Boot配置
CONFIG_FIT_SIGNATURE=y
CONFIG_RSA=y
CONFIG_CMD_RSASIGN=y
CONFIG_OF_SEPARATE=y
步骤3:配置Linux内核完整性验证
makefile
# 内核配置选项
CONFIG_IMA=y
CONFIG_IMA_MEASURE_PCR_IDX=10
CONFIG_IMA_AUDIT=y
CONFIG_IMA_LSM_RULES=y
CONFIG_EVM=y
CONFIG_EVM_ATTR_FSUUID=y
关键配置和参数说明
- CONFIG_IMA_MEASURE_PCR_IDX:指定使用的PCR寄存器索引
- CONFIG_EVM_ATTR_FSUUID:使用文件系统UUID作为EVM密钥派生参数
- CONFIG_FIT_SIGNATURE:启用FIT镜像签名验证
4. 代码示例
示例1:内核中的IMA策略实现
c
#include <linux/integrity.h>
#include <linux/ima.h>
#include <linux/fs.h>
/* 自定义IMA策略规则 */
static struct ima_rule_entry custom_ima_rules[] = {
{.action = MEASURE, .func = FILE_CHECK,
.mask = MAY_READ, .uid = GLOBAL_ROOT_UID,
.flags = IMA_FUNC | IMA_MASK | IMA_UID},
{.action = MEASURE, .func = MODULE_CHECK,
.mask = MAY_EXEC, .uid = GLOBAL_ROOT_UID,
.flags = IMA_FUNC | IMA_MASK | IMA_UID},
{.action = MEASURE, .func = FIRMWARE_CHECK,
.mask = MAY_EXEC, .uid = GLOBAL_ROOT_UID,
.flags = IMA_FUNC | IMA_MASK | IMA_UID},
};
/* 初始化IMA策略 */
static int __init ima_custom_policy_setup(char *str)
{
int i, result;
for (i = 0; i < ARRAY_SIZE(custom_ima_rules); i++) {
result = ima_lsm_rule_init(&custom_ima_rules[i],
LSM_OBJ_UNKNOWN, NULL, 0);
if (result < 0) {
pr_err("IMA: failed to add rule %d, error %d\n", i, result);
return result;
}
list_add_tail(&custom_ima_rules[i].list, &ima_policy_rules);
}
return 1;
}
__setup("ima_policy=custom", ima_custom_policy_setup);
示例2:用户空间签名验证工具
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/sha.h>
#define MAX_BUFFER_SIZE 4096
/* 验证文件签名 */
int verify_file_signature(const char *filename,
const char *signature_file,
const char *public_key_file)
{
FILE *file = NULL, *sig_file = NULL, *key_file = NULL;
unsigned char file_hash[SHA256_DIGEST_LENGTH];
unsigned char signature[256];
unsigned char buffer[MAX_BUFFER_SIZE];
size_t bytes_read;
RSA *rsa_key = NULL;
int result = 0;
SHA256_CTX sha256;
SHA256_Init(&sha256);
/* 计算文件哈希 */
file = fopen(filename, "rb");
if (!file) {
fprintf(stderr, "无法打开文件: %s\n", filename);
goto cleanup;
}
while ((bytes_read = fread(buffer, 1, MAX_BUFFER_SIZE, file)) > 0) {
SHA256_Update(&sha256, buffer, bytes_read);
}
SHA256_Final(file_hash, &sha256);
/* 读取签名 */
sig_file = fopen(signature_file, "rb");
if (!sig_file) {
fprintf(stderr, "无法打开签名文件: %s\n", signature_file);
goto cleanup;
}
fread(signature, 1, 256, sig_file);
/* 加载公钥 */
key_file = fopen(public_key_file, "rb");
if (!key_file) {
fprintf(stderr, "无法打开公钥文件: %s\n", public_key_file);
goto cleanup;
}
rsa_key = PEM_read_RSA_PUBKEY(key_file, NULL, NULL, NULL);
if (!rsa_key) {
fprintf(stderr, "无法加载公钥\n");
goto cleanup;
}
/* 验证签名 */
result = RSA_verify(NID_sha256, file_hash, SHA256_DIGEST_LENGTH,
signature, 256, rsa_key);
if (result == 1) {
printf("签名验证成功\n");
} else {
printf("签名验证失败\n");
ERR_print_errors_fp(stderr);
}
cleanup:
if (file) fclose(file);
if (sig_file) fclose(sig_file);
if (key_file) fclose(key_file);
if (rsa_key) RSA_free(rsa_key);
return result;
}
int main(int argc, char *argv[])
{
if (argc != 4) {
printf("用法: %s <文件> <签名文件> <公钥文件>\n", argv[0]);
return 1;
}
return verify_file_signature(argv[1], argv[2], argv[3]) ? 0 : 1;
}
5. 调试与优化
常见问题排查方法
问题1:内核验证失败
bash
# 查看内核日志
dmesg | grep -i "secure\|ima\|evm\|signature"
# 检查IMA测量结果
cat /sys/kernel/security/ima/ascii_runtime_measurements
问题2:U-Boot签名验证失败
bash
# 在U-Boot中启用调试
setenv verify yes
printenv bootargs
性能优化建议
- 哈希算法选择:在资源受限设备上使用SHA256而非SHA512
- 策略优化:只为关键文件启用完整性验证
- 缓存策略:合理配置IMA测量缓存大小
c
// 优化IMA缓存配置
#define IMA_MAX_MEASURE_ENTRIES 1024
static unsigned int ima_max_entries = IMA_MAX_MEASURE_ENTRIES;
6. 总结
技术要点回顾
- 信任链建立:从硬件可信根到应用层的完整验证
- 密码学基础:非对称加密和数字签名技术的应用
- 内核机制:IMA和EVM提供的完整性保护
- 实践方法:密钥管理、配置策略和调试技巧
进一步学习方向
- TPM集成:学习可信平台模块的深度集成
- 远程证明:实现设备状态的远程验证
- 安全更新:研究安全固件更新机制
- 硬件安全:探索HSM、Secure Element等硬件安全方案
安全启动是嵌入式系统安全的基石,通过本文介绍的技术方案,开发者可以为设备构建可靠的安全防护体系。在实际项目中,需要根据具体的安全需求和资源约束,合理选择和配置各项安全机制。