Android 文件级加密

Android 7.0 及更高版本支持文件级加密 (FBE)。采用文件级加密时,可以使用不同的密钥对不同的文件进行加密,并且可以对这些文件进行单独解密。

本文介绍了如何在新设备上启用文件级加密,以及如何更新系统应用,以充分利用新的 Direct Boot API 并尽可能为用户提供最佳、最安全的体验。

直接启动

借助文件级加密,Android 7.0 中引入了一项称为直接启动的新功能。该功能处于启用状态时,已加密设备在启动后将直接进入锁定屏幕。之前,在使用全盘加密 (FDE) 的已加密设备上,用户在访问任何数据之前都需要先提供凭据,从而导致手机无法执行除最基本操作之外的所有其他操作。例如,闹钟无法运行,无障碍服务不可用,手机无法接电话,而只能进行基本的紧急拨号操作。

引入文件级加密 (FBE) 和新 API 后,便可以将应用设为加密感知型应用,这样一来,它们将能够在受限环境中运行。这些应用将可以在用户提供凭据之前运行,同时系统仍能保护私密用户信息。

在启用了 FBE 的设备上,每位用户均有两个可供应用使用的存储位置:

  • 凭据加密 (CE) 存储空间:这是默认存储位置,只有在用户解锁设备后才可用。
  • 设备加密 (DE) 存储空间:在直接启动模式期间以及用户解锁设备后均可用。

这种区分能够使工作资料更加安全,因为这样一来,加密不再只基于启动时密码,从而能够同时保护多位用户。

Direct Boot API 允许加密感知型应用访问上述每个区域。应用生命周期会发生一些变化,以便在用户的 CE 存储空间因用户在锁定屏幕上首次输入凭据而解锁时,或者在工作资料提供工作挑战时,通知应用。无论是否实现了 FBE,运行 Android 7.0 的设备都必须要支持这些新的 API 和生命周期。不过,如果没有 FBE,DE 和 CE 存储空间将始终处于解锁状态。

Android 开放源代码项目 (AOSP) 中提供了 EXT4 文件系统中的文件级加密的完整实现。在满足相关要求的设备上,只需启用该实现即可使用该功能。选择使用 FBE 的制造商可能想要了解根据所用系统芯片 (SoC) 优化该功能的方法。

AOSP 中的所有必要程序包均已更新为直接启动感知型程序包。不过,如果设备制造商使用的是这些应用的定制版本,则需要确保至少存在能够提供以下服务的直接启动感知型程序包:

  • 电话服务和拨号器
  • 用于在锁定屏幕中输入密码的输入法

示例和源代码

