精华内容
下载资源
问答
  • Android 加密

    2014-09-11 10:00:27
    Android 加密解密 http://www.eoeandroid.com/thread-66236-1-1.html Android 加密解密字符串 http://www.eoeandroid.com/thread-81425-1-1.html Android 用MD5加密算法加密密码(一) ...
    一、实例教程 

    Android 加密解密
    http://www.eoeandroid.com/thread-66236-1-1.html

    Android 加密解密字符串
    http://www.eoeandroid.com/thread-81425-1-1.html

    Android 用MD5加密算法加密密码(一)
    http://www.eoeandroid.com/thread-97843-1-1.html

    Android 用MD6加密算法加密密码(二)
    http://www.eoeandroid.com/thread-97846-1-1.html

    鉴客 Android客户端与服务器端通过DES加密认证
    http://www.eoeandroid.com/thread-159272-1-1.html

    android下md5加密,大家收藏下吧 ,用到的时候可以直接拿来用
    http://www.eoeandroid.com/thread-51590-1-1.html

    Android 获取自己程序APK包的MD5指纹
    http://www.eoeandroid.com/thread-92389-1-1.html

    给自己的网络数据加密
    http://www.eoeandroid.com/thread-97336-1-1.html

    JAVA加密解密
    http://www.eoeandroid.com/thread-69738-1-1.html


    二、资料下载

    加密记事本,陈年作品(源码放出)
    http://www.eoeandroid.com/thread-99203-1-1.html

    通用DES加密解密方法
    http://www.eoeandroid.com/thread-56774-1-1.html

    一个加密的记事本
    http://www.eoeandroid.com/thread-122363-1-1.html


    三、EOE社区实战问题解答

    Android ZIP 加密解密
    http://www.eoeandroid.com/thread-116230-1-1.html

    android AES加密和解密
    http://www.eoeandroid.com/thread-162137-1-1.html

    关于android加密
    http://www.eoeandroid.com/thread-73308-1-1.html

    android 与 java 的RSA加解密
    http://www.eoeandroid.com/thread-72268-1-1.html

    求高手相助,关于Android加密的问题
    http://www.eoeandroid.com/thread-63822-1-1.html

    Android下DES加密和J2SE平台下加密结果不一样
    http://www.eoeandroid.com/thread-83595-1-1.html

    Android中自带的RSA加密算法和JAVA中的不是一个标准的吗?
    http://www.eoeandroid.com/thread-102614-1-1.html

    在DES解密时候出现pad block corrupted错误
    http://www.eoeandroid.com/thread-63284-1-1.html

    请问一下,android与PHP连接安全问题
    http://www.eoeandroid.com/thread-73501-1-1.html

    短信加密问题
    http://www.eoeandroid.com/thread-75590-1-1.html

    我java des加密后的密文和c加密出来的最后一段密文不一样!
    http://www.eoeandroid.com/thread-57876-1-1.html

    MD5 加密的方法不能理解
    http://www.eoeandroid.com/thread-69619-1-1.html

    全新的数据加密技术
    http://www.eoeandroid.com/thread-69548-1-1.html

    求:文件/文件夹加密 解决方案 或指点
    http://www.eoeandroid.com/thread-114552-1-1.html

    CRC32算法如何加密字符串?
    http://www.eoeandroid.com/thread-102782-1-1.html

    讨论一下 :文件/文件夹 的加密和解密 如何实现
    http://www.eoeandroid.com/thread-114550-1-1.html

    关于程序加密的问题
    http://www.eoeandroid.com/thread-91900-1-1.html

    请教Android上的DSA加密方法
    http://www.eoeandroid.com/thread-63756-1-1.html

    Json在android中的加密传输
    http://www.eoeandroid.com/thread-82508-1-1.html

    怎么给视频加密?
    http://www.eoeandroid.com/thread-101882-1-1.html

    资源图片压缩或加密求助
    http://www.eoeandroid.com/thread-71619-1-1.html

    android客户端 访问服务器的url加密 求加密方式
    http://www.eoeandroid.com/thread-147709-1-1.html

    怎么实现对android的文件夹加密或者锁定啊?
    http://www.eoeandroid.com/thread-24955-1-1.html

    如何给文件加密?
    http://www.eoeandroid.com/thread-68058-1-1.html


    四、社区求助未解答帖

    android 如何对视频加密和解密
    http://www.eoeandroid.com/thread-103350-1-1.html

    android base64加密 解码 apk信息读取问题请教
    http://www.eoeandroid.com/thread-163307-1-1.html

    android与PC socket传输加密密文出错
    http://www.eoeandroid.com/thread-156109-1-1.html

    android下是否能用classloader对class进行加解密
    http://www.eoeandroid.com/thread-72582-1-1.html

    新手请教各位大虾: 一个报错,详细请看里面
    http://www.eoeandroid.com/thread-157399-1-1.html

    开发/调试MD5指纹和生产/发布MD5指纹
    http://www.eoeandroid.com/thread-65795-1-1.html

    加密数据库查询的问题
    http://www.eoeandroid.com/thread-166386-1-1.html

    二维码加密了
    http://www.eoeandroid.com/thread-172262-1-1.html

    求一个能实现短信加密解密功能的源码!
    http://www.eoeandroid.com/thread-161206-1-1.html

    急求android短信加密图片加密的源码
    http://www.eoeandroid.com/thread-166981-1-1.html

    加密相片
    http://www.eoeandroid.com/thread-66466-1-1.html

    明文加密码 加密后密文长度不变 要怎么实现?
    http://www.eoeandroid.com/thread-111009-1-1.html

    加密记事本都用到了android哪些知识 希望能得到详细讲解
    http://www.eoeandroid.com/thread-172381-1-1.html
    展开全文
  • Android加密之全盘加密

    万次阅读 2017-08-03 11:19:00
    Android加密之全盘加密前言Android 的安全性问题一直备受关注,Google 在 Android 系统的安全方面也是一直没有停止过更新,努力做到更加安全的手机移动操作系统。在 Android 的安全性方面,有很多模块: 内核安全性 ...

    Android加密之全盘加密

    前言

    Android 的安全性问题一直备受关注,Google 在 Android 系统的安全方面也是一直没有停止过更新,努力做到更加安全的手机移动操作系统。

    在 Android 的安全性方面,有很多模块:

    1. 内核安全性
    2. 应用安全性
    3. 应用签名
    4. 身份验证
    5. Trusty TEE
    6. SELinux
    7. 加密
      等等

    其中,加密又分全盘加密(Android 4.4 引入)和文件级加密(Android 7.0 引入),本文将论述加密中的全盘加密的基本知识。全盘加密在 Android 4.4 中引入,在 Android 5.0 中做了比较大的更新。

    这里写图片描述

    本文部分片段摘自 Android 官网,融合笔者的个人理解和知识。

    什么是全盘加密

    全盘加密是使用已加密的密钥对 Android 设备上的所有用户数据进行编码的过程。设备经过加密后,所有由用户创建的数据在写入磁盘之前都会自动加密,并且所有读取操作都会在将数据返回给调用进程之前自动解密数据。

    Android 5.0 中又引入了以下新功能:

    • 创建了快速加密方式,这种加密方式只会对数据分区中已使用的分块进行加密,以免首次启动用时过长。目前只有 EXT4 和 F2FS 文件系统支持快速加密。
    • 添加了 forceencrypt fstab 标记,以便在首次启动时进行加密。
    • 添加了对解锁图案和无密码加密的支持。
    • 添加了由硬件支持的加密密钥存储空间,该空间使用可信执行环境(TEE,例如 TrustZone)的签名功能。

    全盘加密运作方式

    Android 全盘加密基于在块设备层运行的内核功能 dm-crypt。因此,这种加密方式适用于以块设备的形式呈现给内核的嵌入式多媒体卡 (eMMC) 和类似闪存设备。YAFFS 会直接与原始 NAND 闪存芯片交互,无法进行全盘加密。

    全盘加密采用的是 128 位高级加密标准 (AES) 算法(搭配密码块链接 (CBC) 和 ESSIV:SHA256)。对主密钥进行加密时使用的是 128 位 AES 算法,并会调用 OpenSSL 库。对于该密钥,您必须使用 128 位或更多位(可以选择 256 位)。

    Android 5.0 版中有以下 4 种加密状态:

    • 默认
    • PIN 码
    • 密码
    • 解锁图案

    这里写图片描述

    首次启动时,设备会创建一个随机生成的 128 位主密钥,然后会使用默认密码和存储的盐对其进行哈希处理。默认密码是“default_password”。不过,设备还会通过 TEE(例如 TrustZone)为生成的哈希签名。TEE 会使用相应签名的哈希来加密主密钥。

    您可以在 Android 开放源代码项目 cryptfs.c 文件中找到定义的默认密码。

    当用户在设备上设置 PIN 码/通行码或密码时,只有 128 位的密钥会被重新加密并存储起来(也就是说,更改用户 PIN 码/通行码/解锁图案不会导致重新加密用户数据)。请注意,受管理的设备可能受 PIN 码、解锁图案或密码限制。

    加密操作由 init 和 vold 管理。 init 负责调用 vold,然后 vold 会设置相关属性以触发 init 中的事件。系统的其他部分也会查看这些属性以执行各项任务,例如报告状态、提示输入密码,或有严重错误发生时提示恢复出厂设置。为了调用 vold 中的加密功能,系统会使用命令行工具 vdc 的 cryptfs 命令:checkpw、restart、enablecrypto、changepw、cryptocomplete、verifypw、setfield、getfield、mountdefaultencrypted、getpwtype、getpw 以及 clearpw。

    要加密、解密或清空 /data,/data 不得处于装载状态。但要显示任何界面,框架都必须启动,而框架需要 /data 才能运行。为了解决这一冲突,/data 上会装载一个临时文件系统。通过该文件系统,Android 可以提示输入密码、显示进度或根据需要建议清除数据。不过,该文件系统会带来以下限制:要从临时文件系统切换到实际的 /data 文件系统,系统必须停止临时文件系统中打开了文件的所有进程,并在实际的 /data 文件系统中重启这些进程。为此,所有服务都必须位于以下其中一个组内:core、main 和 late_start。

    • core:启动后一直不会关闭。
    • main:关闭,然后在用户输入磁盘密码后会重启。
    • late_start:在 /data 未解密并装载之前,一直不会启动。

    为了触发这些操作,vold.decrypt 属性会被设为多种字符串。要结束和重启服务,请使用以下 init 命令:

    • class_reset:停止相应服务,但允许通过 class_start 重启该服务。
    • class_start:重启相应服务。
    • class_stop:停止相应服务并添加 SVC_DISABLED 标记。被停止的服务不会对。
    • class_start 做出响应。

    加密流程和启动流程

    使用 forceencrypt 加密新设备

    这是 Android 5.0 设备首次启动时的常规流程。

    1. 检测带有 forceencrypt 标记的未加密文件系统

      /data 未加密,但需要加密,因为 forceencrypt 强制要求进行此项加密。卸载 /data。

    2. 开始加密 /data

      vold.decrypt = “trigger_encryption” 会触发 init.rc,从而使 vold 对 /data 进行无密码加密。(因为这应该是新设备,还没有设置密码。)

    3. 装载 tmpfs

      vold 会装载一个 tmpfs /data(使用 ro.crypto.tmpfs_options 中的 tmpfs 选项),并会将 vold.encrypt_progress 属性设为 0。 vold 会准备 tmpfs /data 以便启动已加密的系统,并会将 vold.decrypt 属性设为 trigger_restart_min_framework

    4. 启动框架以显示进度

      由于设备上几乎没有要加密的数据,加密过程很快就会完成,因此实际上通常并不会显示进度条。如需关于进度界面的更多详细信息,请参阅加密现有设备。

    5. /data 加密后,关闭框架

      vold 会将 vold.decrypt 设为 trigger_default_encryption,这会启动 defaultcrypto 服务。(这会启动以下流程来装载默认的已加密用户数据。)trigger_default_encryption 会检查加密类型,以了解 /data 加密是否使用了密码。由于 Android 5.0 设备是在首次启动时加密,应该没有设置任何密码,因此我们要解密并装载 /data。

    6. 装载 /data

      接下来,init 会使用从 ro.crypto.tmpfs_options(在 init.rc 中设置)中选取的参数在 tmpfs RAMDisk 中装载 /data。

    7. 启动框架

      将 vold 设为 trigger_restart_framework,这会继续常规启动过程。

    启动未进行默认加密的已加密设备

    当您启动设有密码的已加密设备时,则会发生该流程。设备的密码可以是 PIN 码、解锁图案或密码。

    1. 检测设有密码的已加密设备

      会发现 Android 设备已加密,因为设置了 ro.crypto.state = “encrypted” 标记

      由于 /data 是使用密码加密的,因此 vold 会将 vold.decrypt 设为 trigger_restart_min_framework。

    2. 装载 tmpfs

      init 会设置 5 个属性,以保存为 /data(包含从 init.rc 传入的参数)提供的初始装载选项。 vold 会使用这些属性来设置加密映射:
      ro.crypto.fs_type
      ro.crypto.fs_real_blkdev
      ro.crypto.fs_mnt_point
      ro.crypto.fs_options
      ro.crypto.fs_flags (ASCII 码 8 位十六进制数字,以 0x 开头)

    3. 启动框架以提示输入密码

      框架会启动并看到 vold.decrypt 已设为 trigger_restart_min_framework。这让框架知道自己是在 tmpfs /data 磁盘中启动的,并且需要获取用户密码。

      不过,它首先需要确认磁盘是否已经过适当加密。它会向 vold 发送 cryptfs cryptocomplete 命令。 如果加密已成功完成,vold 会返回 0;如果发生内部错误,则会返回 -1;如果加密未成功完成,则会返回 -2。vold 通过查看 CRYPTO_ENCRYPTION_IN_PROGRESS 标记的加密元数据来确定应返回的值。如果设置了此标记,则表示加密过程中断了,并且设备上没有可用的数据。如果 vold 返回错误,界面中应显示一条消息,提示用户重新启动设备并将其恢复出厂设置,并且界面中应为用户提供一个用于执行该操作的按钮。

    4. 通过密码解密数据

      cryptfs cryptocomplete 成功后,框架会显示一个界面,提示用户输入磁盘密码。界面会向 vold 发送 cryptfs checkpw 命令来检查用户输入的密码。如果密码正确(通过以下方式判定:在临时位置成功装载已解密的 /data,然后将其卸载),vold 会将已解密块设备的名称保存在 ro.crypto.fs_crypto_blkdev 属性中,并向界面返回状态 0。如果密码不正确,则向界面返回 -1。

    5. 停止框架

      界面会显示加密启动图形,然后使用 cryptfs restart 命令调用 vold。vold 会将 vold.decrypt 属性设为 trigger_reset_main,这会使 init.rc 执行 class_reset main 命令。此命令会停止 main 类中的所有服务,以便卸载 tmpfs /data。

    6. 装载 /data

      然后,vold 会装载已解密的实际 /data 分区,并准备新的分区(如果加密时采用了首次发布不支持的数据清除选项,则可能永远无法准备就绪)。它会将 vold.post_fs_data_done 属性设为 0,接着将 vold.decrypt 设为 trigger_post_fs_data。这会使 init.rc 运行其 post-fs-data 命令。这些命令会创建所有必要的目录或链接,然后将 vold.post_fs_data_done 设为 1。当 vold 看到该属性中的 1 时,会将 vold.decrypt 属性设为 trigger_restart_framework。这会使 init.rc 再次启动 main 类中的服务,并启动 late_start 类中的服务(这是设备启动后首次启动这些服务)。

    7. 启动整个框架

      现在,框架会使用已解密的 /data 文件系统启动其所有服务,接下来系统就可以使用了。

    代码解读

    结合上章节的流程,下面用代码来解析启动未进行默认加密的已加密设备这个流程。

    # Android fstab file.
    #<src>                                         <mnt_point>  <type> 
    ...... 
    /dev/block/platform/soc.0/f9824900.sdhci/by-name/userdata     /data           ext4    noatime,nosuid,nodev,barrier=1,data=ordered,nomblk_io_submit,noauto_da_alloc,errors=panic wait,check,fileencryption  
    ......
    defaults
    

    这个配置定义在 device/lge/bullhead/fstab_fbe.bullhead 文件中。

    如上面的代码,在 /data 的末尾加上 fileencryption,便会进行全盘加密。

    步骤1:检测设有密码的已加密设备

    //设置ro.crypto.state标记,手机已被用户加密
    static int do_mount_all(const std::vector<std::string>& args) {
        ......
        if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
            ActionManager::GetInstance().QueueEventTrigger("encrypt");
        } else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
            // 全盘加密,ro.crypto.state = encrypted, ro.crypto.type = block
            property_set("ro.crypto.state", "encrypted");
            property_set("ro.crypto.type", "block");
            //发送vdc 命令 defaultcrypto
            ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
        ......
        } 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");
            ......
    
        return ret;
    }

    这个方法定义在文件 system/core/init/builtins.cpp 中。

    # One shot invocation to deal with encrypted volume.
    # do_mount_all 中写入命令 defaultcrypto,执行 vdc 发送命令 mountdefaultencrypted
    on defaultcrypto
        exec - root -- /system/bin/vdc --wait cryptfs mountdefaultencrypted
        # vold will set vold.decrypt to trigger_restart_framework (default
        # encryption) or trigger_restart_min_framework (other encryption)
    
    # One shot invocation to encrypt unencrypted volumes
    on encrypt
        start surfaceflinger
        exec - root -- /system/bin/vdc --wait cryptfs enablecrypto inplace default noui
        # vold will set vold.decrypt to trigger_restart_framework (default
        # encryption)

    这个服务定义在文件 system/vold/vdc.rc 中。

    int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
                                                     int argc, char **argv) {
        ......
        } else if (subcommand == "mountdefaultencrypted") {
            ......
            //执行cryptfs_mount_default_encrypted
            std::thread(&cryptfs_mount_default_encrypted).detach();
        }
        ......
    }

    这个方法定义在文件 vold/CryptCommandListener.cpp 中。

    int cryptfs_mount_default_encrypted(void)
    {
        int crypt_type = cryptfs_get_password_type();
        ......
        } else if (crypt_type != CRYPT_TYPE_DEFAULT) {
            SLOGD("Password is not default - "
                  "starting min framework to prompt");
            //不是默认加密, 设置 vold.decrypt = trigger_restart_min_framework
            property_set("vold.decrypt", "trigger_restart_min_framework");
            return 0;
        } else if (cryptfs_check_passwd(DEFAULT_PASSWORD) == 0) {
        ......

    这个方法定义在文件 system/vold/cryptfs.c 中。

    #属性vold.decrypt==trigger_restart_min_framework 时执行
    on property:vold.decrypt=trigger_restart_min_framework
        # A/B update verifier that marks a successful boot.
        exec - root -- /system/bin/update_verifier trigger_restart_min_framework
        class_start main

    这个服务定义在服务 system/core/rootdir/init.rc 中。

    class_start main 可知重启 main 类别的服务。main 类别的服务包括:

    这里写图片描述

    会重启 zygote。

    步骤2:装载 tmpfs

    Zygote 启动后,会 fork() system_process 进程,就是运行 SystemServer 代码了。但是 system_process 的运行需要正常的用户空间(/data),所以,需要临时挂载 tmpfs 分区,这个分区是在内存里分配的临时空间。

    int CommandListener::StorageCmd::runCommand(SocketClient *cli,
                                                          int argc, char **argv) {
        ......
        if (!strcmp(argv[1], "mountall")) {
            if (argc != 2) {
                cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: mountall", false);
                return 0;
            }
            // 挂载所有设备
            fs_mgr_mount_all(fstab);
            cli->sendMsg(ResponseCode::CommandOkay, "Mountall ran successfully", false);
            return 0;
        }

    这个方法定义在文件 system/vold/CommandListener.cpp 中。

    int fs_mgr_mount_all(struct fstab *fstab)
    {
        ......
        /* mount(2) returned an error, handle the encryptable/formattable case */
        bool wiped = partition_wiped(fstab->recs[top_idx].blk_device);
        //挂载 tmpfs 临时分区
        if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) {
           ++error_count;
           continue;
        }
        //全盘加密
        encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
        ......
    }
    
    

    这个方法定义在文件 system/core/fs_mgr/fs_mgr.c 中。

    步骤3:启动框架以提示输入密码

    private void startBootstrapServices() {
        // Only run "core" apps if we're encrypting the device.
        //启动min-framework 显示密码输入界面,仅启动 coreApp, 在AndroidManifest.xml中声明。
        //此时启动的 APP 在 tmpfs 临时分区,所以,所有app都是原始安装状态,不包含任何用户使用产生的数据。
        String cryptState = SystemProperties.get("vold.decrypt");
        if (ENCRYPTING_STATE.equals(cryptState)) {
            Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
            mOnlyCore = true;
        } else if (ENCRYPTED_STATE.equals(cryptState)) {
            Slog.w(TAG, "Device encrypted - only parsing core apps");
            mOnlyCore = true;
        }
        ......
        mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
        mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
        ......
    
    }

    这个方法定义在文件frameworks/base/services/java/com/android/server/SystemServer.java 中。

    private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
                long currentTime, UserHandle user) throws PackageManagerException {
            if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
            PackageParser pp = new PackageParser();
            pp.setSeparateProcesses(mSeparateProcesses);
            // 设置仅解析 core app only,
            pp.setOnlyCoreApps(mOnlyCore);
            .....
    }

    这个方法定义在文件 frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java 中

    private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
            final PackageLite lite = parseClusterPackageLite(packageDir, 0);
    
            // 如果不是 !lite.coreApp, 跳过该 app,即启动时,不会安装该app
            if (mOnlyCoreApps && !lite.coreApp) {
                throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                        "Not a coreApp: " + packageDir);
            }

    这个方法定义在文件 frameworks/base/core/java/android/content/pm/PackageParser.java 中。

    安卓中定义为 coreApp 的应用有:

    这里写图片描述

    Framework-res.apk 的 manifest 配置文件如下:

    这里写图片描述

    步骤4:通过密码解密数据

    这个过程不再阐述。

    步骤5:停止框架

    #重启 main 类别服务
    on property:vold.decrypt=trigger_reset_main
        class_reset main

    这个 setion 定义在文件 system/core/rootdir/init.rc 中。

    步骤6:装载 /data

    static int do_mount_all(const std::vector<std::string>& args) {
        pid_t pid;
        .....
        //
        if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
            // 发送 encrypt 事件到 vdc
            ActionManager::GetInstance().QueueEventTrigger("encrypt");
        } else if
        .....
    }

    这个方法定义在文件 system/core/init/builtins.cpp 中。

    on encrypt
        start surfaceflinger
        exec - root -- /system/bin/vdc --wait cryptfs enablecrypto inplace default noui
        # vold will set vold.decrypt to trigger_restart_framework (default
        # encryption)

    这个 setion 定义在文件 system/vold/vdc.rc 中。

    on encrypt
        start surfaceflinger
        #发送命令 enablecrypto 到 vold
        exec - root -- /system/bin/vdc --wait cryptfs enablecrypto inplace default noui
        # vold will set vold.decrypt to trigger_restart_framework (default
        # encryption)

    这个 setion 定义在文件 system/vold/vdc.rc 中。

    int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
                                                     int argc, char **argv) {
        } else if (subcommand == "cryptocomplete") {
            if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
            dumpArgs(argc, argv, -1);
            rc = cryptfs_crypto_complete();
        // 命令是 enablecrypto
        } else if (subcommand == "enablecrypto") {
            .....
    
            // Spawn as thread so init can issue commands back to vold without
            // causing deadlock, usually as a result of prep_data_fs.
            char* arg2 = argc > 2 ? strdup(argv[2]) : NULL;
            char* arg4 = argc > 4 ? strdup(argv[4]) : NULL;
            // 执行 do_enablecrypto 方法
            std::thread(&do_enablecrypto, arg2, arg4, type, no_ui).detach();
    }

    这个方法定义在文件 system/vold/CryptCommandListener.cpp 中。

    static int do_enablecrypto(char* arg2, char* arg4, int type, bool no_ui) {
        int rc;
        int tries;
        for (tries = 0; tries < 2; ++tries) {
            // 不是默认加密,运行方法 cryptfs_enable()
            if (type == CRYPT_TYPE_DEFAULT) {
                rc = cryptfs_enable_default(arg2, no_ui);
            } else {
                rc = cryptfs_enable(arg2, type, arg4, no_ui);
            }
            .....
        return -1;
    }

    这个方法定义在文件 system/vold/CryptCommandListener.cpp 中。

    int cryptfs_enable(char *howarg, int type, char *passwd, int no_ui)
    {
        return cryptfs_enable_internal(howarg, type, passwd, no_ui);
    }

    这个方法定义在文件 system/vold/cryptfs.c 中。

    int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
                                int no_ui)
    {
        /* restart the framework. */
        /* Create necessary paths on /data */
        if (prep_data_fs()) {
            goto error_shutting_down;
        }
    }

    这个方法定义在文件 system/vold/cryptfs.c 中。

    static int prep_data_fs(void)
    {
        property_set("vold.post_fs_data_done", "0");
        // 设置 vold.decrypt = trigger_post_fs_data,触发 init.rc
        property_set("vold.decrypt", "trigger_post_fs_data");
        SLOGD("Just triggered post_fs_data\n");
    
        /* Wait a max of 50 seconds, hopefully it takes much less */
        for (i=0; i<DATA_PREP_TIMEOUT; i++) {
            char p[PROPERTY_VALUE_MAX];
            // 等待 init 设置 vold.post_fs_data_done = 1
            property_get("vold.post_fs_data_done", p, "0");
            if (*p == '1') {
                break;
            } else {
                usleep(50000);
            }
        }
    }

    这个方法定义在文件 system/vold/cryptfs.c 中。

    on property:vold.decrypt=trigger_post_fs_data
        trigger post-fs-data

    这个 setion 定义在文件 system/core/rootdir/init.rc 中。

    #创建/data 子目录和链接,启动服务
    on post-fs-data
        # We chown/chmod /data again so because mount is run as root + defaults
        chown system system /data
        chmod 0771 /data
        # We restorecon /data in case the userdata partition has been reset.
        restorecon /data
    
        # start debuggerd to make debugging early-boot crashes easier.
        start debuggerd
        start debuggerd64
    
        #task4597305  added by xiwu.peng to output logcat to uart
        start logcat2uart
    
        # Make sure we have the device encryption key.
        start vold
        installkey /data
    
        # Start bootcharting as soon as possible after the data partition is
        # mounted to collect more data.
        mkdir /data/bootchart 0755 shell shell
        bootchart_init
    
        .....
    
        mkdir /data/system_de 0770 system system
        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
        mkdir /data/user_de 0711 system system
        symlink /data/data /data/user/0
    
        mkdir /data/media 0770 media_rw media_rw
        mkdir /data/media/obb 0770 media_rw media_rw
    
        init_user0
        # If there is no fs-post-data action in the init.<device>.rc file, you
        # must uncomment this line, otherwise encrypted filesystems
        # won't work.
        # Set indication (checked by vold) that we have finished this action
        #setprop vold.post_fs_data_done 1

    这个 setion 定义在文件 system/core/rootdir/init.rc 中。

    static int cryptfs_restart_internal(int restart_main)
    {
        // init 做完 post-fs-data,继续往下执行代码
        if (prep_data_fs()) {
                return -1;
        }
        //init 做完 post-fs-data, vold 将 vold.decrypt 设为 trigger_restart_framework, 触发init
        /* startup service classes main and late_start */
        property_set("vold.decrypt", "trigger_restart_framework");
    }

    这个方法定义在文件 system/vold/cryptfs.c 中。

    #重启所有服务
    on property:vold.decrypt=trigger_restart_framework
        # A/B update verifier that marks a successful boot.
        exec - root -- /system/bin/update_verifier trigger_restart_framework
        class_start main
        class_start late_start

    步骤7:启动整个框架

    vold.decrypt = trigger_restart_framework, framework 就可以正常启动了。

    加密属性

    vold 和 init 之间通过设置属性进行通信。下面列出了可用的加密属性。

    vold 属性

    属性  说明
    vold.decrypt trigger_encryption     以无密码方式加密存储卷。
    vold.decrypt trigger_default_encryption     检查存储卷是否采用了无密码加密。如果是,则解密并装载存储卷;如果不是,则将 vold.decrypt 设为 trigger_restart_min_framework。
    vold.decrypt trigger_reset_main     由 vold 设置,用于关闭提示输入磁盘密码的界面。
    vold.decrypt trigger_post_fs_data   由 vold 设置,用于准备具有必要目录等内容的 /data。
    vold.decrypt trigger_restart_framework  由 vold 设置,用于启动实际框架和所有服务。
    vold.decrypt trigger_shutdown_framework     由 vold 设置,用于关闭整个框架以开始加密。
    vold.decrypt trigger_restart_min_framework  由 vold 设置,用于启动加密进度条界面或提示输入密码,具体取决于 ro.crypto.state 的值。
    vold.encrypt_progress   框架启动时,如果设置了此属性,则会进入进度条界面模式。
    vold.encrypt_progress 0 to 100  进度条界面中应按照设置显示百分比值。
    vold.encrypt_progress error_partially_encrypted     进度条界面中应显示一条消息,告诉用户加密失败,并且界面中应为用户提供一个用于将设备恢复出厂设置的按钮。
    vold.encrypt_progress error_reboot_failed   进度条界面中应显示一条消息,告诉用户加密已完成,并且界面中应为用户提供一个用于重新启动设备的按钮。此错误不应发生。
    vold.encrypt_progress error_not_encrypted   进度条界面中应显示一条消息,告诉用户发生错误,没有已加密的数据或数据已丢失,并且界面中应为用户提供一个用于重新启动系统的按钮。
    vold.encrypt_progress error_shutting_down   进度条界面未运行,因此不清楚谁将响应此错误。在任何情况下,都不应发生此错误。
    vold.post_fs_data_done 0    由 vold 在将 vold.decrypt 设为 trigger_post_fs_data 的前一刻设置。
    vold.post_fs_data_done 1    由 init.rc 或 init.rc 在完成 post-fs-data 任务之后立即设置。
    

    init 属性

    属性  说明
    ro.crypto.fs_crypto_blkdev  由 vold 命令 checkpw 设置,供 vold 命令 restart 以后使用。
    ro.crypto.state unencrypted     由 init 设置,用于说明相应系统正在未加密的 /data ro.crypto.state encrypted 中运行。由 init 设置,用于说明相应系统正在已加密的 /data 中运行。
    
    ro.crypto.fs_type
    ro.crypto.fs_real_blkdev
    ro.crypto.fs_mnt_point
    ro.crypto.fs_options
    ro.crypto.fs_flags
    这 5 个属性由 init 在尝试装载 /data(包含从 init.rc 传入的参数)时设置。vold 会使用这些属性来设置加密映射。
    ro.crypto.tmpfs_options     由 init.rc 设置,包含 init 在装载 tmpfs /data 文件系统时应使用的选项。
    

    init 操作

    on post-fs-data
    on nonencrypted
    on property:vold.decrypt=trigger_reset_main
    on property:vold.decrypt=trigger_post_fs_data
    on property:vold.decrypt=trigger_restart_min_framework
    on property:vold.decrypt=trigger_restart_framework
    on property:vold.decrypt=trigger_shutdown_framework
    on property:vold.decrypt=trigger_encryption
    on property:vold.decrypt=trigger_default_encryption.
    

    Android 全盘加密分析到此为止。

    参考 https://source.android.com/security/encryption/full-disk

    展开全文
  • Android加密之文件级加密

    万次阅读 2017-08-11 11:30:21
    Android加密之文件级加密.Android 的安全性问题一直备受关注,Google 在 Android 系统的安全方面也是一直没有停止过更新,努力做到更加安全的手机移动操作系统。文件级加密和全盘加密相比,有一些非常实用的优点,...

    Android加密之文件级加密

    前置文章

    《Android加密之全盘加密》

    《Android系统之System Server大纲》

    前言

    Android 的安全性问题一直备受关注,Google 在 Android 系统的安全方面也是一直没有停止过更新,努力做到更加安全的手机移动操作系统。

    在 Android 的安全性方面,有很多模块:

    1. 内核安全性
    2. 应用安全性
    3. 应用签名
    4. 身份验证
    5. Trusty TEE
    6. SELinux
    7. 加密
      等等

    其中,加密又分全盘加密(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");
        }
        ......
    }

    这个方法定义在文件 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
        .....

    这个 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

    这个 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;

    这个方法定义在文件 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);
    }

    这个方法定义在文件 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");
    }

    这个方法定义在文件 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;
    }

    这个方法定义在文件 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);
    }

    这个方法定义在文件 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 {
    
    }

    这个方法定义在文件 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));
        }
    }

    这个方法定义在文件 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));
        }
    }

    这个方法定义在文件 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);
    }

    这个方法定义在文件 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();
        }  
        .....                                             
    }

    这个方法定义在文件 system/vold/CryptCommandListener.cpp 中。

    函数 cryptfs_enable_file() 定义如下

    int cryptfs_enable_file()
    {
        return e4crypt_initialize_global_de();
    }

    这个函数定义在文件 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;
    }

    DE密钥创建过程就分析到这里。

    CE密钥

    同样在 init.rc 的 post-fs-data action 中

    on post-fs-data
        .....
        installkey /data
        .....
        执行 init_user0 命令
        init_user0
        .....

    这个 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}},
        .....
    }

    这个方法定义在文件 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();
    }

    这个方法定义在文件 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;
    }

    这个方法定义在文件 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());
        .....
    }

    这个方法定义在文件 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;
    }

    这个方法定义在文件 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;
    }

    这个方法定义在文件 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;
    }

    这个方法定义在文件 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;
        }
    }

    这个方法定义在文件 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

    这个 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}},
        .....
    }

    这个方法定义在文件 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;
    }

    这个方法定义在文件 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;
    }

    这个方法定义在文件 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;
    }

    这个方法定义在文件 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;
    }

    这个方法定义在文件 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);
        }
        .....

    这个方法定义在文件 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) {
            }
        }
    }

    这个方法定义在文件 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;
    }

    这个方法定义在文件 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;
                }
            }
        }
    
    }

    这个方法定义在文件 frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java 中。

    总结

    文件级加密,比较全盘加密具有一些优点,可以让没有输入凭证的设备可以使用更多的功能。文件级加密分 CE 空间和 DE 空间,CE 空间需要凭证加密方可使用,DE 空间则是设备启动后即可使用。应用如果需要区分 CE 和 DE 空间,需要创建不同的上下文环境 Context。

    展开全文
  • android 加密 解密

    2014-04-21 10:33:54
    Android 加密解密 http://www.eoeandroid.com/thread-66236-1-1.html Android 加密解密字符串 http://www.eoeandroid.com/thread-81425-1-1.html Android 用MD5加密算法加密密码(一) ...
    一、实例教程 

    Android 加密解密
    http://www.eoeandroid.com/thread-66236-1-1.html

    Android 加密解密字符串
    http://www.eoeandroid.com/thread-81425-1-1.html

    Android 用MD5加密算法加密密码(一)
    http://www.eoeandroid.com/thread-97843-1-1.html

    Android 用MD6加密算法加密密码(二)
    http://www.eoeandroid.com/thread-97846-1-1.html

    鉴客 Android客户端与服务器端通过DES加密认证
    http://www.eoeandroid.com/thread-159272-1-1.html

    android下md5加密,大家收藏下吧 ,用到的时候可以直接拿来用
    http://www.eoeandroid.com/thread-51590-1-1.html

    Android 获取自己程序APK包的MD5指纹
    http://www.eoeandroid.com/thread-92389-1-1.html

    给自己的网络数据加密
    http://www.eoeandroid.com/thread-97336-1-1.html

    JAVA加密解密
    http://www.eoeandroid.com/thread-69738-1-1.html


    二、资料下载 

    加密记事本,陈年作品(源码放出)
    http://www.eoeandroid.com/thread-99203-1-1.html

    通用DES加密解密方法
    http://www.eoeandroid.com/thread-56774-1-1.html

    一个加密的记事本
    http://www.eoeandroid.com/thread-122363-1-1.html




    展开全文
  • android加密解密完美教程

    千次阅读 2018-07-18 16:44:44
    android加密解密完美教程   经常使用加密算法:DES、3DES、RC4、AES,RSA等; 对称加密:des,3des,aes 非对称加密:rsa 不可逆加密:md5 加密模式:ECB、CBC、CFB、OFB等; 填充模式:NoPadding、PKCS1Padding...
  • 加密是使用对称加密密钥对 Android 设备上的所有用户数据进行编码的过程。设备经过加密后,所有由用户创建的数据在存入磁盘之前都会自动加密,并且所有读取操作都会在将数据返回给调用进程之前自动解密数据。加密可...
  • android加密参数定位方法

    千次阅读 多人点赞 2021-03-31 17:18:13
    在逆向一个Android程序时,如果只是盲目的分析需要阅读N多代码才能找到程序的关键点或Hook点,本文将分享一下如何快速的找到APP程序的加密参数位置,其实不论是找关键位置、找hook点,找加密参数、代码逻辑追踪,都...
  • Android加密算法

    千次阅读 2016-04-18 14:31:25
    目前的数据加密技术根据加密密钥类型可分私钥加密(对称加密)系统和公钥加密(非对称加密)系统。对称加密算法是较传统的加密体制,通信双方在加/解密过程中使用他们共享的单一密钥,鉴于其算法简单和加密速度快的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 77,978
精华内容 31,191
关键字:

android加密