1、什么是Android安全架构?
Android的密钥库已经有很多年了,它为应用程序开发者提供了一种使用加密密钥进行验证和加密的方法。 Keystore将密钥保留在应用程序的进程空间之外,以便应用程序不会无意中将其泄露给可能被钓鱼的用户,通过其他渠道泄漏,或者在应用程序遭到破坏时。 许多设备还为安全硬件中的密钥库密钥提供了基于硬件的安全性,从而将密钥材料完全保留在Android系统之外,从而即使Linux内核泄露也不会泄露密钥材料。 在绝大多数Android设备中,安全硬件是主CPU的特殊模式,硬件强制与Linux内核和Android用户空间隔离。比如基于Arm的TrustZone技术实现的TEE安全操作系统, 或者,某些设备使用单独的安全微处理器。比如SE芯片。
Android提供的API允许应用程序确定给定的密钥库密钥是否在安全硬件中,但是如果操作系统受到威胁,这些API可能不可靠。
1.1 KeyStore的发展历史
Keystore最初是在Android 4.0中引入的,而且密钥是用用户的密码加密的。 在Android 4.1中增加了使用设备安全硬件的基础设施。
直到Android 6.0,Keystore支持RSA和ECDSA。 在Android 6.0中,Keystore得到了显着增强,增加了对AES和HMAC的支持。 此外,加密操作的其他关键要素(如RSA填充1和AES块链接2模式)也被转移到安全的硬件中。
在Android 6.0中,Keystore也获得了限制特定密钥的使用方式的能力。 可以应用的最明显有用的限制是用户身份验证绑定。 这允许密钥的使用被"绑定"到用户的密码 - 它们的PIN,图案或密码 - 或指纹。 对于密码认证绑定,应用程序开发人员可以在几秒钟内指定超时。 如果用户上次输入的密码超过了指定的时间,安全硬件将拒绝任何使用该密钥的请求。 每次使用密钥时,指纹绑定密钥都需要新的用户身份验证。
其他更技术性的限制也可以应用于Android 6.0及更高版本。 特别是在密钥创建或导入时,有必要指定可以使用密钥的加密目的(加密,解密,签名或验证)以及填充和块模式,摘要,熵源 用于初始化向量或随机数,以及密码操作的其他细节。 由于指定的信息是永久性的,并且密码上与密钥材料绑定,所以密钥库不允许以任何其他方式使用密钥。 因此,获得应用程序或系统控制权的攻击者不能误用密钥。 为了帮助防止攻击,开发人员应该为给定的密钥指定可能的最窄范围。
Android密钥库最重要的变化之一是在Android 7.0中引入的。 使用安全锁屏启动Android 7.0+的新设备必须具有安全的硬件,并支持基于硬件的密码验证和密钥库密钥。 在Android 7.0之前,安全的硬件支持非常普遍,但是在未来的几年里,它将会变得普遍。
在Android 8.0中,所有安装了Google Play的新设备都必须提供关键证明。
1.2 为什么要密钥认证?
简单来说,Google的keystore机制提供了一套密钥管理机制,应用越来多,越来越重要了,但是密钥认证问题如果不解决,这一切都是不可靠的!
- keymaster HAL可以是假的,你没办法判断系统HAL以下是否真实,简单来说,如果没有使用TEE,你也可以通过keymaster HAL欺骗Framwork层。
- 即使设备是正常的,应用也可以欺骗第三方。
假设您正在开发一个应用程序,为银行的客户提供银行余额,交易历史记录和账单支付系统。 安全是重要的。 您不希望任何拿起用户手机的人访问他们的银行帐户。 一种方法是使用用户的网站密码。 但是这对用户来说通常是不方便的,因为网站经常需要长而复杂的密码,这在小的触摸屏上是不方便的。
使用Android Keystore,可以生成非对称身份验证密钥,例如256位ECDSA密钥,并让每个用户使用其复杂的Web密码登录一次,然后在银行的客户帐户数据库中注册公钥。 每次打开应用程序时,您都可以使用该ECDSA密钥执行质询 - 响应身份验证协议。 此外,如果您将密钥认证绑定,则用户每次打开应用程序时都可以使用其锁屏密码或指纹进行身份验证。 这使得他们可以在手机上使用更简单,更方便的认证机制。
如果攻击者危及Android并尝试提取密钥,则他们不会成功,因为密钥在安全硬件中。
作为应用程序开发人员,密钥认证允许您在服务器上验证您的应用程序所请求的ECDSA密钥实际上是否安全地存在于硬件中。 请注意,在您的应用程序本身中使用证明是没有意义的。 如果Android操作系统是不妥协和可信的,那么您可以使用6.0中引入的KeyInfo类来发现密钥是否在安全硬件中。 如果它被攻破,那么这个API和你在设备上验证证明的任何尝试都是不可靠的。
请注意,密钥证明不同于SafetyNet认证。 他们是相同的概念,但是证明不同的事物来自不同的地方。 密钥库密钥证明确认密钥存在于安全的硬件中并具有特定的特征。 SafetyNet认证确认设备是真实的(不是仿真器),并且运行已知的软件。 SafetyNet使用Keystore密钥证明,所以如果你想了解设备的完整性使用。 如果您想确认您的密钥是否在安全硬件中,请使用密钥认证。
2、KeyStore架构
当前比较新的Android 例如S的系统默认使用了keymaster的机制,如下图,Android系统中的应用进程加解密的逻辑处理,通过binder方式和HAL接口在TEE这个安全环境下(独立于linux/android系统的上下文,这个下节将会提到)执行。
KeyStore的源码解读参考专栏:keystore/keystore2/keymaster/keymint 深入学习-CSDN博客


