当前位置: 首页 > 知识库问答 >
问题:

将加密的AES密钥导入Android Keystore并以新别名存储

羊浩广
2023-03-14
    null
  1. 生成公钥/私钥对,其中私钥从不离开密钥库
  2. 将此对的公钥上载到服务器
  3. 在服务器上:创建随机对称AES密钥,并用用户上传的公共RSA密钥加密
  4. 在设备上:下载此加密的AES密钥
  5. 将其导入到硬件支持的密钥存储库中,以便使用该对的私钥对其进行解密,并以新别名存储
  6. 使用此新密钥别名执行对称加密和解密

1-4应该是可能的,我现在缺少的环节是第5点。在这个列表中。有没有人可以帮助我,告诉我这是不是可能的,并且/或者给我指出正确的API引用?

我找到了:https://android.googlesource.com/platform/development/+/master/samples/vault/src/com/example/android/vault/secretkeywrapper.java

第一个执行以下操作:

  1. 创建一个随机AES密钥(不在密钥存储库中,这是以后在服务器上会发生的情况)。显然,可以从生成的secretkey对象中检索原始密钥,这不是问题,因为服务器可以知道密钥。
  2. 使用在客户端的Android Keystore中创建的RSA公钥加密/包装密钥(这也会发生在服务器上)。
  3. 使用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();
// 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();

@Robert在下面的回答中提出了一个绝对有效的观点,即解包发生在TEE或Rich OS(应用程序)中实际上并不重要,因为应用程序(或被篡改的版本)总是可以在稍后(截获包好的密钥之后)仅仅“使用”密钥存储库中的私有RSA密钥来解包AES密钥(根本不需要访问原始私钥)。

这里还有另一个想法:我发现可以在Android Keystore中为密钥设置密钥保护参数(参见此处)。

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密钥,使其不能用于解密。不出所料,cipher.unwrap方法停止工作,然后引发incompatible purpose异常。

因此,我需要的是一个保护属性,在该属性中,普通解密功能被阻止,但它允许这样一个“安全导入功能”,我正在寻找。类似于“purpose_import”的内容显然不存在。

共有1个答案

许俊雅
2023-03-14

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

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

这应该适用于API级别为28的任何设备。但是,如果设备的Keymaster版本<4(请参阅认证证书以了解Keymaster的版本),那么解包装操作会将包装好的密钥材料返回到Android用户空间。Keymaster版本4(或更高版本)将在安全的硬件中保留未包装的材料,但因为较低版本不支持包装的密钥特性,所以必须对其进行某种程度的模拟。

 类似资料:
  • 这是我的密码 抱歉,如果我的代码一团糟。

  • 问题内容: 我正在编写一个用于传输文件的小型应用程序,或多或少地将其作为一种学习更多编程加密基础的方法。这个想法是生成一个RSA密钥对,交换公共密钥,并发送AES iv和密钥以进一步解密。我想用接收者的RSA公钥加密AES密钥,如下所示: 然后,我将密钥值写给接收器,并按如下方式解密: 在控制台的另一端,我将其作为输出: 此外,如果我创建一个大小为16的字节数组,并将cipher.doFinal(

  • 问题内容: 我使用openssl生成认证密钥。这是我的命令: openssl genrsa -des3 -out enc_key.pem 1024 我导出到cer文件中,然后使用java keytool导入到Java keystore(jks)中。 密钥库听起来不错。我可以从Java应用程序加载密钥库。 问题是客户端连接到服务器(在这种情况下是FTP服务器,而不是Web服务器,而我使用apache

  • 使用EVP_BytesToKey()返回错误的key和iv可能出了什么问题? 我试过用iter计数值做实验,但似乎没有一个能产生工作键和IV。我假设命令行默认的iter计数是1。 同样确认的是,如果我用命令行显示的工作键和iv覆盖从EVP_BytesToKey()返回的内容和硬代码无符号char数组,我的其余代码工作正常,解密正确。 有人能帮忙吗?

  • 别名s1as被上述命令中使用的别名所取代,即server-alias(在下,上的SSL选项卡)。 jks和cacerts.jks存储在下。 下的JVM选项,上的JVM选项选项卡,以下选项 GlassFish服务器不喜欢什么? 它只在使用别名重新生成这些文件keystore.jks和cacerts.jks时工作,但当使用不同的别名(证书昵称)时,它会失败,出现给定的异常。GlassFish似乎将这个

  • MySQL Server Enterprise edition与Hashicorp集成,在静止状态下加密数据。加密密钥存储在HashiCorp存储库中,如MySQL文档中所述。 基于HashiCorp文档[1][2],我们可以使用Azure密钥库来存储秘密。 是否可以集成mysql-hashicorp-Azure密钥库 MySQL Enterprise Edition使用hashicorp加密 加