nul*_*der 5 java security android cryptography keystore
我只是熟悉Android Keystore API。我发现可以使用以下功能:
- 至少在某些设备上,Android密钥库受硬件支持,这意味着加密操作在安全环境(TEE)中运行。
- 当密钥库由硬件支持时,可以将私钥RSA密钥以及在密钥库中创建的对称对称密钥配置为永不离开密钥库,并且即使具有root访问权限也无法读出原始密钥。
我现在想知道以下情况是否可能:
- 生成公钥/私钥对,其中私钥永远不会离开密钥库
- 将此对的公钥上传到服务器
- 在服务器上:创建随机对称AES密钥,并使用用户上传的公共RSA密钥对其进行加密
- 在设备上:下载此加密的AES密钥
- 将其导入到硬件支持的密钥库中,以便使用该对的私钥在其中解密并存储在新别名下
- 使用此新的密钥别名执行对称加密和解密
1-4应该是可能的,对于我来说,现在缺少的链接是此列表中的第5点。有人可以帮我一下,告诉我是否可行,和/或为我提供正确的API参考吗?
我发现了这一点:https : //android.googlesource.com/platform/development/+/master/samples/Vault/src/com/example/android/vault/SecretKeyWrapper.java
但是在我看来,秘密密钥的解开发生在正常环境中,并且解密的AES密钥将在应用程序中可用,这不能满足我的安全要求。
更新:
我使用链接创建了一个小型测试项目SecretKeyWrapper,这是两个代码段:
第一个执行以下操作:
- 创建一个随机的AES密钥(不在密钥库中,这将在以后的服务器上发生)。显然,可以从生成的
SecretKey对象中检索原始密钥,这没问题,因为服务器可以知道密钥。- 使用在客户端的Android密钥存储区中创建的RSA公共密钥对密钥进行加密/包装(这也会在服务器上发生)。
- 使用RSA私钥再次解密该密钥(这将在客户端上发生,并且实际上在示例中的TEE中发生)。
片段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();
Run Code Online (Sandbox Code Playgroud)
我想要实现的是,[3]中的未包装密钥以新的别名存储在密钥库中,而没有返回原始密钥。当然,我可以SecretKey在此处轻松地将对象导入到密钥库中,但是问题在于,此时可以使用语句[4]从对象中检索原始密钥,这会引发安全漏洞。显然,在密钥库/ TEE中已经发生了解包/解密,因为用于解密的专用RSA密钥位于密钥库中并且无法检索。
如果将此与在密钥库中创建随机秘密AES密钥的情况进行比较,则会注意到返回了不同的类型(实现SecretKey接口)。在上面的示例中,类型为SecretKeySpec,而对于从Android密钥库返回的键(请参见下面的代码段2),在getEncoded()方法始终返回null的情况下使用“不透明”类型。在以下示例中,类型keyAesKeystore为AndroidKeyStoreSecretKey。
片段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();
Run Code Online (Sandbox Code Playgroud)
因此,请改写这个问题:是否可以通过某种方式将RSA包装的AES密钥安全地导入Android密钥存储区,而无需向应用程序公开密钥?
更新2:
@Robert在下面的答案中给出绝对有效的信息,即在TEE或Rich OS(App)中发生解包实际上并不重要,因为该应用程序(或被篡改的版本)总是可以稍后(在截取包装好的密钥之后)只需“使用”密钥库中的RSA私钥来解开AES密钥(根本不需要访问原始私钥)。
不过,这是另外一种想法:我发现可以为Android密钥库中的密钥设置密钥保护参数(请参见此处)。
的链接实现SecretKeyWrapper不使用此类保护参数。在generateKeyPair按如下所示更改方法并添加PURPOSE_DECRYPT和PURPOSE_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();
}
Run Code Online (Sandbox Code Playgroud)
现在,我可以保护RSA密钥,使其通过删除该PURPOSE_DECRYPT属性而不能用于解密。如预期的那样,该Cipher.unwrap方法停止工作,Incompatible purpose然后引发异常。
因此,我需要的是一个保护属性,其中普通的解密功能被阻止了,但允许我正在寻找的这种“安全导入功能”。诸如“ PURPOSE_IMPORT”之类的东西显然不存在。
div*_*eek 10
从 API 级别 28 (Android Pie) 开始,您要查找的内容现在已经存在。要使用您,您需要:
这应该适用于 API 级别 28 的任何设备。 但是,如果设备的 Keymaster 版本 < 4(请参阅证明证书以了解存在的 Keymaster 版本),则解包操作会将包装好的密钥材料返回给 Android用户空间。Keymaster 版本 4(或更高版本)会将未包装的材料保存在安全硬件中,但由于较低版本不支持已包装的密钥功能,因此必须对其进行模拟。
如果您的 Keymaster 版本较低,那么当您创建 PURPOSE_WRAP_KEY 密钥对时,安全硬件实际请求的是 PURPOSE_DECRYPT 密钥对。然后,当您执行导入时,密钥库守护程序使用此 PURPOSE_DECRYPT 私钥从包装器解密机密,然后将机密导入安全硬件并擦除保存它的用户空间内存。因此,密钥材料在密钥库守护进程的内存中只存在几分之一毫秒。同样,如果设备具有 Keymaster 版本 4+,它只会在安全硬件中展开,永远不会离开。
| 归档时间: |
|
| 查看次数: |
3201 次 |
| 最近记录: |