Android 提供了文件级加密的参考实现,其中 vold (system/vold) 负责提供用于管理 Android 上的存储设备和存储卷的功能。添加 PDE 会为 vold 提供一些新命令,以便支持对多位用户的 CE 密钥和 DE 密钥进行密钥管理。除了为使用内核中的 EXT4 加密功能而进行的核心更改外,许多系统程序包(包括锁定屏幕和 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 加密的内核支持(内核配置选项:EXT4_FS_ENCRYPTION)
  • 基于 1.0 或 2.0 版 HAL 的 Keymaster 支持。不支持 Keymaster 0.3,因为它既不提供必要的功能,也不能保证为加密密钥提供充分保护。
  • 必须在可信执行环境 (TEE) 中实现 Keymaster/Keystore 和 Gatekeeper,以便为 DE 密钥提供保护,从而使未经授权的操作系统(刷到设备上的定制操作系统)无法直接请求 DE 密钥。
  • 内核加密性能必须要在使用 AES XTS 时至少达到 50MB/s,以确保良好的用户体验。
  • 硬件信任根验证启动需要绑定到 Keymaster 初始化进程,以确保未经授权的操作系统无法获取设备加密凭据。

注意:存储政策会应用到文件夹及其所有子文件夹。对于以未加密形式存入 OTA 文件夹以及存入系统解密密钥存放文件夹的内容,制造商应加以限制。大多数内容都应存放在凭据加密存储空间(而非设备加密存储空间)内。

实现

最重要的一点是,应根据直接启动开发者文档将诸如闹钟、电话、无障碍功能等应用设为 android:directBootAware。

内核支持

AOSP 提供的文件级加密实现会用到 Linux 4.4 内核中的 EXT4 加密功能。推荐的解决方案是使用基于 4.4 或更高版本的内核。EXT4 加密还反向移植到了 Android 公共代码库内的 3.10 内核以及受支持的 Nexus 内核。

对于希望将该功能引入到其设备内核的设备制造商来说,AOSP 内核/公共 Git 代码库中的 android-3.10.y 分支可作为一个很好的着手点。不过,务必要在最新的稳定版 Linux 内核(目前是 linux-4.6)中应用 EXT4 和 JBD2 项目提供的最新补丁程序。Nexus 设备内核已经包含其中很多补丁程序。

设备 内核
Android Common kernel/common android-3.10.y (Git)
Nexus 5X (bullhead) kernel/msm android-msm-bullhead-3.10-n-preview-2 (Git)
Nexus 6P (angler) kernel/msm android-msm-angler-3.10-n-preview-2 (Git)

请注意,以上每个内核都使用了到 3.10 的反向移植。Linux 3.18 中的 EXT4 和 JBD2 驱动程序已移植到基于 3.10 的现有内核中。由于内核各个部分之间存在依赖关系,因此这种反向移植会导致系统停止支持 Nexus 设备不使用的一些功能。其中包括:

  • EXT3 驱动程序,不过 EXT4 仍可以装载并使用 EXT3 文件系统
  • 全局文件系统 (GFS) 支持
  • EXT4 中的 ACL 支持

除了对 EXT4 加密提供功能支持外,设备制造商还可以考虑实现加密加速功能,以便加快文件级加密的速度并改善用户体验。

启用文件级加密

通过将不带参数的 fileencryption 标记添加到 userdata 分区最后一列的 fstab 行中,可以启用 FBE。要查看示例,请访问 https://android.googlesource.com/device/lge/bullhead/+/nougat-release/fstab_fbe.bullhead

测试设备上的 FBE 实现情况时,可以指定以下标记:forcefdeorfbe="<path/to/metadata/partition>"

此标记会将设备设为使用 FDE,但允许针对开发者转换为 FBE。默认情况下,此标记的行为类似于 forceencrypt,会使设备进入 FDE 模式。不过,它将提供一个调试选项,以便在开发者预览中允许将设备切换到 FBE 模式。另外,还可以使用以下命令在 fastboot 中启用 FBE:

$ fastboot --wipe-and-use-fbe

此标记仅用于开发目的,可提供一个在实际 FBE 设备发布之前演示 FBE 功能的平台。此标记在将来可能会被弃用。

与 Keymaster 集成

vold 负责处理密钥生成和内核密钥环管理工作。AOSP 的 FBE 实现要求设备支持 1.0 或更高版本的 Keymaster HAL。更低版本的 Keymaster HAL 不受支持。

首次启动时,在启动过程的早期阶段会生成并安装用户 0 的密钥。到 init 的 on-post-fs 阶段完成时,Keymaster 必须已做好处理请求的准备。在 Nexus 设备上,这是通过设置一个脚本块处理的:

  • 确保 Keymaster 在 /data 装载之前启动
  • 指定文件加密算法套件:AOSP 的文件级加密实现会用到采用 XTS 模式的 AES-256 算法

    注意:所有加密都基于采用 XTS 模式的 AES-256 算法。XTS 的定义方式决定了它需要两个 256 位密钥;因此实际上 CE 密钥和 DE 密钥都是 512 位密钥。

加密政策

EXT4 加密在目录级应用加密政策。首次创建设备的 userdata 分区时,会由 init 脚本应用基本结构和政策。这些脚本将触发创建首位用户(用户 0)的 CE 密钥和 DE 密钥,并定义要使用这些密钥加密哪些目录。创建其他用户和资料时,会生成必要的其他密钥并将其存储在密钥代码库中;接下来会创建它们的凭据和设备存储位置,并且加密政策会将这些密钥关联到相应目录。

在 AOSP 当前提供的文件级加密实现中,加密政策被硬编码到了以下位置:

/system/extras/ext4_utils/ext4_crypt_init_extensions.cpp

可以在该文件中添加例外情况,以彻底防止特定目录被加密,具体方法是将相应目录添加到 directories_to_exclude列表中。如果进行了此类修改,设备制造商应添加 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 密钥。

处理更新

恢复分区无法访问用户数据分区中采用 DE 保护的存储空间。强烈建议实现 FBE 的设备支持新版 OTA 机制(采用即将推出的 A/B 系统更新方式)。由于可以在正常操作期间应用 OTA 更新,因此恢复分区无需访问已加密存储卷中的数据。

如果使用旧版 OTA 解决方案(该解决方案要求恢复分区访问用户数据分区中的 OTA 文件),则需要执行以下操作:

  • 在用户数据分区中创建一个顶级目录(例如“misc_ne”)。
  • 将该顶级目录添加到加密政策例外情况中(请参阅上文中的加密政策)。
  • 在该目录中创建一个用于存放 OTA 更新包的目录。
  • 添加 SELinux 规则和文件环境,以便控制对该文件夹及其内容的访问。应当只有接收 OTA 更新的进程或应用能够对该文件夹进行读取和写入操作。
  • 任何其他应用或进程都不应具有访问该文件夹的权限。

在该文件夹中创建一个目录,以便存放 OTA 更新包。

验证

为了确保实现的 FBE 功能版本能够按预期工作,需要部署多种 CTS 加密测试

可以顺利为您的主板编译内核后,请另行为 x86 编译内核并在 QEMU 下运行该内核,然后您就可以使用以下命令通过 xfstest 进行测试了:

 
 
$ kvm-xfstests -c encrypt -g auto

此外,设备制造商可以在启用了 FBE 的设备上进行以下手动测试:

  • 检查 ro.crypto.state 是否存在
    • 确认 ro.crypto.state 是否已加密
  • 检查 ro.crypto.type 是否存在
    • 确认 ro.crypto.type 是否已设为 file

此外,测试人员可以在主用户已设置锁定屏幕的情况下启动一个 userdebug 实例。然后通过 adb shell 命令进入设备,并使用 su 获得 root 权限。确认 /data/data 中是否包含加密的文件名;如果没有,则表示存在问题。

AOSP 实现详情

本部分详细介绍了 AOSP 的文件级加密实现,并讲解了文件级加密的运作方式。设备制造商应该无需执行任何更改,即可在其设备上使用 FBE 和“直接启动”功能。

EXT4 加密

AOSP 的文件级加密实现会用到内核中的 EXT4 加密功能,并配置为:

  • 借助采用 XTS 模式的 AES-256 算法加密文件内容
  • 借助采用 CBC-CTS 模式的 AES-256 算法加密文件名

密钥派生

硬盘加密密钥(512 位 AES-XTS 密钥)以加密形式存储:通过另一个存放在 TEE 中的密钥(256 位 AES-GCM 密钥)进行加密。要使用该 TEE 密钥,需要具备以下三项:

  • 身份验证令牌
  • 扩展凭据
  • secdiscardable hash

身份验证令牌是一个经过加密和身份验证的令牌,由 Gatekeeper 在用户成功登录时生成。除非用户提供的身份验证令牌正确无误,否则 TEE 将拒绝用户使用该密钥。如果用户没有任何凭据,则不使用也不需要使用身份验证令牌。

扩展凭据是使用 scrypt 算法进行加盐和扩展处理的用户凭据。实际上,凭据在被传递到 vold(以便传递到 scrypt)之前,会在锁定设置服务中接受一次哈希处理。扩展凭据会以加密形式绑定到 TEE 中的相应密钥,并享有适用于 KM_TAG_APPLICATION_ID 的所有保证。如果用户没有凭据,则不使用也不需要使用扩展凭据。

secdiscardable hash 是 16 KB 随机文件的 512 位哈希,和用于重建相应密钥的其他信息(例如种子)存储在一起。在相应密钥被删除时,该文件会一并被安全地删除,或以新的方式被加密;采用这种附加的保护措施后,攻击者要恢复相应密钥,必须要先恢复这个被安全删除的文件中的每一个位。secdiscardable hash 同样会以加密形式绑定到 TEE 中的相应密钥,并享有适用于 KM_TAG_APPLICATION_ID 的所有保证。请参阅面向 Keystore 实现人员的参考资料

---------------------------------------------------------------------------------------------------------

这边通过一个示例来验证上面的相关操作,创建接收 android.intent.action.LOCKED_BOOT_COMPLETED和android.intent.action.BOOT_COMPLETED的receiver,并在android.intent.action.LOCKED_BOOT_COMPLETED的receiver中创建相关文件。比如在确认机器 在:

m get-fbe-mode 为emulated

的机器上,机器设置PIN加密,然后重启,可以在/data/user_de/0/com.android.cts.splitapp/files/ 目录中确认有相关文件创建。

AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.callen">
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name=".LockedBootReceiver" android:exported="true" android:directBootAware="true">
            <intent-filter>
                <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
        <receiver android:name=".BootReceiver" android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
    </application>
LockedBootReceiver.java:
package com.example.callen;

import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class LockedBootReceiver extends BaseBootReceiver {
    private static final String TAG = "LockedBootReceiver";
}
BaseBootReceiver.java:
package com.example.callen;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.provider.Settings;
import android.util.Log;

import java.io.File;

public class BaseBootReceiver extends BroadcastReceiver {
    private static final String TAG = "BaseBootReceiver";

    @Override
    public void onReceive(Context context, Intent intent) {
        try {
            context = context.createDeviceProtectedStorageContext();
            final File probe = new File(context.getFilesDir(),
                    getBootCount(context) + "." + intent.getAction());
            Log.d(TAG, "=================shine Touching probe " + probe);
            probe.createNewFile();
            exposeFile(probe);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static int getBootCount(Context context) throws Exception {
        return Settings.Global.getInt(context.getContentResolver(), Settings.Global.BOOT_COUNT);
    }

    private static File exposeFile(File file) throws Exception {
        Log.d(TAG, "exposeFile rockchip!");
        file.setReadable(true, false);
        file.setReadable(true, true);

        File dir = file.getParentFile();
        do {
            dir.setExecutable(true, false);
            dir.setExecutable(true, true);
            dir = dir.getParentFile();
        } while (dir != null);

        return file;
    }
}
BootReceiver.java 
package com.example.callen;

import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class BootReceiver extends BaseBootReceiver {
    private static final String TAG = "BootReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "=================================BootReceiver: " + intent.getAction());

    }
}
~ 

代码下载地址:

http://download.csdn.net/download/cigogo/10180511


  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Android启动文件加密的过程如下: 1. 在启动过程中,Android会准备文件加密的主密钥(FBE Master key)\[3\]。 2. Android会设置和校验各个加密存储位置的加密策略,即Encryption Policy\[3\]。 3. 真正的数据加密和解密是在进行文件I/O操作时发生的,而加密和解密所需的信息来自文件的Encryption Policy\[3\]。 4. Encryption Policy包括使用哪个主密钥进行加密文件数据的加密算法以及文件名的加密算法\[3\]。 5. 当设备启动后,如果文件加密功能处于启用状态,已加密的设备将直接进入锁定屏幕\[1\]。 6. 这样,用户可以快速使用重要的设备功能,如无障碍服务和闹钟,而无需在访问任何数据之前提供凭据\[2\]。 综上所述,Android启动文件加密的过程包括准备主密钥、设置加密策略以及在文件I/O操作时进行数据加密和解密。启用文件加密后,设备在启动后直接进入锁定屏幕,用户可以快速使用重要的设备功能。 #### 引用[.reference_title] - *1* [Android 加密文件加密(FBE)](https://blog.csdn.net/baidu_40808339/article/details/115225536)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Android加密文件加密](https://blog.csdn.net/long375577908/article/details/106589643)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [【数据安全】3. Android 文件加密(File-based Encryption)技术介绍](https://blog.csdn.net/cs_tech/article/details/127579028)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值