У меня есть 2 устройства в дикой природе, которые не могут подключиться к моей конечной точке TLS v1.2. Все остальные, в том числе браузеры, PostMan и устройства iOS, похоже, могут.
Устройства работают под управлением Android 5 и 7 (так что должен не будет проблемой с поддержкой TLS v1.2).
Примечание. Это не самозаверяющий сертификат. Подписано Amazon.
Непосредственные мысли были:
Фрагментация Android - возможно, устройства (одно из них - Kindle Fire 7) не включают правильные сертификаты в ОС. Не было бы быть первым, когда производитель устройства принял странное решение это нарушает функциональность.
Доступ к API осуществляется через прокси, и на самом деле является - Man-In-The-Middle, который правильно определяется.
Исправление (1) означает объединение нашего сертификата и приводит к обычным проблемам, когда срок действия нашего сертификата истекает.
Я бы предпочел, чтобы пользователь установил отладочную сборку, которая подтверждает, является ли проблема (1) или (2). Такая сборка будет проверять сертификат SSL, предоставленный сервером / прокси, и записывать его обратно мне.
Веб-фреймворки:
Как мне проверить информацию SSL-сертификата, которую видит устройство при подключении к моей конечной точке?
Оказывается, возникает 2 разных исключения.
Планшет Kindle Fire 7 "(KFAUWI, OS 5.1.1) отбрасывает тот, который я начал исследовать, и на котором этот вопрос должен быть сосредоточен, то есть базовый сбой SSL.
java.security.cert.CertPathValidatorException:
Trust anchor for certification path not found.
at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:331)
at com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:232)
at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:114)
Устройство LG (LG-SP200, OS 7.1.2) закрывает соединение одноранговым узлом, который следует решить в рамках нового вопроса, если он не решен здесь:
javax.net.ssl.SSLHandshakeException:
Connection closed by peer
at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(NativeCrypto.java)
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:360)
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:299)
@Christopher отметил и уже проверил на моей стороне. OkHttp
или Retrofit
фактически позаботились об этом на старых устройствах. Сейчас выходят из строя только устройства Android 5 и 7.
Так что же ты можешь зацепить? Ответ OkHttp? В этом случае я думаю, что информацию можно получить на response.handshake().peerCertificates()
.
Я понятия не имею. У вас может быть приложение для отладки, которое просто доверяет всем сертификатам и проверяет, что вы получаете.
Вперед, продолжать. Поскольку у вас есть тестовый код, вы сможете составить лучший ответ, чем я.
@RichardLeMesurier Вы добавили закрепление сертификатов для okhttp?
@Sangeet Нет, я еще не видел. Это конечная точка, которую я использую рассчитывать на возможность работать (поскольку она работает почти на всех устройствах, iOS, Android, ПК). Я пытаюсь сфокусировать этот вопрос на как отлаживать то, что видит устройство - чтобы определить, потребуется ли закрепление для этих проблемных устройств или какой будет лучший маршрут вперед.
@RichardLeMesurier, вы получаете исключение квитирования SSL или тайм-аут?
@RichardLeMesurier response?.raw()?.handshake()
Думаю, это вам поможет
@RichardLeMesurier Вы можете сделать одно: если устройство не доверяет вашему сертификату, вы можете добавить закрепление сертификата и проверить, работает ли он нормально или нет. Потому что, если вы добавите привязку сертификата, он будет проверять только этот сертификат.
Робби Корнелиссен предоставил основной ответ в комментарии, ссылающийся на OkHttp Response
:
the information should be available from
response.handshake().peerCertificates()
.
Был реализован простой Interceptor
для проверки сертификатов при действительном рукопожатии:
private static class SslCertificateLogger implements Interceptor {
public static final String TAG = "SSL";
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response;
try {
response = chain.proceed(request);
} catch (Exception e) {
Log.d(TAG, "<-- HTTP FAILED: " + e);
throw e;
}
Handshake handshake = response.handshake();
if (handshake == null) {
Log.d(TAG, "no handshake");
return response;
}
Log.d(TAG, "handshake success");
List<Certificate> certificates = handshake.peerCertificates();
if (certificates == null) {
Log.d(TAG, "no peer certificates");
return response;
}
String s;
for (Certificate certificate : certificates) {
s = certificate.toString();
Log.d(TAG, s);
}
return response;
}
}
Это добавляется к OkHttpClient
как обычно:
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.addInterceptor(new SslCertificateLogger())
.build();
Аналогичное решение был предложен Сангит Суреш, который ссылается на объект Retrofit Response
:
response?.raw()?.handshake()
I think this will help you
Здесь важна информация о том, что Retrofit таким образом предоставляет доступ к необработанному ответу OkHttp.
Это будет использоваться не в Interceptor
, а на более высоком уровне, в фактическом коде обработки модернизации, после получения Response<>
модернизации из API.
Преобразование его решения Kotlin обратно в Java могло дать что-то вроде этого:
okhttp3.Response raw = httpResponse.raw();
if (raw != null) {
Handshake handshake = raw.handshake();
if (handshake != null) {
List<Certificate> certificates = handshake.peerCertificates();
if (certificates != null) {
for (Certificate certificate : certificates) {
Log.d(TAG, certificate.toString());
}
}
}
}
Оба решения работают нормально, при условии, что handshake()
не равен нулю, т.е. когда рукопожатие прошло успешно.
Учитывая, что это расследование неудачных рукопожатий, потребовался следующий шаг, чтобы «доверять всем сертификатам» (только для отладочных сборок!).
Это неоднократно документировалось - вот одна из таких версий:
Этот объект рукопожатия спас мне сегодня жизнь! Я искал перехватчик, чтобы проверить сертификаты с сервера и вау, теперь я спасен! Большое спасибо!
TLS 1.2 поддерживается только Android 4.1 и выше. Кроме того, он отключен по умолчанию на устройствах с более ранней версией 5.0.