OkHttp. Решаем ошибку «Trust anchor for certification path not found» — Компьюпико.ком

OkHttp. Решаем ошибку «Trust anchor for certification path not found»

После обновления SSL сертификата сервера, перестала работать интеграция между web-сервером и мобильным Android-приложением. При дебаге обнаружили ошибку Trust anchor for certification path not found. В статье расскажем как решить проблему. На входе имеем Android приложение на движке LibGDX, Android Studio для разработки, библиотеку OkHttp для обменов с сервером, web-сервер.

Проблема\Предисловие

Проблема возникла в приложении Фрупико Мы используем обмен с сервером в этой игре для сохранения резульатов и демонстрации таблицы рейтинга. Для организации обменов с сервером используем OkHttp. Когда выкатывали первые релизы всё работало. Что же пошло не так? Ранее SSL сертификаты для web-сервера мы генерировали через сервис Let’s Encrypt, которые необходимо было обновлять каждые три месяца. По двум причинам было принято решение перейти на платные сертификаты Global Sign:

  1. Обновляются раз в год;
  2. Платный сертификат, вероятно, должен вызывать больше доверия у клиентов, чем бесплатный 🙂

Спустя какое-то время после установки сертификата Global Sign обнаружили, что приложение Фрупико перестало возвращать ответы от сервера (пустые таблицы рейтинга и всплывающие окна).

При дебаге обнаружили ошибку Trust anchor for certification path not found

Ошибка связана с тем, что OkHttp по какой-то причине не доверяет сертификатам Global Sign установленным на сервере.

Решение

1. Скачать AlphaSSL Intermediate Certificates

Зайти на сайт Global Sign и скачать корневые сертификаты. Вы должны получить два файла:

alphasslcasha256g4.crt

gsgccr6alphasslca2023.crt

2. Скопировать сертификаты в папку Assets

3. Добавить метод createSecureHttpClient():

private OkHttpClient createSecureHttpClient() throws Exception {
        // Используем getAssets() вместо Gdx.files.internal()
        InputStream certStream1 = getAssets().open("alphasslcasha256g4.crt");
        InputStream certStream2 = getAssets().open("gsgccr6alphasslca2023.crt");

        // Создаем фабрику сертификатов
        CertificateFactory cf = CertificateFactory.getInstance("X.509");

        // Создаем KeyStore и добавляем сертификаты
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(null, null);

        keyStore.setCertificateEntry("alphassl-g4", cf.generateCertificate(certStream1));
        keyStore.setCertificateEntry("alphassl-2023", cf.generateCertificate(certStream2));

        certStream1.close();
        certStream2.close();

        // Создаем TrustManagerFactory
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(keyStore);

        // Настраиваем SSLContext
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, tmf.getTrustManagers(), new java.security.SecureRandom());

        // Создаем OkHttpClient с кастомным SSL
        return new OkHttpClient.Builder()
            .sslSocketFactory(sslContext.getSocketFactory(), (javax.net.ssl.X509TrustManager) tmf.getTrustManagers()[0])
            .build();
    }

Немного о том, что делает этот код:

  1. Загружает сертификаты из assets
    • getAssets().open(«alphasslcasha256g4.crt»)
    • getAssets().open(«gsgccr6alphasslca2023.crt»)
  2. Создает KeyStore и добавляет туда сертификаты
    • KeyStore — это хранилище сертификатов. Добавляем в него alphassl-g4 и alphassl-2023.
  3. Создает TrustManagerFactory
    • Доверяет только сертификатам из KeyStore.
  4. Настраивает SSLContext с кастомными сертификатами
    • SSLContext нужен для безопасного соединения. init(null, tmf.getTrustManagers(), new SecureRandom()) подключает TrustManagerFactory.
  5. Создает OkHttpClient с этим SSLContext
    • sslSocketFactory(sslContext.getSocketFactory(), trustManager)

Теперь клиент доверяет нашим сертификатам и может подключаться к серверу по HTTPS. Осталось только переписать метод создания объекта client:

 try {
            client = createSecureHttpClient(); // Создаем защищенный клиент OkHttp
        } catch (Exception e) {
            e.printStackTrace();
            client = new OkHttpClient(); // Если ошибка — используем стандартный клиент
        }

На этом всё. Спасибо, что читаете Компьюпико.ком! Оставляйте комментарии, смотрите наш канал на YouTube, играйте в наши игры, используйте наши приложения. Удачи в разработке!

Оставить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *