Android加密之檔案級加密
Android加密之檔案級加密
前置文章
前言
Android 的安全性問題一直備受關注,Google 在 Android 系統的安全方面也是一直沒有停止過更新,努力做到更加安全的手機移動作業系統。
在 Android 的安全性方面,有很多模組:
- 核心安全性
- 應用安全性
- 應用簽名
- 身份驗證
- Trusty TEE
- SELinux
- 加密
等等
其中,加密又分全盤加密(Android 4.4 引入,《Android加密之全盤加密》)和檔案級加密(Android 7.0 引入),本文將論述加密中的檔案級加密的基本知識。
什麼是檔案級加密
Android 7.0 及更高版本支援檔案級加密 (FBE)。採用檔案級加密時,可以使用不同的金鑰對不同的檔案進行加密,並且可以對這些檔案進行單獨解密。
全盤加密和檔案級加密的區別
藉助檔案級加密,Android 7.0 中引入了一項稱為直接啟動的新功能。該功能處於啟用狀態時,已加密裝置在啟動後將直接進入鎖定螢幕。之前,在使用全盤加密 (FDE) 的已加密裝置上,使用者在訪問任何資料之前都需要先提供憑據,從而導致手機無法執行除最基本操作之外的所有其他操作。例如,鬧鐘無法執行,無障礙服務不可用,手機無法接電話,而只能進行基本的緊急撥號操作。
檔案級加密概述
引入檔案級加密 (FBE) 和新 API 後,便可以將應用設為加密感知型應用,這樣一來,它們將能夠在受限環境中執行。這些應用將可以在使用者提供憑據之前執行,同時系統仍能保護私密使用者資訊。
在啟用了 FBE 的裝置上,每位使用者均有兩個可供應用使用的儲存位置:
- 憑據加密 (CE) 儲存空間:這是預設儲存位置,只有在使用者解鎖裝置後才可用。
- 裝置加密 (DE) 儲存空間:在直接啟動模式期間以及使用者解鎖裝置後均可用。
這種區分能夠使工作資料更加安全,因為這樣一來,加密不再只基於啟動時密碼,從而能夠同時保護多位使用者。
Direct Boot API 允許加密感知型應用訪問上述每個區域。應用生命週期會發生一些變化,以便在使用者的 CE 儲存空間因使用者在鎖定螢幕上首次輸入憑據而解鎖時,或者在工作資料提供工作挑戰時,通知應用。無論是否實現了 FBE,執行 Android 7.0 的裝置都必須要支援這些新的 API 和生命週期。不過,如果沒有 FBE,DE 和 CE 儲存空間將始終處於解鎖狀態。
啟用檔案級加密
通過將不帶引數的 fileencryption 標記新增到 userdata 分割槽最後一列的 fstab 行中,可以啟用 FBE。
直接啟動感知型應用
為了實現系統應用的快速遷移,新增了兩個可在應用級別設定的屬性。defaultToDeviceProtectedStorage 屬性僅適用於系統應用,directBootAware 屬性則適用於所有應用。
啟用檔案級加密的條件
- 對 EXT4 加密的核心支援(核心配置選項:EXT4_FS_ENCRYPTION)
- 基於 1.0 或 2.0 版 HAL 的 Keymaster 支援。不支援 Keymaster 0.3,因為它既不提供必要的功能,也不能保證為加密金鑰提供充分保護。
- 必須在可信執行環境 (TEE) 中實現 Keymaster/Keystore 和 Gatekeeper,以便為 DE 金鑰提供保護,從而使未經授權的作業系統(刷到裝置上的定製作業系統)無法直接請求 DE 金鑰。
- 核心加密效能必須要在使用 AES XTS 時至少達到 50MB/s,以確保良好的使用者體驗。
- 硬體信任根和驗證啟動需要繫結到 Keymaster 初始化程式,以確保未經授權的作業系統無法獲取裝置加密憑據。
加密過程
金鑰建立
首次建立裝置的 userdata 分割槽時,會由 init 指令碼應用基本結構和政策。這些指令碼將觸發建立首位使用者(使用者 0)的 CE 金鑰和 DE 金鑰,並定義要使用這些金鑰加密哪些目錄。建立其他使用者和資料時,會生成必要的其他金鑰並將其儲存在金鑰程式碼庫中;接下來會建立它們的憑據和裝置儲存位 置,並且加密政策會將這些金鑰關聯到相應目錄。
DE金鑰
觸發 late-init action
// 開機執行init.cpp,
int main(int argc, char** argv) {
......
// 解析 init.rc file
Parser& parser = Parser::GetInstance();
parser.ParseConfig("/init.rc");
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = property_get("ro.bootmode");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
// 觸發 late-init action
am.QueueEventTrigger("late-init");
}
......
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
這個方法定義在檔案 system/core/init/init.cpp 中。
觸發 post-fs-data
on late-init
.....
trigger post-fs
# Now we can mount /data. File encryption requires keymaster to decrypt
# /data, which in turn can only be loaded when system properties are present
trigger post-fs-data
.....
- 1
- 2
- 3
- 4
- 5
- 6
- 7
這個 action 定義在檔案 system/core/rootdir/init.rc 中。
執行 installkey 命令
on post-fs-data
chown system system /data
chmod 0771 /data
# Make sure we have the device encryption key.
start vold
#執行 installkey 命令
installkey /data
- 1
- 2
- 3
- 4
- 5
- 6
- 7
這個 action 定義在檔案 system/core/rootdir/init.rc 中。
命令 installkey 實質執行 do_installkey 函式
BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
static const Map builtin_functions = {
.....
{"installkey", {1, 1, do_installkey}},
{"load_persist_props", {0, 0, do_load_persist_props}},
.....
};
return builtin_functions;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
這個方法定義在檔案 system/core/init/builtins.cpp 中。
do_installkey() 函式定義如下
// 是否是 檔案級加密
static bool is_file_crypto() {
// 檔案級加密 ro.crypto.type 的值是 file, 全盤加密是 block
std::string value = property_get("ro.crypto.type");
return value == "file";
}
static int do_installkey(const std::vector<std::string>& args) {
// 檢查是否是檔案級加密
if (!is_file_crypto()) {
return 0;
}
// 建立金鑰
return e4crypt_create_device_key(args[1].c_str(),
do_installkeys_ensure_dir_exists);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
這個方法定義在檔案 system/core/init/builtins.cpp 中。
ro.crypto.type 在函式 do_mount_all() 中設定
static int do_mount_all(const std::vector<std::string>& args) {
} else if (ret == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
if (e4crypt_install_keyring()) {
return -1;
}
property_set("ro.crypto.state", "encrypted");
//檔案級加密
property_set("ro.crypto.type", "file");
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
這個方法定義在檔案 system/core/init/builtins.cpp 中。
回到 do_installkey() 函式,e4crypt_create_device_key() 定義如下
int e4crypt_create_device_key(const char* dir,
int ensure_dir_exists(const char*))
{
init_logging();
.....
// 執行 vdc, 傳入命令 enablefilecrypto, 同時需要注意引數 cryptfs
const char* argv[] = { "/system/bin/vdc", "--wait", "cryptfs", "enablefilecrypto" };
// 從 init, 到 vdc, 注意引數 argv[]
int rc = android_fork_execvp(4, (char**) argv, NULL, false, true);
LOG(INFO) << "enablefilecrypto result: " << rc;
return rc;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
這個方法定義在檔案 system/extras/ext4_utils/ext4_crypt_init_extensions.cpp 中。
android_fork_execvp() 實質是呼叫函式 android_fork_execvp_ext()
static inline int android_fork_execvp(int argc, char* argv[], int *status,
bool ignore_int_quit, bool logwrap)
{
// 實質是呼叫函式這個函式
return android_fork_execvp_ext(argc, argv, status, ignore_int_quit,
(logwrap ? LOG_ALOG : LOG_NONE), false, NULL,
NULL, 0);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
這個方法定義在檔案 system/core/logwrapper/include/logwrap/logwrap.h 中。
函式 android_fork_execvp_ext() 的實現如下
int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
int log_target, bool abbreviated, char *file_path,
const struct AndroidForkExecvpOption* opts, size_t opts_len) {
// fork 一個新的程式執行 vdc 程式
pid = fork();
if (pid < 0) {
.....
} else if (pid == 0) {
.....
// fork 程式成功, 執行函式 child()
child(argc, argv);
} else {
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
這個方法定義在檔案 system/core/logwrapper/logwrap.c 中。
static void child(int argc, char* argv[]) {
// create null terminated argv_child array
char* argv_child[argc + 1];
memcpy(argv_child, argv, argc * sizeof(char *));
argv_child[argc] = NULL;
// 開始執行 vdc 程式,引數 cryptfs, enablefilecrypto
// 從 init 程式,進入到 vdc 程式
if (execvp(argv_child[0], argv_child)) {
FATAL_CHILD("executing %s failed: %s\n", argv_child[0],
strerror(errno));
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
這個方法定義在檔案 system/core/logwrapper/logwrap.c 中。
int main(int argc, char **argv) {
// 定義待連線的 socket 標識
const char* sockname = "vold";
//在上面的引數中 argv[1] 等於 cryptfs, 所以 socket name 等於 cryptd
if (!strcmp(argv[1], "cryptfs")) {
sockname = "cryptd";
}
// 等待連線到 vold
while ((sock = socket_local_client(sockname,
ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM)) < 0) {
.....
}
if (!strcmp(argv[1], "monitor")) {
exit(do_monitor(sock, 0));
} else {
//argv[1] 等於 cryptfs, 執行函式 do_cmd()
exit(do_cmd(sock, argc, argv));
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
這個方法定義在檔案 system/vold/vdc.cpp 中。
static int do_cmd(int sock, int argc, char **argv) {
.....
// 寫入 socket,注意引數 cmd.c_str()
if ((write(sock, cmd.c_str(), cmd.length() + 1)) < 0) {
fprintf(stderr, "Failed to write command: %s\n", strerror(errno));
return errno;
}
return do_monitor(sock, seq);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
這個方法定義在檔案 system/vold/vdc.cpp 中。
socket 寫入資料到遠端後,執行到 vold 程式
int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
if (subcommand == "checkpw") {
.....
}
.....
//傳入的命令是 enablefilecrypto
} else if (subcommand == "enablefilecrypto") {
if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
dumpArgs(argc, argv, -1);
rc = cryptfs_enable_file();
}
.....
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
這個方法定義在檔案 system/vold/CryptCommandListener.cpp 中。
函式 cryptfs_enable_file() 定義如下
int cryptfs_enable_file()
{
return e4crypt_initialize_global_de();
}
- 1
- 2
- 3
- 4
這個函式定義在檔案 system/vold/cryptfs.c 中。
bool e4crypt_initialize_global_de() {
.....
// device_key_path = /data/unencrypted/key/
if (path_exists(device_key_path)) {
if (!android::vold::retrieveKey(device_key_path,
kEmptyAuthentication, &device_key)) return false;
} else {
LOG(INFO) << "Creating new key";
// 建立 金鑰
if (!random_key(&device_key)) return false;
// 儲存金鑰
if (!store_key(device_key_path, device_key_temp,
kEmptyAuthentication, device_key)) return false;
}
std::string device_key_ref;
//儲存在金鑰程式碼庫中
if (!install_key(device_key, &device_key_ref)) {
LOG(ERROR) << "Failed to install device key";
return false;
}
// 應用金鑰
std::string ref_filename = std::string("/data") + e4crypt_key_ref;
if (!android::base::WriteStringToFile(device_key_ref, ref_filename)) {
PLOG(ERROR) << "Cannot save key reference";
return false;
}
s_global_de_initialized = true;
return true;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
DE金鑰建立過程就分析到這裡。
CE金鑰
同樣在 init.rc 的 post-fs-data action 中
on post-fs-data
.....
installkey /data
.....
執行 init_user0 命令
init_user0
.....
- 1
- 2
- 3
- 4
- 5
- 6
- 7
這個 action 定義在檔案 system/core/rootdir/init.rc 中。
init_user0 實質是執行函式
BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
static const Map builtin_functions = {
.....
{"ifup", {1, 1, do_ifup}},
//執行 do_init_user0() 函式
{"init_user0", {0, 0, do_init_user0}},
.....
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
這個方法定義在檔案 system/core/init/builtins.cpp 中。
函式 do_init_user0() 定義如下
static int do_init_user0(const std::vector<std::string>& args) {
//直接呼叫了函式 e4crypt_do_init_user0()
return e4crypt_do_init_user0();
}
- 1
- 2
- 3
- 4
這個方法定義在檔案 system/core/init/builtins.cpp 中。
函式 e4crypt_do_init_user0() 定義如下
int e4crypt_do_init_user0()
{
init_logging();
//執行 vdc , 引數 cryptfs 和 init_user0, 和 DE 的建立過程類似
const char* argv[] = { "/system/bin/vdc", "--wait", "cryptfs", "init_user0" };
// fork vdc 程式,並執行 vdc 程式
int rc = android_fork_execvp(4, (char**) argv, NULL, false, true);
LOG(INFO) << "init_user0 result: " << rc;
return rc;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
這個方法定義在檔案 system/extras/ext4_utils/ext4_crypt_init_extensions.cpp 中。
函式 android_fork_execvp() 執行 vdc 後,vdc 並沒有做什麼具體的操作,只是把相應的引數繼續傳遞給 vold,和 DE 的金鑰建立過程一樣,引數 “cryptfs” 和 引數 “init_user0” 決定會執行到 vold 的如下程式碼
int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
.....
} else if (subcommand == "init_user0") {
if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
//執行函式 e4crypt_init_user0()
return sendGenericOkFailOnBool(cli, e4crypt_init_user0());
.....
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
這個方法定義在檔案 system/vold/CryptCommandListener.cpp 中。
函式 e4crypt_init_user0() 定義如下
bool e4crypt_init_user0() {
LOG(DEBUG) << "e4crypt_init_user0";
if (e4crypt_is_native()) {
// user_key_dir 等於 data/misc/vold/user_keys
if (!prepare_dir(user_key_dir, 0700, AID_ROOT, AID_ROOT)) return false;
if (!prepare_dir(user_key_dir + "/ce", 0700, AID_ROOT, AID_ROOT)) return false;
if (!prepare_dir(user_key_dir + "/de", 0700, AID_ROOT, AID_ROOT)) return false;
if (!path_exists(get_de_key_path(0))) {
//建立和安裝 CD keys, user 為 0, 即開機預設的 user
if (!create_and_install_user_keys(0, false)) return false;
}
// TODO: switch to loading only DE_0 here once framework makes
// explicit calls to install DE keys for secondary users
if (!load_all_de_keys()) return false;
}
// We can only safely prepare DE storage here, since CE keys are probably
// entangled with user credentials. The framework will always prepare CE
// storage once CE keys are installed.
if (!e4crypt_prepare_user_storage(nullptr, 0, 0, FLAG_STORAGE_DE)) {
LOG(ERROR) << "Failed to prepare user 0 storage";
return false;
}
// If this is a non-FBE device that recently left an emulated mode,
// restore user data directories to known-good state.
if (!e4crypt_is_native() && !e4crypt_is_emulated()) {
e4crypt_unlock_user_key(0, 0, "!", "!");
}
return true;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
這個方法定義在檔案 system/vold/Ext4Crypt.cpp 中。
函式 create_and_install_user_keys() 定義如下
static bool create_and_install_user_keys(userid_t user_id, bool create_ephemeral) {
std::string de_key, ce_key;
//建立 DE 金鑰
if (!random_key(&de_key)) return false;
//建立 CE 金鑰
if (!random_key(&ce_key)) return false;
.....
std::string de_raw_ref;
// 儲存 DE 金鑰到金鑰程式碼庫
if (!install_key(de_key, &de_raw_ref)) return false;
s_de_key_raw_refs[user_id] = de_raw_ref;
std::string ce_raw_ref;
// 儲存 CE 金鑰到金鑰程式碼庫
if (!install_key(ce_key, &ce_raw_ref)) return false;
s_ce_keys[user_id] = ce_key;
s_ce_key_raw_refs[user_id] = ce_raw_ref;
LOG(DEBUG) << "Created keys for user " << user_id;
return true;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
這個方法定義在檔案 system/vold/Ext4Crypt.cpp 中。
再看看金鑰的真正生成過程 random_key()
static bool random_key(std::string* key) {
// 讀取隨機金鑰
if (android::vold::ReadRandomBytes(EXT4_AES_256_XTS_KEY_SIZE, *key) != 0) {
// TODO status_t plays badly with PLOG, fix it.
LOG(ERROR) << "Random read failed";
return false;
}
return true;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
這個方法定義在檔案 system/vold/Ext4Crypt.cpp 中。
ReadRandomBytes() 定義如下
status_t ReadRandomBytes(size_t bytes, std::string& out) {
out.clear();
//開啟 linux 的隨機數檔案
int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
if (fd == -1) {
return -errno;
}
char buf[BUFSIZ];
size_t n;
//讀取一個隨機數,作為金鑰
while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], std::min(sizeof(buf), bytes)))) > 0) {
out.append(buf, n);
bytes -= n;
}
close(fd);
if (bytes == 0) {
return OK;
} else {
return -EIO;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
這個方法定義在檔案 system/vold/Utils.cpp 中。
使用建立的金鑰加密
在解析 init.rc 檔案時,會執行命令 mkdir, 如
mkdir /data/system_de 0770 system system
on post-fs-data
mkdir /data/system_ce 0770 system system
mkdir /data/misc_de 01771 system misc
mkdir /data/misc_ce 01771 system misc
//使用者資料路徑
mkdir /data/user 0711 system system
// 使用者 DE 空間
mkdir /data/user_de 0711 system system
// /data/data 連線到目錄 /data/user/0
// /data/user 和 /data/data 都是 CE 空間
symlink /data/data /data/user/0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
這個 action 定義在檔案 system/core/rootdir/init.rc 中。
命令 mkdir 實質執行的的是函式 do_mkdir()
BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
static const Map builtin_functions = {
.....
{"mkdir", {1, 4, do_mkdir}},
.....
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
這個方法定義在檔案 system/core/init/builtins.cpp 中。
函式 do_mkdir() 的實現如下
static int do_mkdir(const std::vector<std::string>& args) {
.....
// 建立目錄
ret = make_dir(args[1].c_str(), mode);
.....
if (e4crypt_is_native()) {
// 加密目錄
if (e4crypt_set_directory_policy(args[1].c_str())) {
wipe_data_via_recovery(std::string() + "set_policy_failed:" + args[1]);
return -1;
}
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
這個方法定義在檔案 system/core/init/builtins.cpp 中。
函式 e4crypt_set_directory_policy() 的實現如下
int e4crypt_set_directory_policy(const char* dir)
{
// 只加密 /data 目錄以及子目錄
if (!dir || strncmp(dir, "/data/", 6) || strchr(dir + 6, '/')) {
return 0;
}
// 不需要加密的目錄在這裡設定,但是,它們的子目錄是會被加密的
std::vector<std::string> directories_to_exclude = {
"lost+found",
"system_ce", "system_de",
"misc_ce", "misc_de",
"media",
"data", "user", "user_de",
};
std::string prefix = "/data/";
for (auto d: directories_to_exclude) {
if ((prefix + d) == dir) {
KLOG_INFO(TAG, "Not setting policy on %s\n", dir);
return 0;
}
}
// 金鑰引用
std::string ref_filename = std::string("/data") + e4crypt_key_ref;
std::string policy;
if (!android::base::ReadFileToString(ref_filename, &policy)) {
KLOG_ERROR(TAG, "Unable to read system policy to set on %s\n", dir);
return -1;
}
KLOG_INFO(TAG, "Setting policy on %s\n", dir);
// 加密目錄
int result = e4crypt_policy_ensure(dir, policy.c_str(), policy.size());
if (result) {
KLOG_ERROR(TAG, "Setting %02x%02x%02x%02x policy on %s failed!\n",
policy[0], policy[1], policy[2], policy[3], dir);
return -1;
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
這個方法定義在檔案 system/extras/ext4_utils/ext4_crypt_init_extensions.cpp 中。
函式 e4crypt_policy_ensure() 定義如下
int e4crypt_policy_ensure(const char *directory, const char *policy, size_t policy_length) {
bool is_empty;
if (!is_dir_empty(directory, &is_empty)) return -1;
if (is_empty) {
// 應用加密政策
if (!e4crypt_policy_set(directory, policy, policy_length)) return -1;
} else {
if (!e4crypt_policy_check(directory, policy, policy_length)) return -1;
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
這個方法定義在檔案 system/extras/ext4_utils/ext4_crypt.cpp 中。
函式 e4crypt_policy_set() 定義如下
static bool e4crypt_policy_set(const char *directory, const char *policy, size_t policy_length) {
int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
......
ext4_encryption_policy eep;
eep.version = 0;
// 設定加密型別 AES 256
eep.contents_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
eep.filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
eep.flags = 0;
memcpy(eep.master_key_descriptor, policy, EXT4_KEY_DESCRIPTOR_SIZE);
// 用命令 EXT4_IOC_SET_ENCRYPTION_POLICY 控制 IO
if (ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &eep)) {
PLOG(ERROR) << "Failed to set encryption policy for " << directory;
close(fd);
return false;
}
close(fd);
char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
policy_to_hex(policy, policy_hex);
LOG(INFO) << "Policy for " << directory << " set to " << policy_hex;
return true;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
這個方法定義在檔案 system/extras/ext4_utils/ext4_crypt.cpp 中。
加密過程就分析到這裡。
直接啟動
應用了檔案級加密的裝置,可以以直接啟動的方式啟動。此時,裝置可以載入並使用沒有通過檔案級加密的目錄,如 /data/user_de/0/。那麼,直接啟動的 APP 的資料儲存在這個目錄下。
在上文中,我們知道需要在直接啟動就可以立馬使用的的 APP,需要在應用的 manifest 的 application 標籤宣告 android:directBootAware=”true” 屬性。對於系統的應用,宣告 android:defaultToDeviceProtectedStorage=”true” 可以把應用的預設儲存空間設定為 /data/user_de/。
因此,在使用者沒有輸入憑據解密 CE 空間之前,系統只是載入 DE 下的應用。
在 AMS ready 時,如下(讀者不瞭解這個過程的以看考文章《 Android系統之System Server大綱》)
public void systemReady(final Runnable goingCallback) {
.....
synchronized (this) {
// Only start up encryption-aware persistent apps; once user is
// unlocked we'll come back around and start unaware apps
//啟動 persistent app,注意引數 PackageManager.MATCH_DIRECT_BOOT_AWARE
startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_AWARE);
}
.....
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
這個方法定義在檔案 frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java 中。
方法 startPersistentApps() 的實現如下
private void startPersistentApps(int matchFlags) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) return;
synchronized (this) {
try {
//獲取所有 direct boot 的 app
final List<ApplicationInfo> apps = AppGlobals.getPackageManager()
.getPersistentApplications(STOCK_PM_FLAGS | matchFlags).getList();
for (ApplicationInfo app : apps) {
if (!"android".equals(app.packageName) && validNewProc(app.packageName, UserHandle.getUserId(app.uid))) {//modified by yongfeng.zhang for task 3682193 on 2016-12-28
// 加入啟動佇列
addAppLocked(app, false, null /* ABI override */);
}
}
} catch (RemoteException ex) {
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
這個方法定義在檔案 frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java 中。
方法 addAppLocked() 定義如下
final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,
String abiOverride) {
.....
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
// 啟動 APP
startProcessLocked(app, "added application", app.processName, abiOverride,
null /* entryPoint */, null /* entryPointArgs */);
}
return app;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
這個方法定義在檔案 frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java 中。
在 PMS 啟動時,掃描安裝 APP 是,會過濾不是直接啟動的 APP
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
final int policyFlags, final int scanFlags, long currentTime, UserHandle user)
throws PackageManagerException {
// Apply policy
if ((policyFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
//直接啟動的 APP
if (pkg.applicationInfo.isDirectBootAware()) {
// we're direct boot aware; set for all components
for (PackageParser.Service s : pkg.services) {
s.info.encryptionAware = s.info.directBootAware = true;
}
for (PackageParser.Provider p : pkg.providers) {
p.info.encryptionAware = p.info.directBootAware = true;
}
for (PackageParser.Activity a : pkg.activities) {
a.info.encryptionAware = a.info.directBootAware = true;
}
for (PackageParser.Activity r : pkg.receivers) {
r.info.encryptionAware = r.info.directBootAware = true;
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
這個方法定義在檔案 frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java 中。
總結
檔案級加密,比較全盤加密具有一些優點,可以讓沒有輸入憑證的裝置可以使用更多的功能。檔案級加密分 CE 空間和 DE 空間,CE 空間需要憑證加密方可使用,DE 空間則是裝置啟動後即可使用。應用如果需要區分 CE 和 DE 空間,需要建立不同的上下文環境 Context。
相關文章
- openssl加密檔案加密
- Mac壓縮檔案怎麼加密?BetterZip加密Word壓縮檔案教程Mac加密
- SpringBoot專案配置檔案加密Spring Boot加密
- 資料加密 第六篇:透明檔案加密加密
- 室友的Zip加密檔案探祕,Python解決Zip加密檔案探索祕密!加密Python
- node ~ zip壓縮 && 檔案加密加密
- .net加密web.config檔案加密Web
- 暴力破解zip加密檔案加密
- win10 系統excel檔案如何加密 win10excel表格檔案加密方法Win10Excel加密
- C語言實現檔案加密C語言加密
- java中檔案如何加密壓縮?Java加密
- Concealer for Mac(檔案資訊加密工具)Mac加密
- word文件怎麼加密檔案?Word利用宏一鍵給高機密檔案加密的方法加密
- word文件怎麼加密檔案?Word利用巨集一鍵給高機密檔案加密的方法加密
- 如何對U盤檔案資料加密,U盤加密方法教程分享加密
- Android OTA升級(一)之Makefile檔案分析Android
- Linux加密壓縮檔案/資料夾Linux加密
- SpringBoot配置檔案敏感資訊加密方案Spring Boot加密
- SpringBoot專案配置檔案中密碼的加密Spring Boot密碼加密
- android加密解密完美教程Android加密解密
- win10目錄加密怎麼設定_win10如何加密檔案Win10加密
- 【實戰】檔案加密器進行逆向加密
- SpringBoot自定義classloader加密保護class檔案Spring Boot加密
- 分享一個本人的私密檔案加密方法:用php 對windows 上的自己的本地私密檔案進行加密。加密PHPWindows
- 支援對批次檔案加密的加密軟體方案?根源杜絕企業重要檔案洩密,福建風奧科技加密
- 電腦桌面檔案加密-迅軟資料加密軟體,穩定安全易操作加密
- HTTPS之加密方式HTTP加密
- Java加密之IVJava加密
- Android P 加密程式變更Android加密
- Android中常用的加密方式Android加密
- Android 加密知識詳解Android加密
- 辦公系統生成檔案自動加密方案如何部署?Windows檔案伺服器透明加密方案浙江風奧科技加密Windows伺服器
- 如何正確評估企業級加密系統?上海辦公的資料檔案加密哪個更有效?風奧科技加密
- SpringBoot整合Jasypt安全框架,配置檔案內容加密Spring Boot框架加密
- pdf檔案編輯時提示加密怎麼解除?加密
- 文件透明加密軟體哪家好?window透明加密軟體產品如何對電子檔案加密?陝西風奧科技加密
- Windows電腦桌面檔案加密軟體怎麼選?企業電腦檔案生成自動加密方案,福建風奧科技Windows加密
- 企業檔案加密防複製方法有哪些?廣東國密級專注做加密軟體的企業,風奧科技加密