Android KeyStore流程

article/2025/9/24 13:43:23

文章目录

  • 一、Keystore
  • 二、Keystore架构及接口函数
    • 1. Keystore组件架构
    • 2. IKeymasterDevice.hal中的几个重要接口函数
      • 2.1 begin函数
      • 2.2 update函数
      • 2.3 finish函数
      • 2.4 abort函数
    • 3. Keymaster TA
    • 4. 对称密码函数API
  • 三、从Keystore到Keymaster的完整分析
    • 1. cts问题
    • 2. 代码流程分析
      • 2.1 模块调用关系
      • 2.2 代码分析

一、Keystore

keystore主要是对密钥库的控制操作,包括密钥的生成导入导出、加解密、签名验签、访问控制等。
概念的详细介绍就请看看google官网的介绍。本篇主要是想总结一下keystore的使用流程,貌似其他博客讲这个流程的不多,就整理一篇出来。

原创不易,转载请标明出处 https://blog.csdn.net/jackone12347/article/details/122252644

二、Keystore架构及接口函数

1. Keystore组件架构

keystore涉及到的模块之间的关系,一共有四个角色:

Andriod Keystore: 提供Android framework API,封装密钥库相关的接口;
Keystore:守护进程,通过Binder提供密钥库功能,通过AIDL与Framework keystore通讯;
HIDL Keymaster: HIDL进程,封装调用keymaster TA的密钥函数接口;如android.hardware.keymaster@xxx-xxx
KeyMaster TA: TEE中的TA,提供安全的密钥库操作和安全环境

架构图如下:
架构图

2. IKeymasterDevice.hal中的几个重要接口函数

需要看一下这几个相关的接口,HAL层的IKeymasterDevice.hal都有介绍,下面是我理解的几个,因为和接下来的章节中分析CTS问题是相关的。

2.1 begin函数

作用:使用指定的密钥开始加密操作。 如果一切顺利,begin() 必须返回 ErrorCode::OK 并创建一个操作句柄,该句柄必须传递给后续对 update()、finish() 或 abort() 的调用。
函数原型:

    begin(KeyPurpose purpose, vec<uint8_t> keyBlob, vec<KeyParameter> inParams,HardwareAuthToken authToken)generates (ErrorCode error, vec<KeyParameter> outParams, OperationHandle operationHandle);

2.2 update函数

作用:向正在进行的加密操作提供数据并可能从begin()接收输出。
函数原型:

    update(OperationHandle operationHandle, vec<KeyParameter> inParams, vec<uint8_t> input,HardwareAuthToken authToken, VerificationToken verificationToken)generates (ErrorCode error, uint32_t inputConsumed, vec<KeyParameter> outParams,vec<uint8_t> output);

说明:
1、为了为缓冲区处理提供更大的灵活性,此方法的实现具有使用比提供的更少的数据的选项。
调用者负责循环到在后续调用中提供其余数据。 必须返回consumed的输入量在 inputConsumed 参数中。
实现必须始终至少消耗一个字节,除非operation不能再接受;

如果提供的字节多于0且0字节是消耗,调用者必须认为这是一个错误并中止操作

2、作为update的结果,实现还可以选择返回多少数据。 这仅与加密和解密操作相关,因为签名和验证在完成之前不会返回任何数据。 建议尽早返回数据,而不是缓冲它。

2.3 finish函数

作用:完成以 begin() 开始的加密操作并使 operationHandle 无效。此方法是操作中最后调用的方法,因此必须返回所有处理过的数据。
函数原型:

    finish(OperationHandle operationHandle, vec<KeyParameter> inParams, vec<uint8_t> input,vec<uint8_t> signature, HardwareAuthToken authToken, VerificationToken verificationToken)generates (ErrorCode error, vec<KeyParameter> outParams, vec<uint8_t> output);

2.4 abort函数

中止以 begin() 开始的加密操作,释放所有内部资源并使操作句柄无效。
函数原型:

abort(OperationHandle operationHandle) generates (ErrorCode error);

3. Keymaster TA

keymaster_operation_t结构体,TA中会反复用到这个结构体中的数据。