这里我简单的总结一下:
- TEE:TEE是独立于Android和Linux内核的一套系统,他和Linux内核一样,由bootloader启动。他给整个设备提供了一套安全的环境,这里说的安全并不仅仅指软件,还特别包含加密硬件的相关支撑。即高通芯片/MTK芯片他们需要提供这样的硬件加密能力,TEE就是一套闭源的安全系统,他可以使用调用芯片的这些硬件加密能力。当然他们之间可能存在一些接口或者协议,就跟ARM架构和Linux系统之间一样。重点:TEE是提供安全的软硬件系统,站在软件的角度上他和Linux内核是同一级别的。
- KeymasterHAL:这里说的KeymasterHAL是运行在Android世界里面,它跟普通的硬件抽象层HAL服务没有本质的区别,唯一的区别就是他可以通过Linux内核去和TEE进行数据通信。
- KeyStoreService:它被init进程启动,即和其他native进程也没有本质的区别,但是他维护了一个秘钥相关的复杂数据库,同事完成了一些加密,解密,秘钥查询等事情。例如指纹锁/图案锁等涉及到的秘钥的一些信息数据被存储在这个数据库里面,这些操作都需要通过KeymasterHAL去和TEE进行交互,实际操作的地方还是在TEE空间里面。
- KeyStore:作为KeyStoreService为framework层提供的客户端,即上层应用可以通过KeyStore来调用native层的keystoreservice
- Application:需要使用秘钥相关的应用,常见的有锁屏应用、生物锁、银行金融APP等
3、Google KEY
3.1 什么是Google KEY?
参考Google Keybox功能与TEE关系介绍 Google attenstation keybox CTS认证 KEYBOX-CSDN博客
3.1.1 Google Keybox
Google Keybox 是一项由谷歌提供的安全功能,主要用于保护加密密钥和其他敏感数据。Keybox 通常与可信执行环境(TEE, Trusted Execution Environment)紧密结合,以增强其安全性。主要如下功能:
- 密钥管理:Keybox 主要用于安全地存储和管理加密密钥。这些密钥可以用于加密数据、保护用户凭证和其他敏感信息。
- 密钥保护:通过硬件级别的保护,Keybox 能确保密钥在使用过程中不会被暴露。它利用硬件安全模块(HSM, Hardware Security Module)或可信执行环境来实现这一点。
- 加密操作:Keybox 支持各种加密操作,如加密、解密、签名和验证。这些操作在硬件安全模块内执行,以防止密钥泄露。
- 设备认证:Keybox 还用于设备认证,确保只有经过认证的设备才能访问特定服务或数据。它可以存储设备唯一标识符(如设备 ID)和相关的认证密钥。
我们常见的使用场景如下:
- Android 安全性:在 Android 设备上,Keybox 与 TEE 紧密结合。Android 使用 TrustZone 作为 TEE,确保关键加密操作和密钥存储在安全的环境中进行。
- 设备认证与 DRM:Keybox 和 TEE 常用于设备认证和数字版权管理(DRM)。例如,流媒体服务会利用这些技术确保内容只能在认证过的设备上播放,防止盗版和未授权访问。
3.1.2 Google Attestation Keybox
Google Attestation Keybox 是一种安全机制,旨在验证设备的身份和完整性。这种机制依赖于设备内的硬件安全模块和可信执行环境(TEE),以确保设备在执行敏感操作时的可信度。其功能如下:
- 设备认证:Attestation Keybox 用于生成和存储设备唯一的认证密钥。这些密钥在设备与服务器通信时用于证明设备的真实性。
- 安全密钥存储:利用硬件安全模块和 TEE,Attestation Keybox 确保认证密钥存储在高度安全的环境中,不易受到恶意攻击或篡改。
- 远程证明:设备可以通过 Attestation Keybox 生成证明其身份和状态的证书。服务器可以验证这些证书,以确保设备没有被篡改并且是可信的。
- 数据完整性验证:通过生成和验证数字签名,Attestation Keybox 确保设备传输的数据没有被篡改,保障数据的完整性。
我们常见的使用场景如下:
- 设备初始化:在首次配置或重置设备时,Attestation Keybox 通过认证流程确保设备是合法的。
- 应用验证:一些应用程序在安装或运行时会使用设备证明来确保其运行环境安全。
- 安全更新:在软件更新过程中,利用设备证明来确保更新包是从可信源下载的,并且在更新过程中没有被篡改。
3.1.3 与TEE的关系
因为TEE 提供了一个安全隔离的环境(密钥在存储和使用过程中能得到有效保护,不易受到外部攻击),所以google keybox的存储和保护以及google attestation keybox的所有关键操作(如秘钥生成/签名等)都需要依赖TEE,即这些操作和实现都经过keysotre服务,在TEE中完成的。
3.2 Google KEY使用流程
步骤一:Google KEY的申请
- 每个device单独申请keybox(attestation key),首先需要去跑CTS白名单
- 根据CTS白名单的报告提交到google官方网站进行keybox申请
步骤二:Google KEY的切割
- google审核通过之后会返回一个keybox,为了工厂生产的需要,我们需要对其进行切割
步骤三:Google KEY的写入
- 切割之后的key,需要通过工具写入到android设备里面,通常进入meta模式来进行写key
步骤四:Google KEY查询是否写入
- 成功写入后/mnt/vendor/persist/attest_keybox.so是否存在
- adb shell getprop |grep "googlekey"或者进入工模模式是否有android key:OK
重点说明:Google KEY是否必须要写入?
google key可以不写入,通常我们在调试开发中很少写这个东西,不影响我们的基本功能使用。但是正常出货的设备或者需要做XTS的设备需要写google key,因为他会导致一些功能无法使用。
4、Widevine KEY
4.1 什么是Widevine KEY?
Android Widevine 自 Honey Comb ( 对手机是Ice Cream Sandwitch 4.0) 推出后成为CTS强制测试选项,厂商必须支持。无论是网络传输或者是消费类电子设备,Widevine针对数字媒体提供了视频格式无关的加解密方式。截止2012年,已经有5.39亿台设备支持 Android Widevine,截止2012年, Widevine 推行无软件授权费的发行方式。
Widevine是Google的数字版权管理(DRM)平台,用于保护流媒体服务的内容,防止盗版。它通过CENC加密、许可密钥交换和自适应流媒体质量管理视频,支持L3、L2和L1三个安全级别,其中L1适用于高清和HDR内容。Android和Chrome OS设备支持不同级别的Widevine,L1需要硬件的可信执行环境(TEE)以确保安全性。要流式传输高清内容,用户设备需通过Widevine L1认证。检查设备兼容性可使用DRM Info等应用或参考Netflix的设备列表。
详情参考:什么是 Widevine 数字版权管理 (DRM),它为什么重要-CSDN博客
4.1.1 Widevine相关支持
主要支持平台
- Smart Phone
- STB
- Blue Ray player
- Cloud TV
- Game console. ( Sony PS3, Nintendo Wii )
- Desktop ( Windows, Mac)
Widevine Security Level
- Level 1 Strictest Mandatory factory provision
- Level 2 Intermediate requirements
- Level 3 Field Provision
Android Widevine DRM Framework
- 自Honey Comb版本开始引入Android
- 受保护的音视频媒体播放
- 框架配置需要和DRM机制协同
- 利用插件体系结构,设备制造商可灵活配置以支持多DRM代理和机制
- 数字内容存储需要完成Widevine DRM软件集成
- 支持Widevine DRM 的Honey Comb版本最早发布于2011 Q2
4.1.2 与TEE的关系?
Widevine 是如何实现的Android设备支持L1或L3安全级别,具体取决于硬件和软件a实现,ChromeE OS也是如此。台式机上的Chrome最多只能支持L3,如果您的设备仅符合 L3 标准,则您的分辨率上限为 sub-HD。只有完全在TEE中进行处理的L1 安全设备才能播放来自Widevine安全服务的高清或更高质量的内容。关于Widevine最需要注意的一点可能是它不收取许可费以实施其保护技术。因此一些智能手机的缺失没有经济上的原因。
即Widevine KEY同Google KEY一样,需要依赖于TEE的环境。
4.4 Widevine KEY使用流程
4.4.1 Widevine 代码集成
Widevine的源代码需要从官方网站去下载,然后集成到我们的项目中,如下为其中一个高通项目集成的代码目录。

Android.mk配置如下,从中可知我们如果需要使用只需要讲android.hardware.drm的几个包集成到系统里面就可以了

Widevine的版本更新也比较简单,直接从官方网站下载最新的包,全部替换上面目录即可。替换完成之后需要使用Netflix APP验证一下能否正常播放即可(因为Netflix里面的视频都是数字版权的视频)。
4.4.2 Widevine KEY的写入
同google key的写入一致?
4.4.3 Widevine KEY的查询
可以通过一个apk来进行查询:drminfo_v1.1.8.210515_itmop.com.apk,此apk打开之后会获取相关信息,信息如下:

5、相关案例
案例一:Google KEY缺少导致一系列XTS问题
CtsKeystoreTestCases模块如下用例
android.keystore.cts.KeyAttestationTest#testRsaAttestation
android.keystore.cts.KeyAttestationTest#testEcAttestation
android.keystore.cts.KeyAttestationTest#testEcAttestation_UniqueIdWorksWithCorrectPermission
android.keystore.cts.KeyAttestationTest#testEcAttestation_DeviceLocked
android.keystore.cts.KeyAttestationTest#testRsaAttestation_DeviceLocked
GtsGmscoreHostTestCases模块如下用例
com.google.android.gts.security.AttestationRootHostTest#testRsaAttestationChainTee
com.google.android.gts.security.AttestationRootHostTest#testEcAttestationChainTee
VtsHalKeymasterV4_0TargetTest模块如下用例
PerInstance/AttestationTest#RsaAttestation/0_default
PerInstance/AttestationTest#EcAttestation/0_default
odsign_e2e_tests模块如下用例
com.android.tests.odsign.OnDeviceSigningHostTest#verifyArtUpgradeGeneratesAnyArtifacts
com.android.tests.odsign.OnDeviceSigningHostTest#verifyArtUpgradeSignsFiles

写google key能够pass
案例二:CtsIdentityTestCases

04-03 13:50:53.478 10218 12577 12589 E TestRunner: failed: dynamicAuthTest(android.security.identity.cts.DynamicAuthTest)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: ----- begin exception -----
04-03 13:50:53.478 10218 12577 12589 E TestRunner: java.lang.RuntimeException: Unexpected ServiceSpecificException with code 1
04-03 13:50:53.478 10218 12577 12589 E TestRunner: at android.security.identity.CredstoreIdentityCredentialStore.createCredential (CredstoreIdentityCredentialStore.java:135)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: at android.security.identity.cts.ProvisioningTest.createCredentialWithChallengeAndAcpId(ProvisioningTest.java:142)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: at android.security.identity.cts.ProvisioningTest.createCredential(ProvisioningTest.java:121)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: at android.security.identity.cts.DynamicAuthTest.dynamicAuthTest(DynamicAuthTest.java:76)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: at java.lang.reflect.Method.invoke(Native Method)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:61)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:148)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:142)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: at java.util.concurrent.FutureTask.run(FutureTask.java:264)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: at java.lang.Thread.run(Thread.java:1012)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: Caused by: android.os.ServiceSpecificException: HAL failed with exception code -8 (EX_SERVICE_SPECIFIC), service-specific error code 1, message 'Error initializing WritableIdentityCredential' (code 1)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: at android.os.Parcel.createExceptionOrNull(Parcel.java:3093)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: at android.os.Parcel.createException(Parcel.java:3063)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: at android.os.Parcel.readException(Parcel.java:3046)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: at android.os.Parcel.readException(Parcel.java:2988)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: at android.security.identity.ICredentialStoreStubProxy.createCredential(ICredentialStore.java:197)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: at android.security.identity.CredstoreIdentityCredentialStore.createCredential(CredstoreIdentityCredentialStore.java:125)
04-03 13:50:53.478 10218 12577 12589 E TestRunner: ... 12 more
此案例比较奇怪,我司内部版本在没有写google key的时候,单跑第一条报如上错误,从报错日志来看android.hardware.identity硬件抽象层服务存在问题,在写入了google key之后,此条能够PASS;但是在客户版本写了google key之后,还是出现一模一样的错误,就跟google key没有写一样。
初步分析同样环境排除google环境问题,剩下只有两种可能情况:google key秘钥不匹配;代码引入需要夹版本。最后确定为代码引入,客户注释了android.hardware.identity上层服务,但是没有注释hal层的声明。跟踪identity模块源码如下:


