Android не доверяет сертификату прокси
Приложения под Android 7+ игнорируют пользовательские сертификаты. Как разрешить их отладочной сборке через network_security_config и чего это не починит.
Что это значит
Сертификат установлен, в настройках Android он виден, Chrome на телефоне
работает, а приложение всё равно падает с SSLHandshakeException: Trust anchor for certification path not found. Trust anchor называют корневой сертификат, с
которого начинается проверка пути (RFC 5280, пункт 6.1.1),
и подходящего у приложения здесь нет: собранное под
Android 7.0 (API 24) и выше, оно доверяет только системным центрам сертификации
и игнорирует всё, что добавил пользователь. Так и задумано: это закрывает чтение трафика
приложения для любого, у кого есть доступ к телефону.
Как исправить
Приложение должно само разрешить пользовательские сертификаты, причём только в отладочной сборке.
Создайте app/src/main/res/xml/network_security_config.xml:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<debug-overrides>
<trust-anchors>
<certificates src="user" />
<certificates src="system" />
</trust-anchors>
</debug-overrides>
</network-security-config>
Сошлитесь на него из манифеста:
<application
android:networkSecurityConfig="@xml/network_security_config"
... >
Весь фокус держится на <debug-overrides>. Блок работает только тогда, когда
приложение собрано с android:debuggable="true". Gradle ставит этот флаг в
debug-сборках и никогда не ставит в релизных, поэтому выкладывать такой файл
безопасно.
Не заменяйте его обычным <base-config> с src="user". Он тоже сработает, но
сработает и в релизе, а это ровно та дыра, которую Android закрыл.
Если нужен один хост, а не все:
<domain-config>
<domain includeSubdomains="true">api.example.com</domain>
<trust-anchors>
<certificates src="user" />
</trust-anchors>
</domain-config>
Этот блок нельзя положить внутрь <debug-overrides>: там допустим только
<trust-anchors>. Значит, <domain-config> действует и в релизной сборке.
Чтобы его там не было, положите отдельную копию файла в
app/src/debug/res/xml/, и тогда Gradle возьмёт её только для debug-варианта.
Ещё два момента, на которых спотыкаются:
- Порог задаёт
targetSdkVersionприложения, а не версия Android на телефоне. - Конфиг компилируется внутрь APK. После правки приложение надо пересобрать и переустановить, потому что перезапуск ничего не меняет.
Чего это не починит
Пиннинг сертификата. Приложение может проверять отпечаток само:
CertificatePinner в OkHttp, <pin-set> в том же конфиге, собственный
TrustManager. Тогда ничего из написанного выше не поможет, потому что
приложение отвергнет сертификат, которому система уже доверяет. Пиннинг снимается в коде (убрать
CertificatePinner в debug-варианте) или не снимается вообще.
Flutter. У Dart свой TLS-стек. Он игнорирует network_security_config и не
смотрит на пользовательские сертификаты. Нужен SecurityContext с вашим
корневым сертификатом или badCertificateCallback в отладочной сборке.
Чужое приложение. Исходников нет, значит конфиг добавить некуда. Остаются
два пути: установить сертификат как системный (нужен root и модуль Magisk или
KernelSU вроде cert-fixer или AlwaysTrustUserCerts) либо перепаковать APK
через apk-mitm.
Android 14+. Системное хранилище сертификатов переехало из
/system/etc/security/cacerts в APEX-модуль com.android.conscrypt, по пути
/apex/com.android.conscrypt/cacerts, и доступно теперь только для чтения. Старые
способы «подложить файл в /system» там не работают: нужен модуль, который
умеет про путь в APEX. Пользовательские сертификаты никуда не переезжали.
Как увидеть это у себя
Когда приложение начнёт доверять сертификату, его запросы будут видны целиком:
хост, путь, заголовки, тела запроса и ответа. До этого Solpuga показывает только
соединение, которое открылось и сразу оборвалось. Это и значит, что
network_security_config ещё не подхватился. Прокси на телефоне настраивается
отдельно. Полная инструкция по настройке:
Мобильные устройства.
Рядом
- iOS: профиль установлен, но сертификату нет доверияНа iOS корневой сертификат ставится в два шага, а не в один. Второй спрятан в «Об этом устройстве», и без него не работает ничего.
- Как посмотреть трафик с iPhone или Android — пошаговоТелефон и компьютер в одной сети Wi-Fi, прокси в настройках сети, корневой сертификат с полным доверием. Четыре шага и шесть причин, по которым они не срабатывают.