@ ta/include/operations.h
typedef struct {uint8_t key_id[TAG_LENGTH];keymaster_key_blob_t *key;keymaster_blob_t nonce;keymaster_blob_t last_block;keymaster_operation_handle_t op_handle;keymaster_purpose_t purpose;keymaster_padding_t padding;keymaster_block_mode_t mode;keymaster_blob_list_item_t *sf_item;/*sign/verify data*/TEE_Time *last_access;TEE_OperationHandle *operation;TEE_OperationHandle *digest_op;size_t prev_in_size;uint32_t min_sec;uint32_t mac_length;uint32_t digestLength;uint32_t a_data_length;uint8_t *a_data;bool do_auth;bool got_input;bool buffering;bool padded;bool first;
} keymaster_operation_t;

keymaster_blob_t结构体

typedef struct {uint8_t* data;size_t data_length;
} keymaster_blob_t;

4. 对称密码函数API

Cryptographic API调用流程

-   some_function()                             (Trusted App) -
[1]   TEE_*()                      User space   (libutee.a)
------- utee_*() ----------------------------------------------
[2]       tee_svc_*()              Kernel space
[3]         crypto_*()                          (libtomcrypt.a and crypto.c)
[4]           /* LibTomCrypt */                 (libtomcrypt.a)

对称密码函数定义了执行对称密码操作(例如AES)的方式,涵盖分组密码和流密码。

Cryptographic Operations API - Symmetric Cipher Functions

void TEE_CipherInit(TEE_OperationHandle operation, const void *IV,uint32_t IVLen)

该函数启动对称密码操作,操作必须关联一个密钥。

TEE_Result TEE_CipherUpdate(TEE_OperationHandle operation, const void *srcData,uint32_t srcLen, void *destData, uint32_t *destLen)

该函数用来加密或解密输入数据,输入数据不必是块大小的倍数,除非对此函数的一个或多个调用提供了足够的输入数据,否则不会生成任何输出。

TEE_Result TEE_CipherDoFinal(TEE_OperationHandle operation,const void *srcData, uint32_t srcLen,void *destData, uint32_t *destLen)

完成密码操作,处理以前未通过调用TEE_CipherUpdate函数处理的数据以及srcData中提供的数据。随后操作句柄可以重用或重新初始化。

三、从Keystore到Keymaster的完整分析

直接干巴巴地看源码,有时候也不是很直观地看出这几大模块是如何串联起来 及如何完成从上到下的功能串调。
我们带着一个cts的问题来分析一下相关的流程吧。

1. cts问题

运行

adb shell am instrument -r -e class  android.keystore.cts.AES128CBCNoPaddingCipherTest#testDoFinalResets  -w android.keystore.cts/androidx.test.runner.AndroidJUnitRunner

报错:

 javax.crypto.IllegalBlockSizeExceptionat android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:490)at javax.crypto.Cipher.doFinal(Cipher.java:2055)at android.keystore.cts.BlockCipherTestBase.doFinal(BlockCipherTestBase.java:1400)at android.keystore.cts.BlockCipherTestBase.assertDoFinalResetsCipher(BlockCipherTestBase.java:594)at android.keystore.cts.BlockCipherTestBase.testDoFinalResets(BlockCipherTestBase.java:563)at android.keystore.cts.AES128CBCNoPaddingCipherTest.testDoFinalResets(AES128CBCNoPaddingCipherTest.java:19)at java.lang.reflect.Method.invoke(Native Method)at junit.framework.TestCase.runTest(TestCase.java:168)at junit.framework.TestCase.runBare(TestCase.java:134)at junit.framework.TestResult$1.protect(TestResult.java:115)at androidx.test.internal.runner.junit3.AndroidTestResult.runProtected(AndroidTestResult.java:73)at junit.framework.TestResult.run(TestResult.java:118)at androidx.test.internal.runner.junit3.AndroidTestResult.run(AndroidTestResult.java:51)at junit.framework.TestCase.run(TestCase.java:124)at androidx.test.internal.runner.junit3.NonLeakyTestSuite$NonLeakyTest.run(NonLeakyTestSuite.java:62)at androidx.test.internal.runner.junit3.AndroidTestSuite$2.run(AndroidTestSuite.java:101)at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)at java.util.concurrent.FutureTask.run(FutureTask.java:266)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)at java.lang.Thread.run(Thread.java:923)
