gpt4 book ai didi

java - 将加密的 AES key 导入 Android Keystore 并将其存储在新别名下

转载 作者:太空宇宙 更新时间:2023-11-03 10:38:17 31 4
gpt4 key购买 nike

我只是在熟悉 Android Keystore API。我发现可以使用以下功能:

  • At least on some devices the Android Keystore is hardware backed, meaning that crypto operations run in a secure environment (TEE).
  • When the keystore is hardware backed, private RSA keys as well as secret symmetric keys that have been created within the Keystore can be configured to never leave the Keystore and the raw keys cannot be read out even with root access.

我现在想知道以下是否可行:

  1. Generate a Public/Private key pair where the private key never leaves the Keystore
  2. Upload the public key of this pair to a server
  3. On the server: create a random symmetric AES key and encrypt it with the public RSA key uploaded by the user
  4. On the device: Download this encrypted AES key
  5. Import it into the hardware backed Keystore such that it is decrypted in there with the private key of the pair and stored under a new alias
  6. Use this new key alias to perform symmetric encryption and decryption

1-4 应该是可能的,我现在缺少的链接是此列表中的第 5 点。有人可以帮助我并告诉我这是否可能和/或指向正确的 API 引用吗?

我发现了这个: https://android.googlesource.com/platform/development/+/master/samples/Vault/src/com/example/android/vault/SecretKeyWrapper.java

但在我看来, key 的解包似乎发生在正常环境中,并且解密后的 AES key 将在 App 中可用,这不能满足我的安全要求。

更新:

我使用链接 SecretKeyWrapper 创建了一个小型测试项目这是两个代码 fragment :

第一个执行以下操作:

  1. Create a random AES key (not within in the keystore, this is what would happen on a server later). Obviously the raw key can be retrieved from the generated SecretKey object what isn't a problem since the server can know the key.
  2. Encrypt/wrap the key with a RSA public key that was created in the client's Android Keystore (this would also happen on a server).
  3. Decrypt the key again with the RSA private key (this would happen on the client and actually happens within the TEE in the example).

fragment 1:

SecretKeyWrapper secretKeyWrapper = new SecretKeyWrapper(this,"testKeyRsa");

// Generate a random AES key (not in the keystore) [1]
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128);
SecretKey secretKeyGenerated = keyGen.generateKey();
byte[] secretKeyGeneratedRaw = secretKeyGenerated.getEncoded();

// wrap this key with the RSA key from the keystore [2]
byte[] wrappedKey = secretKeyWrapper.wrap(secretKeyGenerated);

// unwrap it again with the RSA key from the keystore [3]
SecretKey unwrappedKey = secretKeyWrapper.unwrap(wrappedKey);

// the raw key can be read again [4]
byte[] unwrappedKeyRaw = secretKeyGenerated.getEncoded();

我想要实现的是 [3] 中的解包 key 使用新别名存储在 keystore 中,而不返回原始 key 。当然我可以轻松导入 SecretKey对象进入 keystore ,但问题是,此时可以使用语句 [4] 从对象中检索原始 key ,这会导致安全漏洞。很明显,解包/解密已经在 keystore /TEE 中进行,因为用于解密的 RSA 私钥存在于 keystore 中,无法检索。

如果我将此与在 keystore 中创建随机 secret AES key 的情况进行比较,我注意到返回了不同的类型(实现 SecretKey 接口(interface))。在上面的例子中,类型是 SecretKeySpec ,而对于从 Android Keystore 返回的 key (请参阅下面的代码段 2),“不透明”类型用于 getEncoded() 的位置方法总是返回 null。在以下示例中,keyAesKeystore 的类型是AndroidKeyStoreSecretKey .

fragment 2:

