Retrofit2 и ошибка SSLHandshakeException SSLProtocolException

103
04 апреля 2021, 14:20

Нужно решить проблему с эксепшн.

HTTP FAILED: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: 
ssl=0xb80e0c40: Failure in SSL library, usually a protocol error

Api 19 андроид 4.4

Сам код

 HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
                .allEnabledCipherSuites()
                .build();
        OkHttpClient client = new OkHttpClient.Builder()
                .connectionSpecs(Collections.singletonList(spec))
                .addInterceptor(interceptor)
                .build();
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.unsplash.com/")
                .client(client)
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        unsplash = retrofit.create(Unsplash.class);
        unsplash.getPic(howMany, key).enqueue(new Callback<GiveMePhoto>() {
            @Override
            public void onResponse(Call<GiveMePhoto> call, Response<GiveMePhoto> response) {
                Log.e("log", "onResponse: body " );
            }
            @Override
            public void onFailure(Call<GiveMePhoto> call, Throwable t) {
                Log.e("log", "onFailure: ");
            }
        });

Вот логи


D/OkHttp: --> GET https://api.unsplash.com/photos/random?count=1&client_id=семь35a6f98aa0896a1ab0b0cdc65d5a8132477661e18a2236a238e08f526f7323e
D/OkHttp: --> END GET
D/OkHttp: <-- HTTP FAILED: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0xb80e0c40: Failure in SSL library, usually a protocol error
D/OkHttp: error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version (external/openssl/ssl/s23_clnt.c:741 0x8d9dd990:0x00000000)

Я новичок в этом деле, скорее всего обращаю внимание не туда, перепробовал кучу методов, возможно они были рабочие, просто применял не так. Просьба помочь в решение вопроса. T_T

Answer 1

Самый верный способ решения проблемы - прекращение поддержки ОС меньше 5 версии.

Если всё же хочется сохранить поддержку старой и забагованной версии 4.х, то самый простой способ - с помощью гугла обновить сертификаты. Работать это будет, правда, только для девайсов, на которых гугловые сервисы есть.

Делается так:

/**
 * Это должно фиксить SSLException
 *
 * @see {https://stackoverflow.com/a/42471738/3212712}
 */
private void updateAndroidSecurityProvider() {
    try {
        ProviderInstaller.installIfNeeded(this);
    } catch (GooglePlayServicesRepairableException | GooglePlayServicesNotAvailableException e) {
        // Thrown when Google Play Services is not installed, up-to-date, or enabled
        // Show dialog to allow users to install, update, or otherwise enable Google Play services.
        System.out.println("Google Play Services not available.");
    }
}

Вызвать этот метод надо при старте приложения. В Application#onCreate() или Activity#onCreate()

Есть ещё вот такой способ:

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
//https://gist.github.com/gokhangirgin/ac8fdb8843a70a420982
public class TLSSocketFactory extends SSLSocketFactory {
    private SSLSocketFactory socketFactory;
    public TLSSocketFactory() {
        super();
        try {
            final SSLContext sContext = SSLContext.getInstance("TLSv1.2");
            sContext.init(null, null, null);
            socketFactory = sContext.getSocketFactory();
        } catch (final NoSuchAlgorithmException | KeyManagementException e) {
            System.out.println(e);;
        }
    }
    @Override
    public String[] getDefaultCipherSuites() {
        return socketFactory.getDefaultCipherSuites();
    }
    @Override
    public String[] getSupportedCipherSuites() {
        return socketFactory.getSupportedCipherSuites();
    }
    @Override
    public Socket createSocket(final Socket s, final String host, final int port, final boolean autoClose)
            throws IOException {
        final SSLSocket ss = (SSLSocket) socketFactory.createSocket(s, host, port, autoClose);
        ss.setEnabledProtocols(ss.getSupportedProtocols());
        ss.setEnabledCipherSuites(ss.getSupportedCipherSuites());
        return ss;
    }
    @Override
    public Socket createSocket(final String host, final int port) throws IOException {
        final SSLSocket ss = (SSLSocket) socketFactory.createSocket(host, port);
        ss.setEnabledProtocols(ss.getSupportedProtocols());
        ss.setEnabledCipherSuites(ss.getSupportedCipherSuites());
        return ss;
    }
    @Override
    public Socket createSocket(final String host, final int port, final InetAddress localHost, final int localPort)
            throws IOException {
        final SSLSocket ss = (SSLSocket) socketFactory.createSocket(host, port, localHost, localPort);
        ss.setEnabledProtocols(ss.getSupportedProtocols());
        ss.setEnabledCipherSuites(ss.getSupportedCipherSuites());
        return ss;
    }
    @Override
    public Socket createSocket(final InetAddress host, final int port) throws IOException {
        final SSLSocket ss = (SSLSocket) socketFactory.createSocket(host, port);
        ss.setEnabledProtocols(ss.getSupportedProtocols());
        ss.setEnabledCipherSuites(ss.getSupportedCipherSuites());
        return ss;
    }
    @Override
    public Socket createSocket(final InetAddress address, final int port, final InetAddress localAddress,
            final int localPort) throws IOException {
        final SSLSocket ss = (SSLSocket) socketFactory
                .createSocket(address, port, localAddress, localPort);
        ss.setEnabledProtocols(ss.getSupportedProtocols());
        ss.setEnabledCipherSuites(ss.getSupportedCipherSuites());
        return ss;
    }
}

После чего применить класс выше в Application#onCreate()

//enable tls v1.2 for android < 21
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
    HttpsURLConnection.setDefaultSSLSocketFactory(new TLSSocketFactory());
}
READ ALSO
Java Maps of Lamda

Java Maps of Lamda

Требуется перевести оператор switch в лямбда-выраженияДля этого нужно сделать Map(у) с возможными командами, и в зависимости от символа сделать...

175
Как запустить чужой проект в Intellij Idea

Как запустить чужой проект в Intellij Idea

Все классы подсветились оранжевым и написано что нет SDKSDK подключил 1

299
Запись данных из .json файла в JTable

Запись данных из .json файла в JTable

У меня естьjson файл типа:

111
Поиск в базе данных Spring MVC и JPA

Поиск в базе данных Spring MVC и JPA

Только осваиваю Spring и, вот такая задача - нужно осуществить поиск по разным параметрамПричем параметры все не обязательные, т

124