Caused by: android.security.KeyStoreException: Keystore consumed 0 of 8 bytes provided.at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.update(KeyStoreCryptoOperationChunkedStreamer.java:143)at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineUpdate(AndroidKeyStoreCipherSpiBase.java:338)at javax.crypto.Cipher.update(Cipher.java:1682)at android.keystore.cts.BlockCipherTestBase.update(BlockCipherTestBase.java:1454)at android.keystore.cts.BlockCipherTestBase.assertDoFinalResetsCipher(BlockCipherTestBase.java:593)

2. 代码流程分析

下面针对以上报错内容,进行详细的分析,包含涉及到哪些模块,以及是怎么完成这项AES测试的。

2.1 模块调用关系

根据前面的内容得知一共有四个角色,其实还应该包含一个角色,就是JAVA应用程序APK,即keystore调用者。
所以五个角色为如下:
应用程序APK android.keystore.cts
Framework Keystore API
Keystore守护进程
Keymaster: HIDL进程:android.hardware.keymaster@xxx-xxx
Keymaster TA程序

五个角色的调用关系:

cts apk进程通过API ==> Framework Keystore API
Framework Keystore API 通过Binder调用 ==> Keystore进程
Keystore进程通过HIDL ==> Keymaster HAL进程
Keymaster HAL进程  ==> 调用TEE中的keymaster TA

2.2 代码分析

顺着cts报错,我们分析一下相关代码。

报错内容:

