gpt4 book ai didi

java - Android java更新Android KeyStore中的证书和私钥

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:27:41 37 4
gpt4 key购买 nike

我有一个使用 HTTPS 客户端证书进行身份验证的系统,但证书本身是根据以下过程生成的:

  1. 客户端设备生成证书(包括公钥和私钥)
  2. 客户端设备将公钥发送给服务器,服务器对公钥进行签名,并将其作为签名证书返回
  3. 客户端以安全的方式存储证书,然后将其用作 HTTPS 客户端证书

我们有这个系统在 iOS 上运行,我正在尝试移植到 android,但遇到了很多 Android 文档不完善且安全 API 令人困惑的问题。

我的代码大致是这样的:

生成证书

keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
keyStore.load(null);

Date startDate = new Date();
Date endDate = new Date(startDate.getTime() + FORTY_YEARS_IN_MILLISECONDS);

KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
.setAlias(alias)
.setKeySize(2048)
.setKeyType(KeyProperties.KEY_ALGORITHM_RSA)
.setSubject(new X500Principal("CN=" + alias))
.setSerialNumber(BigInteger.TEN)
.setStartDate(startDate)
.setEndDate(endDate)
.build();

KeyPairGenerator generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEYSTORE);
generator.initialize(spec);
KeyPair keyPair = generator.generateKeyPair(); // this will put a certificate and key pair in the keyStore.
dumpKeyStore(keyStore);

byte[] entireKey = keyPair.getPublic().getEncoded();
// chop off first 24 bytes; the java key pair generator puts an object ID of 1.2.840.113549.1.1.1 RSA (RSA_SIGN) before the key which gets mangled when the server signs and sends back the certificate
byte[] publicKeyBytes = Arrays.copyOfRange(entireKey, 24, entireKey.length);

dumpKeyStore 是一种实用方法,它迭代 keystore ,调用 keyStore.getEntry 获取每个条目并记录内容。此时,它报告有一个具有给定别名的条目,并且类型为 KeyStore.PrivateKeyEntry。它有一个关联的证书和公钥,可以从 PrivateKeyEntry 中获取。

发送到服务器

publicKeyBytes 被发送到服务器,服务器将其作为新的签名 x509 证书的公钥,并在响应中发回。我没有输入代码,这只是基本的网络。据我所知,返回的证书已加载并且看起来不错。

保存并关联证书

我试图将它放入具有相同别名的 keyStore 中,因此它(理论上)可以与之前的正确私钥相关联。到目前为止,我的代码是这样的:

KeyStore keyStore;
try {
keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
keyStore.load(null);
}catch (IOException | NoSuchAlgorithmException | CertificateException e) {
Log.wtf(TAG, e);
throw new FatalError(TAG, e);
}

CertificateFactory certificateFactory;
try {
certificateFactory = CertificateFactory.getInstance("X.509");
} catch (CertificateException e) {
Log.wtf(TAG, e);
throw new FatalError(TAG, e);
}

Certificate cert = certificateFactory.generateCertificate(new ByteArrayInputStream(certificateFromServer));

// find the existing certificate, copy it's private key out, then replace the certificate with the one from the server but keeping the private key
try {
KeyStore.PrivateKeyEntry existingPrivateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);

KeyStore.PrivateKeyEntry newEntry = new KeyStore.PrivateKeyEntry(existingPrivateKeyEntry.getPrivateKey(), new Certificate[]{ cert });
keyStore.setEntry(alias, newEntry, null);
} catch (Exception e) {
Log.wtf(TAG, e);
throw new FatalError(TAG, e);
}
dumpKeyStore(keyStore);

此时,最终的 dumpKeyStore 指示存在具有正确别名的条目,但是当它尝试调用 keyStore.getEntry

我想做的事情(替换证书但保留私钥)在 Android 中可行吗?如果是这样,我该怎么做?这似乎并没有真正起作用

谢谢

猎户座

最佳答案

事实证明,我做错了事。您不需要替换或修改 KeyStore 中的证书,您只需要使用自定义 KeyManager当你初始化 SSLContextHttpsURLConnection 使用并且 KeyManager 可以选择您想要的任何证书或私钥。

这大大简化了 KeyStore 的管理。我现在的场景是

  1. 使用 KeyPairGenerator 生成公钥/私钥对别名 X
  2. 将公钥发送到服务器,服务器根据该公钥生成新的签名证书,并将其发回
  3. 使用 setCertificateEntry 将此签名证书放入 keystore 中别名 X-Signed

当我建立 HttpsURLConnection ,它是这样的:

KeyStore androidKeyStore = KeyStore.getInstance(LocalKeyStore.ANDROID_KEYSTORE);
androidKeyStore.load(null);

X509Certificate signedClientCertificate = (X509Certificate)androidKeyStore.getCertificate("X-Signed");
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)androidKeyStore.getEntry("X", null);

X509ExtendedKeyManager keyManager = new X509ExtendedKeyManager() {
@Override
public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
return clientCertificateAlias;
}
@Override
public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
return null; // different if you're validating the server's cert
}
@Override
public X509Certificate[] getCertificateChain(String alias) {
return new X509Certificate[] { signedClientCertificate };
}
@Override
public String[] getClientAliases(String keyType, Principal[] issuers) {
return new String[]{ "X" };
}

@Override
public String[] getServerAliases(String keyType, Principal[] issuers) {
return null; // different if you're validating server's cert
}
@Override
public PrivateKey getPrivateKey(String alias) {
if(alias != clientCertificateAlias) {
Log.e(TAG, String.format("X509ExtendedKeyManager is asking for privateKey with unknown alias %s. Expecting it to ask for %s", alias, clientCertificateAlias));
return null;
}
return privateKeyEntry.getPrivateKey();
}
};

X509TrustManager trustServerCertificates = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// do nothing, this method doesn't get called
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
// code to validate server's cert in here
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null; // any issuer
}
};

m_sslContext = SSLContext.getInstance("TLS");
m_sslContext.init(new KeyManager[]{ keyManager }, new TrustManager[] { trustServerCertificates }, null);

// later on

conn = (HttpURLConnection)url.openConnection();
SSLContext sslContext = m_sslContext;

if(conn instanceof HttpsURLConnection && sslContext != null) {
((HttpsURLConnection)conn).setSSLSocketFactory(sslContext.getSocketFactory());
}

这对我来说效果很好,我可以继续使用 AndroidKeyStore凭借其每个应用程序的隐私和硬件支持的存储

关于java - Android java更新Android KeyStore中的证书和私钥,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33771095/

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