Использование X509Certificate из KeyChain в OKHttpClient sslSocketFactory

Я 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?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
80
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я понял, код должен стать немного другим, но это не было задокументировано нигде, что я мог найти: мне пришлось буквально катить клавиатуру лицом, чтобы это сделать

так. в примере 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);

Другие вопросы по теме