12-29 13:39:04.818  3206  3228 E TestRunner: Caused by: android.security.KeyStoreException: Keystore consumed 0 of 8 bytes provided.
12-29 13:39:04.818  3206  3228 E TestRunner: 	at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.update(KeyStoreCryptoOperationChunkedStreamer.java:143)
12-29 13:39:04.818  3206  3228 E TestRunner: 	at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineUpdate(AndroidKeyStoreCipherSpiBase.java:338)
12-29 13:39:04.818  3206  3228 E TestRunner: 	at javax.crypto.Cipher.update(Cipher.java:1682)
12-29 13:39:04.818  3206  3228 E TestRunner: 	at android.keystore.cts.BlockCipherTestBase.update(BlockCipherTestBase.java:1454)
12-29 13:39:04.818  3206  3228 E TestRunner: 	at android.keystore.cts.BlockCipherTestBase.assertDoFinalResetsCipher(BlockCipherTestBase.java:593)
  1. Framework KeyStore API
    BlockCipherTestBase.java的assertDoFinalResetsCipher函数如下:
    private void assertDoFinalResetsCipher(int opmode) throws Exception {byte[] input = getKatInput(opmode);byte[] expectedOutput = getKatOutput(opmode);createCipher();initKat(opmode);assertEquals(expectedOutput, doFinal(input));if ((opmode == Cipher.ENCRYPT_MODE) && (getKatIv() != null)) {// Assert that this cipher cannot be reused (thus making IV reuse harder)try {doFinal(input);fail();} catch (IllegalStateException expected) {}return;}// Assert that the same output is produced after the above resetassertEquals(expectedOutput, doFinal(input));assertEquals(expectedOutput, concat(update(subarray(input, 0, getBlockSize() * 3 / 2)),doFinal(subarray(input, getBlockSize() * 3 / 2, input.length))));assertEquals(expectedOutput, doFinal(input));// Assert that the IV with which the cipher was initialized is still there after the resets.assertEquals(getKatIv(), mCipher.getIV());assertAlgoritmParametersIv(getKatIv());}

getBlockSize()返回的是16字节,刚好是AES128的128bit, concat将两个24字节update和final送入,一共48个字节。
关于AES算法以及工作模式,请参考我写的另一博客AES及其工作模式详解

如果送入的数据是48字节,刚好是16字节的整数倍,没有问题;
但如果一次送入的数据是24字节,这样需要分多次送入,第一次先送入16字节,然后第二次送入8字节进行存储,然后再送入8字节,能拼成一个Blocksize 16字节进行处理。
这项cts测试大概就是这么个目的,想看一下设备中能否处理这种不是16字节整数倍的情况。

=》调用 mCipher.update函数

    protected byte[] update(byte[] input) {byte[] output = mCipher.update(input);assertUpdateOutputSize((input != null) ? input.length : 0, (output != null) ? output.length : 0);return output;}

内部的调用流程如下:
=》android.security.keystore.AndroidKeyStoreCipherSpiBase.engineUpdate
=》KeyStoreCryptoOperationChunkedStreamer.update
=》内部类MainDataStream的update实现
=》调用KeyStore.java的update方法
KeyStore》java的update方法,开始binder调用到守护进程Keystore

    public OperationResult update(IBinder token, KeymasterArguments arguments, byte[] input) {OperationPromise promise = new OperationPromise();try {mBinder.asBinder().linkToDeath(promise, 0);int errorCode =  mBinder.update(promise, token, arguments, input);if (errorCode == NO_ERROR) {return interruptedPreservingGet(promise.getFuture());} else {return new OperationResult(errorCode);}} catch (RemoteException e) {
,,,}

到这里的整个过程基本上都是AOSP的流程,所以问题一般不会出现在原生的代码流程中。
需要继续分析HAL层的调用。

Keystore服务端返回的流程在key_store_service.cpp中,能看到AIDL相关内容了。
调用dev->update函数和HAL keymaster进程通讯。

@system/security/keystore/key_store_service.cpp
Status KeyStoreService::update(const ::android::sp<IKeystoreOperationResultCallback>& cb,const ::android::sp<::android::IBinder>& token,const ::android::security::keymaster::KeymasterArguments& params,const ::std::vector<uint8_t>& input, int32_t* _aidl_return) {ALOGE("key_store_service update entry");if (!checkAllowedOperationParams(params.getParameters())) {return AIDL_RETURN(ErrorCode::INVALID_ARGUMENT);}auto dev = mKeyStore->getOperationDevice(token);dev->update(token, params.getParameters(), input, [this, cb, token](OperationResult result_) {if (!result_.resultCode.isOk()) {mKeyStore->removeOperationDevice(token);}cb->onFinished(result_);});ALOGE("key_store_service update done");return AIDL_RETURN(ResponseCode::NO_ERROR);
}

因为每家的keymaster方案,基本上都属于定制,为什么呢?因为HAL封装了和keymaster TA交互的接口。所以每家遇到的CTS问题可能都不大相同,需要具体问题具体分析了。
但各家基本上都会参考GP标准来开发

CA调用接口
TEEC_InvokeCommand(&sess, cmd, &op, &err_origin);

TA调用接口
TEE_CipherUpdate

最后这个CTS的修改是在keymaster TA中修复,具体code就不方便贴了,遇到类似问题时请具体看各家的keymaster TA及TEE中对应的libutee中封装的函数实现。

修复后的复测结果:

# adb shell am instrument -r -e class  android.keystore.cts.AES128CBCNoPaddingCipherTest#testDoFinalResets  -w android.keystore.cts/androidx.test.runner.AndroidJUnitRunner
INSTRUMENTATION_STATUS: class=android.keystore.cts.AES128CBCNoPaddingCipherTest
INSTRUMENTATION_STATUS: current=1
INSTRUMENTATION_STATUS: id=AndroidJUnitRunner
INSTRUMENTATION_STATUS: numtests=1
INSTRUMENTATION_STATUS: stream=
android.keystore.cts.AES128CBCNoPaddingCipherTest:
INSTRUMENTATION_STATUS: test=testDoFinalResets
INSTRUMENTATION_STATUS_CODE: 1
INSTRUMENTATION_STATUS: class=android.keystore.cts.AES128CBCNoPaddingCipherTest
INSTRUMENTATION_STATUS: current=1
INSTRUMENTATION_STATUS: id=AndroidJUnitRunner
INSTRUMENTATION_STATUS: numtests=1
INSTRUMENTATION_STATUS: stream=.
INSTRUMENTATION_STATUS: test=testDoFinalResets
INSTRUMENTATION_STATUS_CODE: 0
INSTRUMENTATION_RESULT: stream=Time: 8.063OK (1 test)

为方便与大家及时交流,弄了一个微信公众号,欢迎大家留言沟通~
在这里插入图片描述


http://chatgpt.dhexx.cn/article/YvVOAjCi.shtml

相关文章

AndroidStudio生成keystore

相信大家都慢吞吞的切换将开发工具迁移到了AS&#xff0c;今天&#xff0c;奉上生成keystore的方法。 看图&#xff1a; 点击我选中的Generate Signed APK&#xff0c;翻译过来大致是&#xff0c;生成已签署的APK&#xff0c;我们点击这一项 如果还没有生成keystore&#xff…

keytool生成keystore、truststore、证书

keytool生成keystore、truststore、证书 1. 打开cmd命令行&#xff0c;进入文件夹&#xff08;如&#xff1a;D:\test_icessl_key&#xff09;。keytool会把接下来生成的所有文件都保存到此处。 2. 输入&#xff1a;keytool -genkeypair -alias icesslkey -keyalg RSA -validi…

Android Studio 默认keystore 以及自定义keystore

我们使用Android Studio 运行或测试我们的app 它使用一个默认的debug.keystore进行签名。 这个默认签名(keystore)是不需要密码的&#xff0c;它的默认位置在 $HOME/.android/debug.keystore&#xff0c;如果不存在Android studio会自动创建它。 例如我的debug.keystore就在…

SSL证书中的keystore是什么

什么是keystore 是java的密钥库、用来进行通信加密用的、比如数字签名。keystore就是用来保存密钥对的&#xff0c;比如公钥和私钥。 在keystore里&#xff0c;包含两种数据&#xff1a; 密钥实体&#xff08;Key entity&#xff09;——密钥&#xff08;secret key&#xff0…

Keystore密钥库

近来由于项目需要做Single Sign On, 研究了一下CAS&#xff08;具体配置等下篇再介绍&#xff09;, 而这个CAS的配置最关键的不是CAS本身&#xff0c;而是数字证书&#xff0c;如何配置多台服务器之间的信任链接。因此&#xff0c;有必要把keystore, keytool的东西翻出来晒晒。…

KeyStore秘钥库

keytool 在打包Android APK的时候进行签名需要选择一个keystore&#xff0c;查看秘钥库&#xff1a; C:\Users\47355\.android>keytool -list -v -keystore debug.keystore 输入密钥库口令: 密钥库类型: PKCS12 密钥库提供方: SUN 您的密钥库包含 1 个条目 别名: androidd…

keystore 介绍

随时随地阅读更多技术实战干货&#xff0c;获取项目源码、学习资料&#xff0c;请关注源代码社区公众号(ydmsq666) Keytool 是一个有效的安全钥匙和证书的管理工具. Java 中的 keytool.exe &#xff08;位于 JDK\Bin 目录下&#xff09;可以用来创建数字证书&#xff0c;所有的…

安卓生成keystore和查看keystore

————————————————重要通知—————————————— Hello&#xff0c;本人的博客文章已更新至个人网站&#xff08;www.jonexu.cn&#xff09; 文章中有问题可以到网站联系博主&#xff0c;后续新的文章也将更新在个人网站 —————————————…

Python Tkinter库的简单使用

今天写了两个小小的图像界面小游戏&#xff0c;对Tkinter库进行了简单的熟悉。 1.随机造句小游戏&#xff1a; import Tkinter as tk import random window tk.Tk()def randomNoun():nouns ["cats", "hippos", "cakes"]noun random.choice…

《tkinter实用教程二》tkinter的子模块ttk

更多《Tkinter 实用教程》系列文章 tkinter 的子模块 ttk 本文主要介绍 tkinter 子模块的特性&#xff0c;以及和原生 tkinter 控件之间的区别。 tkinter.ttk 模块提供了对 tk 风格控件集合的访问途径&#xff0c;在 tkinter 8.5 中引入。 引入 tkinter.ttk 的基本思想是尽…

tkinter 使用详解

文章目录 1、窗口设置各控件 显示 一览表&#xff1a;变量有&#xff1a; 2、Label 标签部件3、Button 按钮部件4、Checkbutton 选择部件&#xff08;可以多选&#xff09;5、Radiobutton 选择部件&#xff08;只能单选&#xff09;6、Frame 部件LabelFrame 部件 7、Entry 单行…

Tkinter基础

1.tkinter tkinter是Python下面向tk的图形界面接口库&#xff0c;可以方便地进行图形界面设计和交互操作编程。tkinter的优点是简单易用、与Python的结合度好。tkinter在Python 3.x下默认集成&#xff0c;不需要额外的安装操作&#xff1b;不足之处为缺少合适的可视化界面设计工…

Python Tkinter教程(二)——Label控件、Frame控件、Button控件的完整参数和所有方法及详细用法

>>>【上节回顾&#xff1a;tkinter编程基本步骤、窗口基本属性及Toplevel控件的使用】<<< Python Tkinter教程&#xff08;二&#xff09; 这篇博客将详细并尽可能完整地介绍tkinter模块15种基本控件中的Label控件、Frame控件和Button控件&#xff0c;包括所…

【python】tkinter教程、35个tkinter示例代码和GUI图示

#示例1&#xff1a;主窗口及标题import tkinter as tkapp tk.Tk() #根窗口的实例(root窗口)app.title(Tkinter root window) #根窗口标题theLabel tk.Label(app, text我的第1个窗口程序&#xff01;) #label组件及文字内容theLabel.pack() #pack()用于自动调节组件的尺寸app.…

Tkinter模块学习

Tkinter 主窗口和位置大小 通过geometry(wxhxy)进行设置&#xff0c;w为宽度&#xff0c;h为高度&#xff0c;x表示距离屏幕左边的距离&#xff0c;-x表示距离屏幕右边的距离&#xff0c;y表示距离屏幕上边的距离&#xff0c;-y表示屏幕下边的距离 # -*- coding: UTF-8 -*- D…

Python GUI之tkinter库教程

tkinter的简介 tkinter 是 Python 的标准 GUI 库。它基于 Tk 工具包&#xff0c;该工具包最初是为工具命令语言&#xff08;Tool Command Language&#xff0c;Tcl&#xff09;设计的。Tk 普及后&#xff0c;被移植到很多其他的脚本语言中&#xff0c;包括 Perl&#xff0c;Ru…

Tkinter使用

一、前言 这篇文章是去年8月份学习python时顺便在博客园写的一篇笔记&#xff0c;现在搬运到hexo博客上来&#xff0c;主要针对新手。由于本篇文章较长&#xff0c;所以下面给出内容目录方便跳转阅读&#xff0c;当然也可以用博客页面最右侧的文章目录导航栏进行跳转查阅。 首…

Tkinter保姆级教程(上)

目录 什么是GUI Tkinter用法详解 第一个Tkinter程序 常用控件和属性 主窗口 Label标签控件 Button按钮控件 Entry输入控件 基本属性 Text 文本控件 列表框(ListBox)和组合框(Combobox) 单选框(Radiobutton)和多选框按钮(Checkbutton) 什么是GUI 图形用户界面&#xf…

《tkinter实用教程一》Linux环境下安装tkinter

更多《Tkinter 实用教程》系列文章 在 Linux 环境下安装 tkinter tkinter 是 Python 编程语言中描述用于构建图形用户界面 (GUI) 的控件集&#xff0c;因此&#xff0c;学习 tkinter 安装之前&#xff0c;需要首先确定您已经正确安装了 Python。 您可以使用如下命令&#xf…

Tkinter简介

Tkinter简介 (也叫 Tk 接口)是 Tk 图形用户界面工具包标准 的 Python 接口。 Tk 是一个轻量级的跨平台图形用户界面 (GUI)开发工具。Tk 和 Tkinter 可以运行在大多数 的 Unix 平台、Windows、和 Macintosh 系统。 由于是Python自带的标准库&#xff0c;我们想要使用它的时候…