【问题标题】:Android KeyStore System - Saving a KeyPair?Android KeyStore 系统 - 保存密钥对?
【发布时间】:2015-08-11 17:59:43
【问题描述】:

我目前正在尝试在我的 Android 应用程序中整合一个正常运行的 KeyStore 实现。我目前正在针对 18 的最低 API 进行构建,以便我可以充分利用私有 KeyStore 为我的应用程序。我正在尝试生成n 数量的KeyPair 对象,并将它们保存在KeyStore 中以供以后检索。我看过this question,但它似乎有点过时(2012 年),并没有真正很好地回答任何问题。老实说,我在 Stack Overflow 上发现的大多数问题似乎都已经过时了,例如 herehere

所以我的预期流程是这样的:

  1. 尝试从与适当别名相关的证书中检索公钥。
  2. 如果此公钥为空,请创建一个新密钥。
  3. 使用KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
  4. 生成密钥对。

到目前为止,一切都非常简单,并且运行良好。接下来是毛茸茸的地方。

  1. 保存密钥对。通过KeyStore.getInstance("AndroidKeyStore");初始化KeyStore
  2. 尝试通过X509V3CertificateGenerator 生成X509Certificate。此证书是自签名的。对于证书,我将签名算法设置为"SHA1WithRSAEncryption"
  3. 最后拨打keyStore.setKeyEntry

对于这最后一步,似乎有两种选择:

keyStore.setKeyEntry(String alias, byte[] key, Certificate[] chain);

keyStore.setKeyEntry(String alias, Key key, char[] password, Certificate[] chain);

我从两者中的第二个开始,但收到了java.security.KeyStoreException: entries cannot be protected with passwords ...。好吧,这很奇怪,为什么会有一种方法可以保证抛出异常?让我们试试 1 号门。

此时,当我调用 setKeyEntry 并将 keyPair.getPrivate().getEncoded() 作为第二个参数传递时,我会收到来自系统的java.security.KeyStoreException: Operation not supported because key encoding is unknown

所以我有点不知所措。像这样的加密对我来说相对较新,所以我希望有人能对 Android KeyStore 系统这个非常令人困惑的情况有所了解。

【问题讨论】:

    标签: java android encryption android-keystore


    【解决方案1】:

    所以我找到了答案 - 希望这将有助于为未来的用户解决一些问题,因为文档中没有明确说明。

    KeyPairGeneratorSpec.Builder 有一个方法 setAlias。生成密钥时,它会自动存储在此别名下的 KeyStore 中。无需额外节省即可使其正常工作。然后,您可以通过使用与 KeyPairGenerator 相同的 String provider 实例化 KeyStore 来轻松检索这些密钥。

    【讨论】:

      【解决方案2】:

      我试图将 PKCS12 文件(比如用户手动下载)导入 AndroidKeyStore。看来

      keyStore.setKeyEntry(String alias, byte[] key, Certificate[] chain);
      

      AndroidKeyStore 没有实现,它总是抛出以下异常:

      KeyStoreException("Operation not supported because key encoding is unknown")
      

      参考: https://android.googlesource.com/platform/frameworks/base.git/+/android-5.1.1_r19/keystore/java/android/security/AndroidKeyStore.java

      @Override
          public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
                  throws KeyStoreException {
              throw new KeyStoreException("Operation not supported because key encoding is unknown");
          }
      

      所以,我尝试使用

      keyStore.setKeyEntry(String alias, Key key, char[] password, Certificate[] chain);
      

      通过将密码作为空值传递。对我来说效果很好。

      【讨论】: