【问题标题】:How to make client certificate authentication from Android 4.1 with Apache client and the KeyChain API如何使用 Apache 客户端和 KeyChain API 从 Android 4.1 进行客户端证书身份验证
【发布时间】:2012-11-02 01:41:42
【问题描述】:

由于 ICS Android 支持通过 KeyChain API 统一访问系统密钥库和受信任的 CA。

这很好,但是当我尝试使用来自该来源的私钥进行客户端证书身份验证时,给我带来了麻烦。

我在这里检查了一些答案,我发现最好的答案是:Android 4.0 SSL Authentication

参考4.0,提供如下编码:

更新 - 这个编码是错误的 - 它不能在 4.1+ 中工作

String alias = "test";
KeyStore memoryKeyStore = KeyStore.getInstance("BKS");
memoryKeyStore.load(null);
X509Certificate[] chain = KeyChain.getCertificateChain(getApplicationContext(),alias);
PrivateKey key = KeyChain.getPrivateKey(getApplicationContext(),alias);
memoryKeyStore.setKeyEntry(alias, key.getEncoded(), chain);

这在 4.1 中不起作用,因为 KeyChain.getPrivateKey() 返回的 PrivateKey 对象在调用 getEncoded() 方法并且无法初始化 KeyStore 时返回 null。

在 4.1 中有没有其他方法可以做到这一点?

更新 - 这里是定义您自己的 KeyStore 和 KeyStoreSpi 实现的正确方法 这里是 KeyStore 和 KeyStoreSpi 的示例实现:

KeyStoreSpi 实现:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Key;
import java.security.KeyStoreException;
import java.security.KeyStoreSpi;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;

import java.security.PrivateKey;

public class KeyChainProxy extends KeyStoreSpi {


  private String alias = null;
  private PrivateKey privateKey = null;
  private Certificate[] certChain = null;

  public KeyChainProxy(String alias, PrivateKey privateKey, Certificate[] certChain) {
    this.alias = alias;
    this.privateKey = privateKey;
    this.certChain = certChain;
  }

  @Override
  public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, UnrecoverableKeyException {
    return privateKey;
  }

  @Override
  public Certificate[] engineGetCertificateChain(String alias) {
    return certChain;
  }

  @Override
  public Certificate engineGetCertificate(String alias) {
    return certChain[0];
  }

  @Override
  public Date engineGetCreationDate(String alias) {
    return new Date();
  }

  @Override
  public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) throws KeyStoreException {
    throw new KeyStoreException("Not Implemented");

  }

  @Override
  public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) throws KeyStoreException {
    throw new KeyStoreException("Not Implemented");

  }

  @Override
  public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
    throw new KeyStoreException("Not Implemented");

  }

  @Override
  public void engineDeleteEntry(String alias) throws KeyStoreException {
    throw new KeyStoreException("Not Implemented");

  }

  @Override
  public Enumeration<String> engineAliases() {
    List<String> list = new ArrayList<String>();
    list.add(alias);
    return Collections.enumeration(list);
  }

  @Override
  public boolean engineContainsAlias(String alias) {
    return alias != null && alias.equals(this.alias);
  }

  @Override
  public int engineSize() {
    return 1;
  }

  @Override
  public boolean engineIsKeyEntry(String alias) {
    return true;
  }

  @Override
  public boolean engineIsCertificateEntry(String alias) {
    return false;
  }

  @Override
  public String engineGetCertificateAlias(Certificate cert) {
    return null;
  }

  @Override
  public void engineStore(OutputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException {

  }

  @Override
  public void engineLoad(InputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException {

  }

}

KeyStore 实现:

import java.security.KeyStore;
import java.security.KeyStoreSpi;
import java.security.Provider;

public class KeyChainKeystore extends KeyStore {

  public KeyChainKeystore(KeyStoreSpi keyStoreSpi, Provider provider, String type) {
    super(keyStoreSpi, provider, type);
    try {
      load(null, null);
    } catch (Exception e) {
      // ignore - our spi doesn't do anything
    }
  }

}

谢谢

瓦西尔

【问题讨论】:

  • 您能解释一下接下来如何使用这两个自定义类吗?我正在努力解决同样的问题......
  • 我想通了。这是为寻找最后一块的人准备的: KeyChainProxy keyChainProxy = new KeyChainProxy("your-alias", yourKey, yourChain); KeyChainKeystore keyChainKeystore = new KeyChainKeystore(keyChainProxy, null, "type-of-keystore-you-want");
  • 就是这样。您可以从钥匙串中制作一个 SPI 并使用它来初始化密钥库。然后,您可以在 ssl 调用中将此密钥库用作客户端证书。

标签: android ssl keystore


【解决方案1】:

KeyChain 旨在保护您的私钥。

因此,您可以获得对私钥的引用,并在您的应用程序中使用该私钥进行身份验证、加密……但您永远无法访问实际的私钥数据。

因此您无法导出私钥 - 这是您正在尝试做的事情。

【讨论】:

  • 你说的似乎是对的,但是......那么为什么 Apache 客户端需要一个显式的 KeyStore 对象才能进行客户端证书身份验证? Android 的受信任 CA 可通过 KeyStore.getInstance("AndroidCAStore") 作为 KeyStore 对象使用,但私钥不可用。
  • 界面似乎不能很好地协同工作。但是没有任何地方声明它必须是 BKS KeyStore 实例。您可以尝试实现您自己的 KeyStoreSpi 实例,该实例仅包含您从 KeyChain 获得的 PrivateKey。如果仅使用密钥,它可能会起作用。
  • 将尝试这个并用结果更新问题 - 感谢您的帮助。
  • 已确认 - 按照建议工作 - 我将使用两个类的示例实现来更新答案 - KeyStoreSPI 和 KeyStore
猜你喜欢
  • 2014-08-23
  • 2012-01-10
  • 2014-05-01
  • 1970-01-01
  • 2022-06-23
  • 2015-10-29
  • 2018-07-05
  • 2019-01-14
  • 1970-01-01
相关资源
最近更新 更多