此问题原因没有搞清楚,但是可以大胆猜测,identity服务是针对身份等相关信息秘钥的认证,其依赖keystore<->tee架构,另外此服务放在google目录,因此需要通过google key来进行相关秘钥校验,因此在此案例中写了google key之后,因为上层服务被干掉,CtsIdentityTestCases针对identity服务的测试,identity服务无法正常实现功能,因此失败。
案例三:Widevine Version过低导致WvtsDeviceTestCases

因为widevine version过低,需要更新Widevine的版本(注意需要clean编译),更新之后的版本记录如下:

案例四:keystore数据库被损坏,导致所有应用无法打开

因为keystore数据库被损坏,导致使用了加密手机,即使输入正确的密码, 进入系统之后也无法正常解密文件系统,导致三方应用数据库无法加载,无法启动成功。后使用了keystore数据库备份方案:省略
案例五:因为KM版本太老导致odsign校验耗时开机时长超过100S
问题描述:格式化刷机第一次开机时长耗时110秒,第二次开机时长80秒,第三次开机时长100秒,第四次开机时长80秒,第五次开机时长100秒
日志分析:抓取开机日志发现奇数次和偶数次日志不一样,存在odsign去创建秘钥,验证秘钥失败删除秘钥,然后apex进行校验等。最后开机时长变长原因为init进程在等待odsign进行校验

问题分析:odsign为keysotre2里面的一个实例对象,因此验证了google key相关的, 最后发现和google key并没有什么关联,咨询google,google回复这里校验的key并不是google key,而是odsign自己的key,如下回复:



如上沟通记录最终和TEE供应商确定为我们的KM4.0不支持MAX_USES_PER_BOOT,KM4.1支持
解决方案:升级KM4.0到KM4.1,因为其源码在MTK基线都存在,因此做如下修改即可,开机时长恢复为40秒
bash
device/mediateksample/$(ProjectName)/ProjectConfig.mk
KEYMASTER_VERSION = 4.1