Закрепление открытого ключа с помощью X509TrustManagerExtensions checkServerTrusted

Закрепление открытого ключа для соединения HTTPS TLS.

Существует проблема с Android API, ниже 17, которая позволяет MITM (Человек в середине) атаки в случае закрепления открытого ключа. Это было объяснено в ссылке ниже.

https://www.cigital.com/blog/ineffective-certificate-pinning-implementations/

Таким образом, в Android минимум sdk ниже 17, то есть ниже Android версии 4.2, мы должны инициализировать X509TrustManager с Android Keystore, в котором есть только корневые сертификаты сервера (вместо хранилища ключей по умолчанию; в нем будут установлены все сертификаты на устройстве). Это помогает очистить листовые сертификаты, полученные от сервера, перед выполнением закрепления открытого ключа.

Начиная с Android API 17 и выше, Android представила X509TrustManagerExtensions, которая выполняет эту корневую очистку на уровне ОС.

https://developer.android.com/reference/android/net/http/X509TrustManagerExtensions.html

Мой вопрос:

Я был бы рад, если бы кто-нибудь мог предоставить пример того, как реализовать следующий метод, предоставляемый X509TrustManagerExtensions для очистки корня.

List<X509Certificate> checkServerTrusted (X509Certificate[] chain, 
                String authType, 
                String host)

Меня смущает следующее.

  1. host; это должен быть URL домена? с https или без? или это должен быть полный URL (домен + относительный путь)

  2. Как создать мгновение X509TrustManagerExtensions? Конструктор для X509TrustManagerExtensions принимает X509TrustManager в качестве ввода. Создаем ли мы этот X509TrustManager с хранилищем ключей по умолчанию для Android?

Фрагмент кода (не работает):

   TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
   tmf.init(KeyStore.getInstance(KeyStore.getDefaultType()));

   for (TrustManager trustManager : tmf.getTrustManagers()) {
       X509TrustManagerExtensions tme = new X509TrustManagerExtensions((X509TrustManager) trustManager);
       tme.checkServerTrusted(chain, authType, <<String https://www.example.com>>);
   }

Исключение: не найдена доверенная привязка для пути сертификации

Возможный риск безопасности: использование KeyStore.getDefaultType()

Любая помощь будет принята с благодарностью.

1 ответ

Во-первых, вам нужно завладеть доверительным менеджером, используя TrustManagerFactory, При инициализации этого вы передаете нуль для того, чтобы использовать по умолчанию Keystore и он вернет доверенные менеджеры по умолчанию. С этим вы можете создать X509TrustManagerExtensions используя первый X509TrustManager,

TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
        TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);

// Find first X509TrustManager in the TrustManagerFactory
X509TrustManager x509TrustManager = null;
for (TrustManager trustManager : trustManagerFactory.getTrustManagers()) {
    if (trustManager instanceof X509TrustManager) {
        x509TrustManager = (X509TrustManager) trustManager;
        break;
    }
}

X509TrustManagerExtensions x509TrustManagerExtensions = 
        new X509TrustManagerExtensions(trustManager());

Затем для выполнения этого хоста я успешно использовал только часть домена:

List<X509Certificate> trustedCerts = x509TrustManagerExtensions
        .checkServerTrusted(untrustedCerts, "RSA", "appmattus.com");

Для тех, кто использует HttpUrlConnection Ненадежные сертификаты определяются с помощью:

Certificate[] serverCerts = ((HttpsUrlConnection)conn).getServerCertificates();
X509Certificate[] untrustedCerts = Arrays.copyOf(serverCerts, 
        serverCerts.length, 
        X509Certificate[].class);

Если вы используете OkHttp, вы можете просто использовать встроенный CertificatePinner, который с тех пор был обновлен, чтобы исправить проблемы, упомянутые в этой статье.

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