Я Android-разработчик, который должен использовать KeyChain
, а не KeyStore
. Вариант KeyStore нашего кода работает. Мне нужно добавить эквивалент KeyChain
.
это работает
final char[] PASSWORD = "***SOMEPASSWORD****".toCharArray();
TrustManager[] trustManager;
SSLSocketFactory sslSocketFactory;
KeyStore keyStore;
InputStream inputStream = context.getResources().getAssets().open("xxxx-xxxxx-xxxxx-xxxx.pfx");
keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(inputStream,PASSWORD);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance (TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager))
{
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
trustManager = trustManagers;
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("X509");
keyManagerFactory.init(keyStore,PASSWORD);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(),null,null);
sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(15000, TimeUnit.MILLISECONDS).readTimeout(0, TimeUnit.MILLISECONDS)
.writeTimeout(15000, TimeUnit.MILLISECONDS).cookieJar(new ReactCookieJarContainer());
builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManager[0]);
OkHttpClient okHttpClient = builder.build();
Проблема в этой строке InputStream inputStream = context.getResources().getAssets().open("xxxx-xxxxx-xxxxx-xxxx.pfx");
нам не разрешено использовать папку с ресурсами (по причинам, выходящим за рамки этого разговора), но нам разрешено помещать тот же самый файл в KeyChain
, что я и сделал, и я могу получить его с помощью следующий. X509Certificate[] chain = KeyChain.getCertificateChain(context, "xxxx-xxxxx-xxxxx-xxxx");
так с тех пор
X509Certificate[] chain = KeyChain.getCertificateChain(context, "xxxx-xxxxx-xxxxx-xxxx"); //this gets the correct X509Certificate
Получает сертификат через KeyChain
, я инстинктивно заменил его на это:
X509TrustManager customTm = new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
try {
return X509Certificate[] chain = KeyChain.getCertificateChain(context, "xxxx-xxxxx-xxxxx-xxxx");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeyChainException e) {
e.printStackTrace();
}
return null;
}
};
TrustManager[] trustManager = new TrustManager[] { customTm };
sslContext.init(null, trustManager, null);
но это не работает, поэтому мой вопрос: как мне использовать X509Certificate, который у меня есть от KeyChain
, в качестве замены активу, который я втянул в KeyStore
?
Я понял, код должен стать немного другим, но это не было задокументировано нигде, что я мог найти: мне пришлось буквально катить клавиатуру лицом, чтобы это сделать
так. в примере OP я использовал файл pfx в папке с ресурсами.
Чтобы не использовать файл ресурса в среде сборки (он же KeyStore
), чтобы загрузить сертификат напрямую из части ОС Android во время выполнения и получить его в коде (он же KeyChain
) (т.е. чтобы загрузите сертификат, который вы можете увидеть на экране ниже на устройстве)....
ЭТО ИЛЛЮСТРАЦИЯ того, где он находится в андроиде, НО НЕ МОЙ СНИМОК
...используйте этот код
final char[] PASSWORD = "***SOMEPASSWORD****".toCharArray();
TrustManager[] trustManager;
SSLSocketFactory sslSocketFactory;
KeyStore keyStore;
X509Certificate[] keychain;
PrivateKey privateKey;
keychain = KeyChain.getCertificateChain(context, "xkeyx-xaliasxx");
privateKey = KeyChain.getPrivateKey(context, "xkeyx-xaliasxx");
keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(null, null);
keyStore.setKeyEntry("xkeyx-xaliasxx", privateKey, PASSWORD, keychain);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance (TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager))
{
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
trustManager = trustManagers;
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("X509");
keyManagerFactory.init(keyStore,PASSWORD);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(),null,null);
sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(15000, TimeUnit.MILLISECONDS).readTimeout(0, TimeUnit.MILLISECONDS)
.writeTimeout(15000, TimeUnit.MILLISECONDS).cookieJar(new ReactCookieJarContainer());
builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManager[0]);
OkHttpClient okHttpClient = builder.build();
ПРИМЕЧАНИЕ. Это будет работать только ПОСЛЕ того, как пользователь в приложении подтвердит сертификат: поэтому этот код должен быть запущен хотя бы ОДИН РАЗ.
KeyChain.choosePrivateKeyAlias(RN.getReactActivity(), (KeyChainAliasCallback) alias -> {
Log.d("cert", String.format("User has selected client certificate alias: %s should be xkeyx-xaliasxx", alias));
X509Certificate[] truechain;
try {
truechain = KeyChain.getCertificateChain(Context, alias);
if (truechain != null) {
Log.d("cert", "truechain count" + truechain.length);
}
else{
Log.d("cert", "truechain count is null");
}
} catch (InterruptedException e) {
Log.e("cert", "InterruptedException", e);
} catch (KeyChainException e) {
Log.e("cert", "KeyChainException", e);
}
}, null, null, null, -1, null);