Android 7.0 及更高版本支持文件级加密 (FBE)。采用文件级加密时,可以使用不同的密钥对不同的文件进行加密,也可以对加密文件单独解密。
本文介绍了如何在新设备上启用文件级加密,以及系统应用如何利用 Direct Boot API 尽可能为用户提供最佳、最安全的体验。
所有发布时搭载 Android 10 及更高版本的设备都必须使用文件级加密。
直接启动
借助文件级加密,Android 7.0 中引入了一项称为直接启动的新功能。该功能处于启用状态时,已加密设备在启动后将直接进入锁定屏幕。之前,在使用全盘加密 (FDE) 的已加密设备上,用户在访问任何数据之前都需要先提供凭据,以致于手机只能执行最基本的操作。例如,手机甚至无法接听电话,只能执行基本的紧急拨号操作,而且闹钟无法运行,无障碍服务也不可用。
引入文件级加密 (FBE) 和可以将应用设置为加密感知型应用的新 API 后,这些应用将能够在受限环境中运行。这意味着,应用可以在用户提供凭据之前运行,同时系统仍能保护私密用户信息。
在启用了 FBE 的设备上,每位用户均有两个可供应用使用的存储位置:
- 凭据加密 (CE) 存储空间,这是默认存储位置,只能在用户解锁设备后使用。
- 设备加密 (DE) 存储空间,该存储位置在直接启动模式下和用户解锁设备后均可使用。
这种分离能够使工作资料更加安全,因为这样一来,加密不再只基于启动密码,从而能够同时保护多位用户。
Direct Boot API 允许加密感知型应用访问上述任何一个存储空间。应用生命周期会发生一些变化,以便系统在以下情况下通知应用:用户的 CE 存储空间因用户在锁定屏幕上首次输入凭据而解锁时,或者在工作资料提供工作资料安全质询时。无论是否实现了 FBE,搭载 Android 7.0 的设备都必须支持这些新的 API 和生命周期。不过,如果没有启用 FBE,DE 和 CE 存储空间将始终处于解锁状态。
Android 开源项目 (AOSP) 中提供了 Ext4 和 F2FS 文件系统中的文件级加密的完整实现。在满足相关要求的设备上,只需启用该实现即可使用该功能。选择使用 FBE 的制造商可能想要了解基于所用系统芯片 (SoC) 优化该功能的方法。
AOSP 中的所有必要程序包均已更新为直接启动感知型程序包。不过,如果设备制造商使用的是这些应用的定制版本,则需要确保至少存在能够提供以下服务的直接启动感知型程序包:
- 电话服务和拨号器
- 用于在锁定屏幕中输入密码的输入法
示例和来源
Android 提供了文件级加密的参考实现,其中 vold (system/vold) 负责提供用于管理 Android 上的存储设备和存储卷的功能。添加 FBE 会为 vold 提供一些新命令,以便支持对多位用户的 CE 密钥和 DE 密钥进行密钥管理。除了为使用内核中的文件级加密功能而进行的核心更改外,许多系统程序包(包括锁定屏幕和 SystemUI)也经过了修改,以支持 FBE 和“直接启动”功能。其中包括:
- AOSP 拨号器 (packages/apps/Dialer)
- 桌面时钟 (packages/apps/DeskClock)
- LatinIME (packages/inputmethods/LatinIME)*
- “设置”应用 (packages/apps/Settings)*
- SystemUI (frameworks/base/packages/SystemUI)*
* 使用 defaultToDeviceProtectedStorage
清单属性的系统应用
通过在 AOSP 源代码树的框架或程序包目录中运行 mangrep directBootAware
命令,可以找到更多加密感知型应用和服务的示例。
依赖关系
为了安全地使用 AOSP 的 FBE 实现,设备需要满足以下依赖关系:
- 对 Ext4 加密或 F2FS 加密的内核支持。
- 基于 1.0 或更高版本 HAL 的 Keymaster 支持。不支持 Keymaster 0.3,因为它既不提供必要的功能,也不能保证为加密密钥提供充分保护。
- 必须在可信执行环境 (TEE) 中实现 Keymaster/Keystore 和 Gatekeeper,以便为 DE 密钥提供保护,从而使未经授权的操作系统(刷写到设备上的定制操作系统)无法直接请求 DE 密钥。
- 硬件信任根和启动时验证需要绑定到 Keymaster 初始化进程,以确保未经授权的操作系统无法获取 DE 密钥。
实现
最重要的一点是,应根据直接启动开发者文档将诸如闹钟、电话、无障碍功能等应用设为 android:directBootAware。
内核支持
Android 通用内核 3.18 及更高版本中提供对 Ext4 和 F2FS 加密的内核支持。如需在 5.1 版或更高版本的内核中启用此功能,请使用以下命令行:
CONFIG_FS_ENCRYPTION=y
对于较旧的内核,如果设备的 userdata
文件系统为 Ext4,就使用 CONFIG_EXT4_ENCRYPTION=y
;如果设备的 userdata
文件系统为 F2FS,就使用 CONFIG_F2FS_FS_ENCRYPTION=y
。
如果您的设备支持可合并的存储设备或者将对内部存储设备使用元数据加密,还要启用元数据加密所需的内核配置选项(如元数据加密文档中所述)。
除了对 Ext4 或 F2FS 加密提供功能支持之外,设备制造商还应启用加密加速,以便加快文件级加密的速度并改善用户体验。例如,在 ARM64 设备上,可以通过设置以下内核配置选项来启用 ARMv8 CE(加密扩展)加速:
CONFIG_CRYPTO_AES_ARM64_CE_BLK=y CONFIG_CRYPTO_SHA2_ARM64_CE=y
为了进一步提高性能并降低能耗,设备制造商还可以考虑实现内嵌加密硬件,用于在数据往返于存储设备的途中对数据进行加密/解密。Android 通用内核(4.14 版及更高版本)包含一个框架,允许在提供硬件和供应商驱动程序支持的情况下使用内嵌加密。可以通过设置以下内核配置选项来启用内嵌加密框架:
CONFIG_BLK_INLINE_ENCRYPTION=y CONFIG_FS_ENCRYPTION=y CONFIG_FS_ENCRYPTION_INLINE_CRYPT=y
如果您的设备使用基于 UFS 的存储设备,则还应启用:
CONFIG_SCSI_UFS_CRYPTO=y
如果您的设备使用基于 eMMC 的存储设备,则还应启用:
CONFIG_MMC_CRYPTO=y
启用文件级加密
如需在设备上启用文件级加密 (FBE),就必须在内部存储设备 (userdata
) 上启用 FBE。这也会自动为可合并的存储设备启用 FBE;但是,如有必要,可以覆盖可合并的存储设备的加密格式。
内部存储设备
通过将 fileencryption=contents_encryption_mode[:filenames_encryption_mode[:flags]]
选项添加到 userdata
的 fstab
行 fs_mgr_flags 列,可启用 FBE。此选项用于定义内部存储设备的加密格式。它最多包含三个以英文冒号分隔的参数:
contents_encryption_mode
参数指定将哪种加密算法用于加密文件内容,可为aes-256-xts
或adiantum
。从 Android 11 开始,它也可以留空以指定默认算法,即aes-256-xts
。filenames_encryption_mode
参数指定将哪种加密算法用于加密文件名,可为aes-256-cts
、aes-256-heh
、adiantum
或aes-256-hctr2
。如果不指定,则当contents_encryption_mode
为aes-256-xts
时该参数默认为aes-256-cts
,当contents_encryption_mode
为adiantum
时该参数默认为adiantum
。- Android 11 中新增的
flags
参数是以+
字符分隔的一个标记列表。支持以下标记:v1
标记用于选择第 1 版加密政策;v2
标记用于选择第 2 版加密政策。第 2 版加密政策使用更安全、更灵活的密钥派生函数。如果设备搭载的是 Android 11 或更高版本(由ro.product.first_api_level
确定),则默认选择第 2 版;如果设备搭载的是 Android 10 或更低版本,则默认选择第 1 版。inlinecrypt_optimized
标记用于选择针对无法高效处理大量密钥的内嵌加密硬件进行了优化的加密格式。其具体做法是仅为每个 CE 或 DE 密钥派生一个文件内容加密密钥,而不是为每个文件派生一个。IV(初始化向量)的生成也会相应地进行调整。emmc_optimized
标记与inlinecrypt_optimized
类似,但它还选择了将 IV 限制为 32 位的 IV 生成方法。此标记应仅在符合 JEDEC eMMC v5.2 规范的内嵌加密硬件上使用,因此仅支持 32 位 IV。在其他内嵌加密硬件上,请改用inlinecrypt_optimized
。此标记一律不得在基于 UFS 的存储设备上使用;UFS 规范允许使用 64 位 IV。- 在支持硬件封装密钥的设备上,
wrappedkey_v0
标记允许为 FBE 使用硬件封装的密钥。此标记只能与inlinecrypt
装载选项以及inlinecrypt_optimized
或emmc_optimized
标记结合使用。 - 即使文件系统块大小不是 4096 字节,
dusize_4k
标志也会强制将加密数据单元大小设为 4096 字节。加密数据单元大小是文件内容加密的细分单位。此标志从 Android 15(AOSP 实验版)开始提供。此标志应仅用于启用不支持大于 4096 字节的数据单元的内联加密硬件,并且前提条件是设备使用大于 4096 字节的页面大小且使用 f2fs 文件系统。
如果不使用内嵌加密硬件,则建议对大多数设备采用设置 fileencryption=aes-256-xts
。如果使用的是内嵌加密硬件,则建议对大多数设备采用设置 fileencryption=aes-256-xts:aes-256-cts:inlinecrypt_optimized
(或等效的 fileencryption=::inlinecrypt_optimized
)。在没有采用任何形式的 AES 加速的设备上,可以设置 fileencryption=adiantum
,从而用 Adiantum 代替 AES。
从 Android 14 开始,AES-HCTR2 是采用加速加密指令的设备的首选文件名加密模式。不过,只有较新的 Android 内核支持 AES-HCTR2。在未来的 Android 版本中,AES-HCTR2 计划成为文件名加密的默认模式。如果您的内核支持 AES-HCTR2,可以通过将 filenames_encryption_mode
设置为 aes-256-hctr2
来为其启用这种文件名加密模式。在最简单的情况下,可以使用 fileencryption=aes-256-xts:aes-256-hctr2
完成此操作。
在搭载 Android 10 或更低版本的设备上,也可以使用 fileencryption=ice
来指定使用 FSCRYPT_MODE_PRIVATE
文件内容加密模式。Android 通用内核未实现该模式,但供应商可使用自定义内核补丁程序实现该模式。该模式生成的磁盘格式因供应商而异。在搭载 Android 11 或更高版本的设备上,不允许再使用该模式,而必须使用标准加密格式。
默认情况下,使用 Linux 内核的加密 API 完成文件内容加密。如果想改用内嵌加密硬件,也请添加 inlinecrypt
装载选项。例如,完整的 fstab
行可能如下所示:
/dev/block/by-name/userdata /data f2fs nodev,noatime,nosuid,errors=panic,inlinecrypt wait,fileencryption=aes-256-xts:aes-256-cts:inlinecrypt_optimized
可合并的存储设备
从 Android 9 开始,FBE 和可合并的存储设备可以一起使用。
为 userdata
指定 fileencryption
fstab 选项也会自动为可合并的存储设备启用 FBE 和元数据加密。但是,可以通过设置 PRODUCT_PROPERTY_OVERRIDES
中的属性来覆盖可合并的存储设备上的 FBE 和/或元数据加密格式。
在搭载 Android 11 或更高版本的设备上,请使用以下属性:
ro.crypto.volume.options
(Android 11 中新增的属性),用于为可合并的存储设备选择 FBE 加密格式。其语法与fileencryption
fstab 选项的参数相同,并且使用相同的默认值。请参阅上文关于fileencryption
的建议,了解此处应使用什么设置。ro.crypto.volume.metadata.encryption
用于为可合并的存储设备选择元数据加密格式。请参阅元数据加密文档。
在搭载 Android 10 或更低版本的设备上,请使用以下属性:
ro.crypto.volume.contents_mode
,用于选择内容加密模式。这相当于ro.crypto.volume.options
的第一个以英文冒号分隔的字段。ro.crypto.volume.filenames_mode
,用于选择文件名加密模式。这相当于ro.crypto.volume.options
的第二个以英文冒号分隔的字段,但在搭载 Android 10 或更低版本的设备上默认设置为aes-256-heh
。在大多数设备上,需要将其明确替换为aes-256-cts
。ro.crypto.fde_algorithm
和ro.crypto.fde_sector_size
用于为可合并的存储设备选择元数据加密格式。请参阅元数据加密文档。
与 Keymaster 集成
Keymaster HAL 应作为 early_hal
类的一部分启动。这是因为 FBE 要求 Keymaster 在 post-fs-data
启动阶段(即 vold
设置初始密钥时)准备好处理请求。
排除目录
init
会将系统 DE 密钥应用于 /data
的所有顶级目录,但必须采用非加密格式的目录除外:比如本身就包含系统 DE 密钥的目录,以及包含用户 CE 或 DE 目录的目录。加密密钥以递归方式应用,且无法由子目录替换。
在 Android 11 及更高版本中,init
应用于目录的密钥可以通过 init 脚本中 mkdir
命令的 encryption=<action>
实参来控制。<action>
的可能值记录在 Android init 语言自述文件中。
在 Android 10 中,init
加密操作已硬编码到以下位置:
/system/extras/libfscrypt/fscrypt_init_extensions.cpp
在 Android 9 及更早版本中,该位置如下:
/system/extras/ext4_utils/ext4_crypt_init_extensions.cpp
可以添加例外情况,以防止某些目录被加密。如果进行了此类修改,设备制造商应添加 SELinux 政策,以便仅向需要使用未加密目录的应用授予访问权限(应排除所有不可信的应用)。
目前已知唯一可接受的使用这种方法的情况是在支持旧版 OTA 功能方面。
在系统应用中支持直接启动
将应用设为直接启动感知型应用
为了实现系统应用的快速迁移,新增了两个可在应用级别设置的属性。defaultToDeviceProtectedStorage
属性仅适用于系统应用,directBootAware
属性则适用于所有应用。
<application android:directBootAware="true" android:defaultToDeviceProtectedStorage="true">
应用级别的 directBootAware
属性的含义是将相应应用中的所有组件均标记为加密感知型组件。
defaultToDeviceProtectedStorage
属性用于将默认的应用存储位置重定向到 DE 存储空间(而非 CE 存储空间)。使用此标记的系统应用必须仔细审核存储在默认位置的所有数据,并将敏感数据的路径更改为使用 CE 存储空间。使用此选项的设备制造商应仔细检查要存储的数据,以确保其中不含任何个人信息。
在这种模式下运行时,以下系统 API 可在需要时用于明确管理由 CE 存储空间支持的 Context(这些 API 与设备保护存储空间适用的同类 API 相对应)。
Context.createCredentialProtectedStorageContext()
Context.isCredentialProtectedStorage()
支持多位用户
多用户环境中的每位用户均会获得单独的加密密钥。每位用户均会获得两个密钥:一个 DE 密钥和一个 CE 密钥。用户 0 由于是特殊用户,因此必须先登录设备。这部分适用于使用设备管理功能的情况。
加密感知型应用按照以下方式在用户间互动:INTERACT_ACROSS_USERS
和 INTERACT_ACROSS_USERS_FULL
允许应用在设备上的所有用户间互动。不过,这些应用只能访问已解锁用户的 CE 加密目录。
应用或许能够在 DE 区域间自由互动,但一位用户已解锁并不意味着设备上的所有用户均已解锁。应用在尝试访问这些区域之前,应先检查解锁状态。
每个工作资料用户 ID 也会获得两个密钥:一个 DE 密钥和一个 CE 密钥。当满足工作挑战时,资料用户会被解锁,并且 Keymaster(在 TEE 中)可以提供资料的 TEE 密钥。
处理更新
恢复分区无法访问 userdata 分区中采用 DE 保护的存储空间。强烈建议实现 FBE 的设备使用 A/B 系统更新来支持 OTA 机制。由于可以在正常操作期间安装 OTA 更新,因此恢复分区无需访问已加密存储卷中的数据。
如果使用旧版 OTA 解决方案(该解决方案要求恢复分区访问 userdata
分区中的 OTA 文件),则需要执行以下操作:
- 在
userdata
分区中创建一个顶级目录(例如misc_ne
)。 - 将该顶级目录配置为非加密(请参阅排除目录)。
- 在顶级目录中创建一个用于存放 OTA 更新包的目录。
- 添加 SELinux 规则和文件环境,以便控制对该目录及其内容的访问。应当只有接收 OTA 更新的进程或应用能够对该目录进行读取和写入操作。任何其他应用或进程都不应具有访问该目录的权限。
验证
为了确保实现的 FBE 功能版本能够按预期工作,首先需要运行多个 CTS 加密测试,例如 DirectBootHostTest 和 EncryptionTest。
如果设备搭载的是 Android 11 或更高版本,请同时运行 vts_kernel_encryption_test:
atest vts_kernel_encryption_test
或:
vts-tradefed run vts -m vts_kernel_encryption_test
此外,设备制造商还可以执行以下手动测试。在启用了 FBE 的设备上进行以下手动测试:
- 检查
ro.crypto.state
是否存在- 确认
ro.crypto.state
是否已加密
- 确认
- 检查
ro.crypto.type
是否存在- 确认
ro.crypto.type
是否已设置为file
- 确认
此外,测试人员可以验证 CE 存储空间是否在设备自启动后首次解锁之前已锁定。为此,请使用 userdebug
或 eng
build,为主要用户设置 PIN 码、图案或密码,然后重新启动设备。在解锁设备之前,请运行以下命令来检查主要用户的 CE 存储空间。如果设备使用无头系统用户模式(大多数 Automotive 设备),则主要用户是用户 10,因此请运行:
adb root; adb shell ls /data/user/10
在其他设备(大多数非 Automotive 设备)上,主要用户是用户 0,因此请运行:
adb root; adb shell ls /data/user/0
验证列出的文件名是否采用 Base64 编码,这表示文件名已加密,而且尚无可用于为它们解密的密钥。如果文件名以明文形式列出,则表示存在问题。
我们还建议设备制造商探索如何在其设备或内核上运行针对 fscrypt 的上游 Linux 测试。这些测试是 xfstests 文件系统测试套件的一部分。不过,Android 不对这些上游测试提供官方支持。
AOSP 实现详情
本部分详细介绍了 AOSP 的文件级加密实现,并讲解了文件级加密的运作方式。设备制造商应该无需执行任何更改,即可在其设备上使用 FBE 和“直接启动”功能。
fscrypt 加密
AOSP 实现会用到内核中的“fscrypt”加密(受 ext4 和 f2fs 支持),并通常配置如下:
- 借助采用 XTS 模式的 AES-256 算法加密文件内容
- 借助采用 CBC-CTS 模式的 AES-256 算法加密文件名
Adiantum 加密也受支持。启用 Adiantum 加密后,文件内容和文件名都会使用 Adiantum 进行加密。
fscrypt 支持两种版本的加密政策:版本 1 和版本 2。 版本 1 已废弃;发布时搭载 Android 11 及更高版本的设备所适用的 CDD 要求仅与版本 2 兼容。版本 2 加密政策使用 HKDF-SHA512 从用户空间提供的密钥派生实际加密密钥。
如需详细了解 fscrypt,请参阅上游内核文档。
存储类别
下表列出了 FBE 密钥及其保护的目录的更多详细信息:
存储类别 | 说明 | 目录 |
---|---|---|
未加密 | /data 中无法或不需受 FBE 保护的目录。在使用元数据加密的设备上,这些目录并非真正未加密,而是受元数据加密密钥(等同于系统 DE)保护。 |
|
系统 DE | 未与特定用户关联的设备加密数据 |
|
按启动 | 无需在重新启动后保留的临时系统文件 | /data/per_boot |
用户 CE(内部) | 内部存储设备中按用户的凭据加密数据 |
|
用户 DE(内部) | 内部存储设备中按用户的设备加密数据 |
|
用户 CE(可合并) | 可合并的存储设备中按用户的凭据加密数据 |
|
用户 DE(可合并) | 可合并的存储设备中按用户的设备加密数据 |
|
密钥存储和保护
除了在每次启动时才使用的 FBE 密钥(系统根本不存储)之外,所有其他 FBE 密钥均由 vold
管理并加密存储在磁盘上。下表列出了各种 FBE 密钥的存储位置:
密钥类型 | 密钥位置 | 密钥位置的存储类别 |
---|---|---|
系统 DE 密钥 | /data/unencrypted |
非加密 |
用户 CE(内部)密钥 | /data/misc/vold/user_keys/ce/${user_id} |
系统 DE |
用户 DE(内部)密钥 | /data/misc/vold/user_keys/de/${user_id} |
系统 DE |
用户 CE(可合并)密钥 | /data/misc_ce/${user_id}/vold/volume_keys/${volume_uuid} |
用户 CE(内部) |
用户 DE(可合并)键 | /data/misc_de/${user_id}/vold/volume_keys/${volume_uuid} |
用户 DE(内部) |
如上表所示,大多数 FBE 密钥都存储在由另一个 FBE 密钥加密的目录中。只有先解锁包含这些密钥的存储类别,才能解锁这些密钥。
此外,vold
还会对所有 FBE 密钥应用一层加密。除了用于内部存储设备的 CE 密钥之外,每个密钥都使用自己的 Keystore 密钥(该密钥不在 TEE 外部公开)以 AES-256-GCM 加密。这样一来,除非受信任的操作系统已启动(正如启动时验证所强制执行的那样),否则便无法解锁 FBE 密钥此外,Keystore 密钥还需要设置抗回滚,以便在 Keymaster 支持抗回滚的设备上也能安全地删除 FBE 密钥。为了在抗回滚不可用时能够尽力回退,系统使用存储在与密钥一起存储的 secdiscardable
文件中的 16384 个随机字节的 SHA-512 哈希作为 Keystore 密钥的应用 ID 标记。只有将这些字节全部恢复,才能恢复 FBE 密钥。
用于内部存储设备的 CE 密钥将获得更高级别的保护,以确保在未掌握用户的锁屏知识因素 (LSKF)(PIN 码、图案或密码)、安全密码重置令牌或重新启动时恢复操作的客户端密钥及服务器端密钥的情况下,CE 密钥无法解锁。只允许为工作资料和完全受管设备创建密码重置令牌。
为此,vold
会使用从用户的合成密码派生的 AES-256-GCM 密钥加密每个用于内部存储设备的 CE 密钥。合成密码是为每个用户随机生成的不可变的高熵加密密钥。system_server
中的 LockSettingsService
用于管理合成密码及其保护方式。
为了使用 LSKF 保护合成密码,LockSettingsService
首先会扩展 LSKF(方法是通过 scrypt
传递 LSKF,目标时间约为 25 毫秒且内存用量约为 2 MiB)。由于 LSKF 通常较短,因此该步骤通常无法提供多少安全性。主要的安全保障是下文所述的安全元件 (SE) 或由 TEE 强制执行的速率限制。
如果设备具有安全元件 (SE),则 LockSettingsService
使用 Weaver HAL 将经过扩展的 LSKF 映射到存储在 SE 中的高熵随机密钥。然后,LockSettingsService
将对合成密码进行两次加密:第一次使用从经过扩展的 LSKF 和 Weaver 密钥派生的软件密钥,第二次使用未经身份验证绑定的 Keystore 密钥。这样即可对 LSKF 猜测施加 SE 强制速率限制。
如果设备没有 SE,则 LockSettingsService
会改为使用经过扩展的 LSKF 作为 Gatekeeper 密码。然后,LockSettingsService
将对合成密码进行两次加密:第一次使用从经过扩展的 LSKF 和 secdiscardable 文件的哈希派生的软件密钥,第二次使用经过身份验证绑定至 Gatekeeper 注册的 Keystore 密钥。这样即可对 LSKF 猜测施加 TEE 强制速率限制。
更改 LSKF 后,LockSettingsService
会删除与合成密码和旧 LSKF 的绑定相关的所有信息。在支持 Weaver 或可抗回滚的 Keystore 密钥的设备上,这样做可以保证安全地删除旧绑定。因此,即使用户没有 LSKF,系统也会应用此处所述的保护措施。