第23章 安全编译:SELinux 策略编译、密钥配置、安全启动

说到 Android 安全,很多人第一反应是权限管理、应用沙箱这些。但真正深入到系统底层,你会发现 SELinux密钥配置安全启动 这三样东西,才是 Android 安全的「地基」。

我刚开始接触这部分时,也觉得头大。尤其是 SELinux,光看那些 .te 文件就够晕的。但后来在项目中踩过几次坑,才明白这些机制到底有多重要。今天我就把这三块内容掰开揉碎了讲给你听。

23.1 SELinux 策略编译

SELinux,全称 Security-Enhanced Linux。说白了,它就是给 Android 系统装了一套「门禁系统」。每个进程能访问什么资源,都得按规则来。

在 Android 构建系统中,SELinux 策略的编译是一个独立环节。我们来看它的工作流程。

23.1.1 策略源文件结构

SELinux 策略源文件通常放在 device/system/sepolicy/ 目录下。常见的文件类型有:

  • .te 文件:类型强制文件,定义域和规则
  • .fc 文件:文件上下文,定义文件的安全标签
  • .if 文件:接口文件,封装可复用的规则
  • file_contexts:全局文件上下文映射
  • property_contexts:属性服务上下文映射

举个例子,一个简单的 .te 文件长这样:

# 定义一个名为 my_daemon 的域
type my_daemon, domain;
type my_daemon_exec, exec_type, file_type;

# 允许 my_daemon 读取系统日志
allow my_daemon logd:unix_dgram_socket sendto;
allow my_daemon self:capability { dac_override };

嗯,这里要注意:每个域都必须有明确的 allow 规则。缺一条,你的守护进程可能就跑不起来。

23.1.2 编译流程

在 Android 构建系统中,SELinux 策略的编译由 sepolicy.mkAndroid.bp 驱动。核心步骤是:

  1. 收集所有 .te、.fc、.if 文件
  2. 调用 checkpolicy 工具,将源文件编译成二进制策略文件
  3. 生成 file_contexts.bin,供 init 进程加载
  4. 打包到 boot.img 或 system.img

我记得有一次,我在编译时遇到一个奇怪的错误:

libsepol.report_failure: neverallow on line 123 of system/sepolicy/public/domain.te
  violated by allow my_daemon self:capability { sys_admin };

这个错误的意思是:你写的 allow 规则,违反了全局的 neverallow 约束。说白了,就是系统不允许某个域拥有 sys_admin 权限。我当时查了半天,最后发现是策略文件写错了。

避坑指南:我曾经在项目中因为少写了一条 neverallow 豁免规则,导致编译通过但运行时 SELinux 疯狂报错。建议你在修改策略后,先用 adb shell dmesg | grep avc 检查 AVC 拒绝日志。

23.1.3 策略编译的 Makefile 示例

在 Android 10 及之后版本,策略编译主要通过 Android.bp 配置。但老项目里还是能看到 Makefile 的方式:

# 编译 SELinux 策略
include $(CLEAR_VARS)
LOCAL_MODULE := sepolicy
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/selinux
LOCAL_SRC_FILES := $(LOCAL_PATH)/sepolicy
include $(BUILD_PREBUILT)

我个人习惯在 BoardConfig.mk 中指定策略路径:

BOARD_SEPOLICY_DIRS += \
    device/xxx/sepolicy \
    vendor/xxx/sepolicy

23.2 密钥配置

密钥配置是安全编译的另一大块。Android 使用密钥来签名系统镜像、验证 OTA 更新、保护应用签名等。

23.2.1 密钥类型

密钥名称 用途 存储位置
releasekey 系统镜像签名 build/target/product/security/
platform 平台应用签名 同上
shared 共享 UID 应用签名 同上
media 媒体进程签名 同上
verity dm-verity 哈希表签名 同上

你想想看,如果这些密钥泄露了,攻击者就能伪造系统签名,后果不堪设想。所以 密钥文件必须严格保护,不能提交到版本控制系统中。

23.2.2 配置密钥路径

device/xxx/device.mk 中,你需要指定密钥路径:

# 指定 release 密钥
PRODUCT_DEFAULT_DEV_CERTIFICATE := device/xxx/security/releasekey

