【问题标题】:Android: Decryption process is taking lot of timeAndroid:解密过程需要很多时间
【发布时间】:2019-06-10 12:03:22
【问题描述】:

当我们使用以下代码创建键集时:

val generator = KeyPairGenerator.getInstance(RSA)
generator.initialize(KEY_SIZE)
val keyPair = generator.genKeyPair()

那么当我们调用如下:

val cipher = Cipher.getInstance(RSA_TRANSFORMATION)
cipher.init(Cipher.DECRYPT_MODE, privateKey)

init 方法执行在 0-2 毫秒内完成。

但是当我们尝试使用以下内容创建密钥时,我们需要将私钥存储在密钥库中:

val keyPairGenerator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore")
        keyPairGenerator.initialize(keyGenParameterSpecBuilder.getProvider()
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
                .setDigests(KeyProperties.DIGEST_SHA1)
                .setKeySize(KEY_SIZE)
                .build())

        keyPairGenerator.genKeyPair()

相同的 init 方法执行时间超过 35-40 毫秒。此外,如果我们注释 setEncryptionPaddings 和 setDigest,init 方法会抛出异常。

预期输出: 能够在 KeyStore 中存储私钥并在 0-2 毫秒内执行 init 方法,就像没有提供程序生成器一样。

【问题讨论】:

  • 40 毫秒并不是设置 RSA 私钥操作的很多时间,尤其是因为执行操作所花费的时间会使设置时间相形见绌。
  • 在其他情况下,所花费的时间约为 0-2 毫秒,这是我期望的输出。

标签: android encryption private-key


【解决方案1】:

AndroidKeyStore 总是比进程中的键操作慢很多。

当您进行进程内密钥生成时,会发生对 BoringSSL 加密库的 JNI 调用。没有 IPC 调用,没有上下文切换等。BoringSSL 将对内核进行一些系统调用以获取随机位(来自 /dev/random),并且有可能触发对硬件真随机数生成器的一些调用,但不多.

当您在 AndroidKeyStore 中生成密钥时会发生什么(粗略地说;硬件绑定器调用下面的所有内容都取决于实现,因此您必须与设备制造商交谈以了解详细信息):

  • 您的进程对 Keystore 进程进行绑定调用,这可能会启动一个线程来处理您的请求。
  • Keystore 从 /dev/random 获取一些随机位。
  • Keystore 对 Keymaster HAL 服务进行硬件绑定调用。
  • 服务将密钥生成请求格式化为消息并将其写入字符设备节点,该节点调用内核驱动程序。
  • 内核驱动程序将请求与一些控制数据一起复制到缓冲区中并调用 SMC 指令。
  • 处理器暂停 Linux 并跳转到安全监视器处理程序,该处理程序检查一堆内容,然后将处理器切换到安全模式。
  • 受信任的 OS 内核开始执行,从传输缓冲区读取控制数据,识别应该接收并调用它的受信任应用程序。
  • 受信任的应用程序解析请求消息并生成您的密钥,通过从硬件真随机数生成器中读取来获取必要的随机位,并将该熵与密钥库(来自 /dev/random)提供的位安全地混合。
  • 然后,受信任的应用程序使用硬件绑定密钥对密钥进行加密,并将结果写入响应缓冲区并将控制权返回给受信任的操作系统。
  • 受信任的操作系统写入一些控制数据并调用 SMC 指令。
  • 处理器挂起受信任的操作系统并跳转到安全监视器处理程序,该处理程序检查一堆内容,然后将处理器切换出安全模式。
  • Linux内核开始执行,内核驱动通过字符设备节点返回数据。
  • HAL 服务从字符设备节点读取数据。
  • HAL 服务解析数据并通过硬件绑定器返回。
  • Keystore 接收加密的密钥包并将其写入文件(与您提供的别名相关联)。
  • Keystore 生成自签名证书并将其写入另一个文件。
  • Keystore 通过 binder 返回结果状态。

我认为 AndroidKeyStore 的性能可以改进,但从根本上说,它必须比进程内密钥生成多很多,而且所有这些 IPC 都需要时间。

额外的时间换来的是更高的安全性。使用进程内加密,破坏您的应用程序的攻击者可以获得私钥的副本,然后可以用它做任何他们喜欢的事情。使用 AndroidKeyStore,入侵您的应用程序的攻击者可能能够以与您的应用程序相同的方式使用密钥,但他们无法从设备中提取它,因此他们无法在其他任何地方使用它。此外,如果您对如何使用它添加一些限制(例如,仅当用户进行身份验证时),那么攻击者就不能违反这些限制。

即使攻击者不仅破坏了您的应用程序,还破坏了密钥库守护程序、HAL 服务,甚至 Linux 内核本身,这些安全保证仍然有效。要实际提取密钥,攻击者必须破坏受信任的应用程序或受信任的操作系统。这不是不可能的(没有什么是不可能的),但要困难得多。

为了完整起见,我还应该提到KeyGenParameterSpec.Builder.setIsStrongBoxBacked(true),在 API 级别 28 中可用。在支持 StrongBox 的设备上(目前不多,但这会改变)您的密钥不会在安全运行的受信任操作系统中生成在主 CPU 模式下,它将在一个专门构建的安全处理器中生成——一个嵌入式安全元件或类似的——称为“StrongBox”。 StrongBox 不得与主 CPU 共享处理器、RAM 或其他重要资源,并且必须由经认可的测试实验室针对直接渗透、侧信道、故障等进行正式评估。

StrongBox 设备通常比移动 CPU 更小、更慢的处理器。通常原始速度慢大约两个数量级,尽管它们通过专用的加密加速器硬件部分抵消了这一点。这意味着如果您使用KeyGenParameterSpec.Builder.setIsStrongBoxBacked(true),您可以预期的不是 40 毫秒,而是 400 毫秒,或者可能是 1000 毫秒。另一方面,从 StrongBox 设备中提取任何秘密是非常困难的。如果国家情报机构足够关心,他们可能会努力做到这一点。任何比这能力差的人都可能确实度过了一段非常艰难的时期。

(顺便说一句:如果你想提高性能,你应该考虑转储旧的、慢的 RSA。EC 更快。不幸的是,如果你需要非对称加密,AndroidKeyStore 还不支持使用 EC 进行加密/解密,只有签名/验证。但是,如果您可以使用对称加密,AES 和 HMAC 比 EC 和 RSA 快得多。)

(顺便说一句:我是从 API 级别 23 开始负责 AndroidKeyStore 的 Google 工程师。)

【讨论】:

  • 非常感谢您的详细回复
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-19
  • 2018-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多