Keystore 简介
Android Keystore 是一个允许开发人员在容器中创建和存储密钥的系统,这使得从设备中提取密钥变得更加困难。 这些密钥存储在专门的硬件中,即所谓的可信执行环境。 密钥可以在其内部生成,甚至操作系统本身也不应该直接访问这个安全内存。 Android Keystore 提供 API 来在这个受信任的环境中执行加密操作并接收结果。 它是在 API 18(Android 4.3)中引入的。 一个支持保险箱的 Android Keystore 是目前最安全和推荐的密钥存储类型。
利用 Android 密钥库系统,你可以在容器中存储加密密钥,从而提高从设备中提取密钥的难度。在密钥进入密钥库后,可以将它们用于加密操作,而密钥材料仍不可导出。此外,奇热它提供了密钥使用的时间和方式限制措施,例如要求进行用户身份验证才能使用密钥,或者限制为只能在某些加密模式中使用。
keyStore有什么作用?
· 程序升级
新旧版本的数字证书相同时 Android系统才会认为这是同一程序的不同版本 如果数字证书不同 会 产生冲突 要求更改包名
· 程序的模块化和开发
拥有同一签名的程序可以运行在同一进程中 可以分模块开发 用户在需要的时候下载对应的模块
· 多个程序间共享数据和代码
Android 提供了基于数字证书的权限赋予机制 如果某个权限(permission)的protectionLevel是signature 则这个权限就只能授予那些跟该权限所在的包拥有同一个数字证书的程序。
密钥库系统并不是让程序直接进行存储程序的私密信息的,比如说用户账号密码,其提供了一个密钥安全容器,保护密钥材料免遭未经授权的使用,一个应用程序可以在密钥库中存储多个密钥并且只允许应用自身访问,应用程序可以在密钥库系统中生成,存储,获取存储其中的公钥或者私钥,因此可使用密钥库系统中的密钥来进行数据的加密。
密钥库系统由 KeyChain API 以及在 Android 4.3(API 级别 18)中引入的 Android 密钥库提供程序功能使用。
Android Keystore 支持7种不同类型的密钥存储机制,每种机制各有优缺点。 例如,Android Keystore 使用硬件芯片以安全的方式存储密钥,而 Bouncy Castle Keystore (BKS)是一个软件密钥存储库,并使用放置在文件系统上的加密文件。 Android 文档提供了许多对开发人员有用的代码示例,但是在描述其 keystore 机制时有些令人困惑和费解。 这导致了很多开发人员头疼的问题。 在许多与 keystore 相关的类中,Android 文档通常直接取自 Java 文档。 在没有找到一个简洁明了的解释和一个开发安全本地身份验证的解决方案之后,开发人员搜索到了 StackOverflow,直接复制和粘贴代码,这些代码不安全,可以很容易地用一些 Frida 脚本绕过。 支离破碎的 Android 生态系统也使得使用 keystore 成为一种不愉快的体验,因为需要执行多个兼容性检查,并且需要实现各种代码路径以支持各种设备。
让我们看看下面的截图。 左边是 Android 文档,右边是 Java 文档。
AndroidKeystore 实现不支持解锁密钥存储库或其特定条目的密码。 文档中显示的代码片段将引发异常。 上面的截图显示 Android 文档中的关于 AndroidKeystore 实现的知识来源并不是100% 的可靠。 由于 Android 支持一种不包含在传统 Java 中的新型密钥存储系统,因此上面的例子不适用于 AndroidKeystore。 在发现了更多的问题之后,我们决定更深入地研究一下 Android 中可用的 keystore 系统。 在本文的后续部分,你可以找到用于测试不安全密钥存储库使用情况的方法。
Keystores 到处都是 keystores
一个 Android Keystore 只是一个 Android 开发者可以使用的 Java 类。 Android Keystore 是 Java Keystore API 的另一个实现,其他类型的 Keystore,比如 BSK,也实现了这个 API。
我们可以使用普通的 Java KeyStore API 来访问 Android KeyStore:
KeyStore ks = KeyStore.getInstance(“AndroidKeystore”); Keystore 类型
下面的表格列出了普通安卓系统(直到安卓9)支持的密钥库类型:
还有其他的密钥存储类型,例如三星支持它自己的名为 TIMA 的密钥存储类型。
如何使用 Android Keystore?
Java 密钥库 API 包含一个 java.security。 带有插入密钥的方法的 KeyStore 类。 但是,大多数应该在密钥库中插入密钥的调用都会引发异常。 这样做是为了防止开发人员插入硬编码的密钥。 Android Keystore 的假设是密钥永远不能离开可信环境,因此开发人员只能使用 android.security.keystore.KeyGenParameterSpec.Builder。 一个实现本地身份验证的示例引用应用程序可以点击这里查看。
存储在密钥存储库中的每个密钥都可以设置以下参数:
· alias - 别名,用来识别密钥
· key size - 密钥大小(API 23)
· purpose –加密 / 解密(API 23)
· 加密模式、算法和填充(API 23)
· 密钥在使用之前是否应该通过密钥存储库进行身份验证(API 23)
· 一个成功的身份验证之后密钥可以被使用的持续时间(API 23)
· 新登记的指纹上的钥匙应该作废吗(API 24) ?
· 密钥存储库在执行加密操作之前是否应该要求解锁屏幕? (API 28)
· 硬碟安全模组? 密钥应该由 StrongBox 硬件安全模块保护吗?(API 28)
支持的 Android API 版本包含在括号中,以显示在不同的 Android 版本中引入了哪些安全特性。 更多的设置和相关信息可以在适当的 Android 文档中找到。
让我们来讨论一下与密钥库相关的常见漏洞:
· 使用中的不安全密钥存储类型: 例如,如果正在使用 BKS 密钥存储,密钥存储在特权用户可以访问的文件中。 AndroidKeystore 支持硬件支持的容器,应该是首选的。
· 在新的指纹登记中没有失效的密钥: 有物理访问权限的攻击者可以登记他们自己的指纹并使用生物识别锁定的密钥。
· 无需屏幕解锁即可访问的 Keystore: 允许在不解锁屏幕的情况下使用密钥这无疑增加了攻击面。
· 使用中的弱加密算法: KeyGenerator.getInstance 方法中使用了弱加密算法。
· 为密钥存储库或密钥存储库条目设置了的弱密码或者硬编码了密码: 只有在可以设置密码访问密钥存储库时,才会出现这种类型的漏洞。 AndroidKeystore 是推荐的密钥存储类型,但如果应用程序设计需要使用软件支持的密钥存储,则建议设置一个强大的用户派生密码。
Frida 审计脚本
为了加快 keystore 审计并使评估更加健壮,我们准备了一些有用的 Frida 脚本。 这些脚本可以在这里找到。 下面的列表描述了它们的功能。
Keystore tracer
通用的 keystore 调试脚本:
· 列出应用程序使用的密钥存储类型
· 列出特定密钥存储库(或应用程序中的任何密钥存储库)使用的密钥存储库条目别名
· 列出所有关于 AndroidKeystore 密钥的信息。当确定是否可以绕过生物特征识别时非常有用。
· 在寻找有趣的密钥时,在逆向工程中有用的其他实用程序。
KeyGenParameterSpec tracer
· 列出有关在运行时使用 KeyGenParameterSpec API生成的密钥的信息。信息包括密钥大小、加密模式、算法等。
· 在审查应用程序中使用的加密时非常有用。
SecretKeyFactory tracer
· HOOK 掉 PBEKeySpec 构造函数和getInstance() 方法,以提取用于创建 PBKDF 密钥的密码、迭代次数、盐值和密钥大小。
· 在审查应用程序中使用的加密技术时非常有用——不仅仅是密钥库
Cipher tracer
· HOOK 掉 Cipher API,列出关于特定密钥的信息,如密钥大小、加密模式、算法等
· 在检查应用程序中使用的加密技术时非常有用——不仅仅是密钥存储
这些脚本可以通过以下命令启动:
$ frida -U -f com.example.keystorecrypto --no-pause -l SCRIPT-PATH.js 下面的代码片段显示了用于示例应用程序的 Keystore 跟踪程序脚本的输出:
如上所示,我们用一个函数发现了两个脆弱点:
· 可以添加新的指纹解锁应用程序
· 不需要对密钥使用进行用户身份验证
Cipher 跟踪程序可以成为检查加密操作非常有用的脚本。 该脚本转储有关应用程序使用的加密算法、模式和填充的信息。 此外,它 HOOK 了 doFinal 方法,并出现在加密 / 解密之前和之后可以理解为数据的操作输入和输出。 Cipher 跟踪输出的下列片段以可读的格式显示所描述的功能:
所以我们有一个 Android Keystore,它被认为是安全的,因为我们不能访问密钥。 但是,攻击者实际上可能并不需要密钥内容。 Keystore API 可用于检索密钥引用,然后可以使用它们初始化 Cipher 对象,然后可以使用它们对应用程序存储进行解密或加密。
是的,这是可能的,而且大多数应用程序都很容易受到这类攻击,具有设备实际访问权限或拥有特权的恶意软件的攻击者可以做以下事情:
· 启动受害者的应用程序
· HOOK 受害者的应用程序并使用 Frida 在受害者应用程序的上下文中执行代码,可以做到如下事情:
· 使用Keystore API检索对AndroidKeystore密钥的引用。
· 使用检索到的密钥引用初始化密码对象。
· 在应用程序存储中解密/加密/签名数据。
啊! 使用 Android Keystore 并不能保证二进制安全性。 为了防止这种攻击,开发人员必须将密钥库密钥标记为只有在以下情况下才能访问:
· 设备已经解锁
· 指纹或其他生物识别技术已被验证
对于此配置,开发人员必须在生成密钥期间将 setUserAuthenticationRequired() 设置为 true。 另一个重要属性是 setUserAuthenticationValidityDurationSeconds()。 如果设置为 -1,那么只有使用指纹或生物识别技术才能解锁密钥。 如果它被设置为任何其他值,也可以使用设备屏幕锁解锁密钥。
在设备屏幕锁定的情况下,首先通过调用 KeyguardManager.createConfirmDeviceCredentialIntent()来访问密钥。
需要注意的是,KeyguardManager API 不允许开发人员检查配置了哪种类型的屏幕锁,也不允许验证密码或PIN码或手势图案模式策略。 因此,这个设备可能有一个不安全的屏幕锁:
· 简单的图案(在大多数设备上是3x3,可以通过尝试常见的图案或检查屏幕上的指纹来猜测)
· 简单的PIN码(通常是4-5个数字,常见的PIN码或特别简单的如0000或1234这样的连续PIN码)
· 可猜测的密码(你的狗的名字)
因此,建议对于银行应用程序等高度敏感的应用程序,密码管理器或安全信使的 setUserAuthenticationValidityDurationSeconds() 不应该有除 -1以外的任何值。
此脚本(https://github.com/mwrlabs/android-keystore-audit/blob/master/frida-scripts/keyguard-credential-intent.js)可用于使用 KeyguardManager 触发“设备解锁”状态,并解锁未将有效期设置为 -1的密钥。
Keystore 简介
Android Keystore 是一个允许开发人员在容器中创建和存储密钥的系统,这使得从设备中提取密钥变得更加困难。 这些密钥存储在专门的硬件中,即所谓的可信执行环境。 密钥可以在其内部生成,甚至操作系统本身也不应该直接访问这个安全内存。 Android Keystore 提供 API 来在这个受信任的环境中执行加密操作并接收结果。 它是在 API 18(Android 4.3)中引入的。 一个支持保险箱的 Android Keystore 是目前最安全和推荐的密钥存储类型。
利用 Android 密钥库系统,你可以在容器中存储加密密钥,从而提高从设备中提取密钥的难度。在密钥进入密钥库后,可以将它们用于加密操作,而密钥材料仍不可导出。此外,奇热它提供了密钥使用的时间和方式限制措施,例如要求进行用户身份验证才能使用密钥,或者限制为只能在某些加密模式中使用。
keyStore有什么作用?
· 程序升级
新旧版本的数字证书相同时 Android系统才会认为这是同一程序的不同版本 如果数字证书不同 会 产生冲突 要求更改包名
· 程序的模块化和开发
拥有同一签名的程序可以运行在同一进程中 可以分模块开发 用户在需要的时候下载对应的模块
· 多个程序间共享数据和代码
Android 提供了基于数字证书的权限赋予机制 如果某个权限(permission)的protectionLevel是signature 则这个权限就只能授予那些跟该权限所在的包拥有同一个数字证书的程序。
密钥库系统并不是让程序直接进行存储程序的私密信息的,比如说用户账号密码,其提供了一个密钥安全容器,保护密钥材料免遭未经授权的使用,一个应用程序可以在密钥库中存储多个密钥并且只允许应用自身访问,应用程序可以在密钥库系统中生成,存储,获取存储其中的公钥或者私钥,因此可使用密钥库系统中的密钥来进行数据的加密。
密钥库系统由 KeyChain API 以及在 Android 4.3(API 级别 18)中引入的 Android 密钥库提供程序功能使用。
Android Keystore 支持7种不同类型的密钥存储机制,每种机制各有优缺点。 例如,Android Keystore 使用硬件芯片以安全的方式存储密钥,而 Bouncy Castle Keystore (BKS)是一个软件密钥存储库,并使用放置在文件系统上的加密文件。 Android 文档提供了许多对开发人员有用的代码示例,但是在描述其 keystore 机制时有些令人困惑和费解。 这导致了很多开发人员头疼的问题。 在许多与 keystore 相关的类中,Android 文档通常直接取自 Java 文档。 在没有找到一个简洁明了的解释和一个开发安全本地身份验证的解决方案之后,开发人员搜索到了 StackOverflow,直接复制和粘贴代码,这些代码不安全,可以很容易地用一些 Frida 脚本绕过。 支离破碎的 Android 生态系统也使得使用 keystore 成为一种不愉快的体验,因为需要执行多个兼容性检查,并且需要实现各种代码路径以支持各种设备。
让我们看看下面的截图。 左边是 Android 文档,右边是 Java 文档。
AndroidKeystore 实现不支持解锁密钥存储库或其特定条目的密码。 文档中显示的代码片段将引发异常。 上面的截图显示 Android 文档中的关于 AndroidKeystore 实现的知识来源并不是100% 的可靠。 由于 Android 支持一种不包含在传统 Java 中的新型密钥存储系统,因此上面的例子不适用于 AndroidKeystore。 在发现了更多的问题之后,我们决定更深入地研究一下 Android 中可用的 keystore 系统。 在本文的后续部分,你可以找到用于测试不安全密钥存储库使用情况的方法。
Keystores 到处都是 keystores
一个 Android Keystore 只是一个 Android 开发者可以使用的 Java 类。 Android Keystore 是 Java Keystore API 的另一个实现,其他类型的 Keystore,比如 BSK,也实现了这个 API。
我们可以使用普通的 Java KeyStore API 来访问 Android KeyStore:
KeyStore ks = KeyStore.getInstance(“AndroidKeystore”); Keystore 类型
下面的表格列出了普通安卓系统(直到安卓9)支持的密钥库类型:
还有其他的密钥存储类型,例如三星支持它自己的名为 TIMA 的密钥存储类型。
如何使用 Android Keystore?
Java 密钥库 API 包含一个 java.security。 带有插入密钥的方法的 KeyStore 类。 但是,大多数应该在密钥库中插入密钥的调用都会引发异常。 这样做是为了防止开发人员插入硬编码的密钥。 Android Keystore 的假设是密钥永远不能离开可信环境,因此开发人员只能使用 android.security.keystore.KeyGenParameterSpec.Builder。 一个实现本地身份验证的示例引用应用程序可以点击这里查看。
存储在密钥存储库中的每个密钥都可以设置以下参数:
· alias - 别名,用来识别密钥
· key size - 密钥大小(API 23)
· purpose –加密 / 解密(API 23)
· 加密模式、算法和填充(API 23)
· 密钥在使用之前是否应该通过密钥存储库进行身份验证(API 23)
· 一个成功的身份验证之后密钥可以被使用的持续时间(API 23)
· 新登记的指纹上的钥匙应该作废吗(API 24) ?
· 密钥存储库在执行加密操作之前是否应该要求解锁屏幕? (API 28)
· 硬碟安全模组? 密钥应该由 StrongBox 硬件安全模块保护吗?(API 28)
支持的 Android API 版本包含在括号中,以显示在不同的 Android 版本中引入了哪些安全特性。 更多的设置和相关信息可以在适当的 Android 文档中找到。
让我们来讨论一下与密钥库相关的常见漏洞:
· 使用中的不安全密钥存储类型: 例如,如果正在使用 BKS 密钥存储,密钥存储在特权用户可以访问的文件中。 AndroidKeystore 支持硬件支持的容器,应该是首选的。
· 在新的指纹登记中没有失效的密钥: 有物理访问权限的攻击者可以登记他们自己的指纹并使用生物识别锁定的密钥。
· 无需屏幕解锁即可访问的 Keystore: 允许在不解锁屏幕的情况下使用密钥这无疑增加了攻击面。
· 使用中的弱加密算法: KeyGenerator.getInstance 方法中使用了弱加密算法。
· 为密钥存储库或密钥存储库条目设置了的弱密码或者硬编码了密码: 只有在可以设置密码访问密钥存储库时,才会出现这种类型的漏洞。 AndroidKeystore 是推荐的密钥存储类型,但如果应用程序设计需要使用软件支持的密钥存储,则建议设置一个强大的用户派生密码。
Frida 审计脚本
为了加快 keystore 审计并使评估更加健壮,我们准备了一些有用的 Frida 脚本。 这些脚本可以在这里找到。 下面的列表描述了它们的功能。
Keystore tracer
通用的 keystore 调试脚本:
· 列出应用程序使用的密钥存储类型
· 列出特定密钥存储库(或应用程序中的任何密钥存储库)使用的密钥存储库条目别名
· 列出所有关于 AndroidKeystore 密钥的信息。当确定是否可以绕过生物特征识别时非常有用。
· 在寻找有趣的密钥时,在逆向工程中有用的其他实用程序。
KeyGenParameterSpec tracer
· 列出有关在运行时使用 KeyGenParameterSpec API生成的密钥的信息。信息包括密钥大小、加密模式、算法等。
· 在审查应用程序中使用的加密时非常有用。
SecretKeyFactory tracer
· HOOK 掉 PBEKeySpec 构造函数和getInstance() 方法,以提取用于创建 PBKDF 密钥的密码、迭代次数、盐值和密钥大小。
· 在审查应用程序中使用的加密技术时非常有用——不仅仅是密钥库
Cipher tracer
· HOOK 掉 Cipher API,列出关于特定密钥的信息,如密钥大小、加密模式、算法等
· 在检查应用程序中使用的加密技术时非常有用——不仅仅是密钥存储
这些脚本可以通过以下命令启动:
$ frida -U -f com.example.keystorecrypto --no-pause -l SCRIPT-PATH.js 下面的代码片段显示了用于示例应用程序的 Keystore 跟踪程序脚本的输出:
如上所示,我们用一个函数发现了两个脆弱点:
· 可以添加新的指纹解锁应用程序
· 不需要对密钥使用进行用户身份验证
Cipher 跟踪程序可以成为检查加密操作非常有用的脚本。 该脚本转储有关应用程序使用的加密算法、模式和填充的信息。 此外,它 HOOK 了 doFinal 方法,并出现在加密 / 解密之前和之后可以理解为数据的操作输入和输出。 Cipher 跟踪输出的下列片段以可读的格式显示所描述的功能:
所以我们有一个 Android Keystore,它被认为是安全的,因为我们不能访问密钥。 但是,攻击者实际上可能并不需要密钥内容。 Keystore API 可用于检索密钥引用,然后可以使用它们初始化 Cipher 对象,然后可以使用它们对应用程序存储进行解密或加密。
是的,这是可能的,而且大多数应用程序都很容易受到这类攻击,具有设备实际访问权限或拥有特权的恶意软件的攻击者可以做以下事情:
· 启动受害者的应用程序
· HOOK 受害者的应用程序并使用 Frida 在受害者应用程序的上下文中执行代码,可以做到如下事情:
· 使用Keystore API检索对AndroidKeystore密钥的引用。
· 使用检索到的密钥引用初始化密码对象。
· 在应用程序存储中解密/加密/签名数据。
啊! 使用 Android Keystore 并不能保证二进制安全性。 为了防止这种攻击,开发人员必须将密钥库密钥标记为只有在以下情况下才能访问:
· 设备已经解锁
· 指纹或其他生物识别技术已被验证
对于此配置,开发人员必须在生成密钥期间将 setUserAuthenticationRequired() 设置为 true。 另一个重要属性是 setUserAuthenticationValidityDurationSeconds()。 如果设置为 -1,那么只有使用指纹或生物识别技术才能解锁密钥。 如果它被设置为任何其他值,也可以使用设备屏幕锁解锁密钥。
在设备屏幕锁定的情况下,首先通过调用 KeyguardManager.createConfirmDeviceCredentialIntent()来访问密钥。
需要注意的是,KeyguardManager API 不允许开发人员检查配置了哪种类型的屏幕锁,也不允许验证密码或PIN码或手势图案模式策略。 因此,这个设备可能有一个不安全的屏幕锁:
· 简单的图案(在大多数设备上是3x3,可以通过尝试常见的图案或检查屏幕上的指纹来猜测)
· 简单的PIN码(通常是4-5个数字,常见的PIN码或特别简单的如0000或1234这样的连续PIN码)
· 可猜测的密码(你的狗的名字)
因此,建议对于银行应用程序等高度敏感的应用程序,密码管理器或安全信使的 setUserAuthenticationValidityDurationSeconds() 不应该有除 -1以外的任何值。
此脚本(https://github.com/mwrlabs/android-keystore-audit/blob/master/frida-scripts/keyguard-credential-intent.js)可用于使用 KeyguardManager 触发“设备解锁”状态,并解锁未将有效期设置为 -1的密钥。
举报