// create a new AES key in the keystore
KeyGenerator keyGenAndroid = KeyGenerator.getInstance("AES","AndroidKeyStore");
keyGenAndroid.init(
new KeyGenParameterSpec.Builder("testKeyAes",
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
SecretKey keyAesKeystore = keyGenAndroid.generateKey();

// this returns null
byte[] keyAesKeystoreRaw = keyAesKeystore.getEncoded();

因此换个问题:是否可以通过某种方式将 RSA 包装的 AES key 安全地导入到 Android Keystore 中而不向应用程序泄露 key ?

更新 2:

@Robert 在下面的回答中绝对有效,如果解包发生在 TEE 或 Rich OS(应用程序)中实际上并不重要,因为应用程序(或篡改版本)以后总是可以(在拦截包装 key )只需“使用”Keystore 中的 RSA 私钥来解包 AES key (根本不需要访问原始私钥)。

不过还有一个想法:我发现可以在 Android keystore 中为 key 设置 key 保护参数(请参阅 here)。

SecretKeyWrapper 的链接实现不使用此类保护参数。更改 generateKeyPair 后方法如下并添加 PURPOSE_DECRYPTPURPOSE_ENCRYPT属性一切仍然有效。

private static void generateKeyPair(Context context, String alias)
throws GeneralSecurityException {
final Calendar start = new GregorianCalendar();
final Calendar end = new GregorianCalendar();
end.add(Calendar.YEAR, 100);
final KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)

.build();
final KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
gen.initialize(keyGenParameterSpec);
gen.generateKeyPair();
}

我现在可以通过删除 PURPOSE_DECRYPT 来保护 RSA key ,使其不能用于解密属性(property)。正如预期的那样 Cipher.unwrap方法停止工作并抛出 Incompatible purpose那么异常(exception)。

那么我需要的是一个保护属性,其中普通解密功能被阻止,但它允许我正在寻找的这种“安全导入功能”。类似“PURPOSE_IMPORT”的东西显然不存在。

最佳答案

从 API 级别 28 (Android Pie) 开始,您正在寻找的东西现在已经存在。要使用你,你需要:

  • 创建一个包装 key 对,一个目的为 PURPOSE_WRAP_KEY 的 RSA key 对。您还应该为公钥生成证明,以验证私钥是安全硬件中的 keystore key 。
  • 将公钥(和证明)从您的应用程序发送到将提供包装对称 key 的服务器。
  • 在服务器上,您需要包装对称 key 。这不仅仅涉及加密,因为包装器不仅需要包含 key Material ,还需要包含定义 key 使用方式的授权列表。这是通过根据模式 documented here 将 key 和授权信息打包在 ASN.1 DER 编码结构中来完成的。 . CTS test 中有一些示例包装代码.请注意,如果此格式看起来过于复杂(例如可选的“屏蔽 key ”),那是因为在未来的 Android 版本中将有相应的安全导出功能,并且使用案例需要额外的复杂性。安全导出功能没有进入 Q,但可能会进入 R。
  • 将包装好的 key 发送到应用程序,该应用程序必须创建一个 WrappedKeyEntry并使用 Keystore.setEntry()存储它。

这应该适用于 API 级别 28 的任何设备。但是,如果设备的 Keymaster 版本 < 4(请参阅认证证书以了解存在的 Keymaster 版本),然后解包操作将返回包装后的 key Android 用户空间的 Material 。 Keymaster 版本 4(或更高版本)会将未包装的 Material 保存在安全的硬件中,但由于较低版本不支持包装 key 功能,因此必须对其进行模拟。

如果您的 Keymaster 版本较低,会发生以下情况:当您创建 PURPOSE_WRAP_KEY key 对时,安全硬件实际请求的是 PURPOSE_DECRYPT key 对。然后当你进行导入时,keystore 守护进程使用这个 PURPOSE_DECRYPT 私钥从包装器中解密 secret ,然后它将 secret 导入安全硬件并删除保存它的用户空间内存。因此, key Material 在 keystore 守护程序的内存中存在几分之一毫秒。同样,如果设备具有 Keymaster 版本 4+,它只会在安全硬件中解包并且永远不会离开。

关于java - 将加密的 AES key 导入 Android Keystore 并将其存储在新别名下,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39249856/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com