# 指定 verity 密钥
PRODUCT_VERITY_SIGNING_KEY := device/xxx/security/verity_key

# 指定 OTA 签名密钥
PRODUCT_OTA_PUBLIC_KEYS := device/xxx/security/ota_key

我个人建议:开发阶段使用 testkey,发布阶段再换成 releasekey。testkey 是公开的,任何人都能拿到,但方便调试。

小技巧:你可以用 make generate_key 命令生成新的密钥对。生成的密钥会放在 build/target/product/security/ 下。

23.3 安全启动

安全启动(Verified Boot)是 Android 安全链的起点。它确保设备从开机到系统加载,每一步都是可信的。

23.3.1 安全启动的流程

我用一张图来展示安全启动的核心逻辑:

Android 安全启动流程 Boot ROM 验证 Bootloader 签名 Bootloader 验证 Boot 分区签名 Boot 分区 (kernel+ramdisk) 验证 System 分区 (dm-verity) System 分区 关键点 • 每级验证上一级的签名 • 使用公钥验证,私钥签名 • 密钥存储在硬件中 • dm-verity 保证分区只读 • 任何篡改都会导致启动失败 ⚠ 常见问题 • 密钥丢失 → 无法签名 • 分区损坏 → 无法验证 • 解锁 bootloader → 安全降级

从图中你能看到,安全启动是一条信任链。每一级都验证下一级的签名,直到系统完全启动。如果任何一级验证失败,设备就会进入 警告状态 或直接关机。

23.3.2 配置安全启动

在构建系统中,安全启动的配置主要在 BoardConfig.mk 中:

# 启用 dm-verity
BOARD_AVB_ENABLE := true

# 指定 AVB 密钥
BOARD_AVB_KEY_PATH := device/xxx/security/avb_key.priv.pem
BOARD_AVB_ALGORITHM := SHA256_RSA4096

# 启用系统分区验证
BOARD_AVB_SYSTEM_KEY_PATH := device/xxx/security/avb_system.priv.pem
BOARD_AVB_SYSTEM_ALGORITHM := SHA256_RSA2048

我记得有一次,我在配置 AVB 时忘了设置 BOARD_AVB_ENABLE := true,结果编译出来的镜像没有签名信息。刷到设备上后,安全启动直接报错,设备变砖了。嗯,从那以后我每次编译前都会检查这个变量。

核心要点:安全启动不是可选项,而是 Android 系统的安全基石。如果你在开发自定义 ROM,务必开启 AVB 并配置正确的密钥。

23.3.3 调试安全启动问题

当安全启动失败时,设备通常会显示一个黄色警告图标。你可以通过以下方式排查:

  • 查看内核日志adb shell dmesg | grep -i avb
  • 检查分区哈希adb shell avbtool verify_image --image /dev/block/boot
  • 验证签名openssl dgst -verify public_key.pem -signature sig.bin boot.img

我曾经在项目中遇到一个诡异的问题:安全启动在开发板上正常,但在量产设备上总是失败。排查了半天,发现是 密钥长度不匹配。开发板用的是 2048 位密钥,但量产设备的 Boot ROM 只支持 4096 位。所以,密钥配置一定要和硬件规格对齐

23.4 总结

这一章我们聊了三个安全编译的核心模块:

  • SELinux 策略编译:从 .te 文件到二进制策略,每一步都要小心 neverallow 约束
  • 密钥配置:releasekey、platform、verity 等密钥各有用途,开发阶段用 testkey,发布阶段换 releasekey
  • 安全启动:信任链从 Boot ROM 开始,每级验证上一级的签名,AVB 是核心配置

说实话,这三块内容单独拎出来都能讲一整天。但作为入门,你只要掌握它们的基本原理和配置方法就够了。在实际项目中,多踩几次坑,自然就熟练了。

我的建议:如果你刚开始接触安全编译,可以先从 SELinux 策略入手。写一个简单的 .te 文件,编译、刷机、测试,感受一下「门禁系统」是怎么工作的。然后再逐步深入到密钥和安全启动。

公众号:蓝海资料掘金营,微信deep3321