Отключить SSL как протокол в HttpsURLConnection

Из-заПУДЕЛЬ Уязвимость мой сервер, размещенный в Amazon AWS, больше не поддерживает SSLv3.

В результате первое подключение HTTPS, которое мое приложение Android выполняет к серверу, приводит к ошибке при установлении соединения.

Error reading server response: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x77d8ab68: Failure in SSL library, usually a protocol error
error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:741 0x7339ad74:0x00000000)
       [....]
Caused by: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x77d8ab68: Failure in SSL library, usually a protocol error
error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:741 0x7339ad74:0x00000000)
       at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:448)
       at com.android.okhttp.Connection.upgradeToTls(Connection.java:146)
       at com.android.okhttp.Connection.connect(Connection.java:107)
       at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:294)
       at com.android.okhttp.internal.http.HttpEngine.sendSocketRequest(HttpEngine.java:255)
       at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:206)

Ошибка происходит только в первом запросе. Последующие запросы работают в течение некоторого времени.

Чтобы это исправить, я пытаюсь удалить SSL из списка протоколов, принятых клиентом Android, и убедиться, что я работаю только с TLS. Для этого я установил пользовательский SSLSocketFactory, который удаляет SSL из списка включенных протоколов и поддерживаемых наборов шифров.

/**
 * SSLSocketFactory that wraps one existing SSLSocketFactory and delegetes into it adding
 * a new cipher suite
 */
public class TLSOnlySocketFactory extends SSLSocketFactory {

    private final SSLSocketFactory delegate;

    public TLSOnlySocketFactory(SSLSocketFactory delegate) {
        this.delegate = delegate;
    }

    @Override
    public String[] getDefaultCipherSuites() {

        return getPreferredDefaultCipherSuites(this.delegate);
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return getPreferredSupportedCipherSuites(this.delegate);
    }



    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        final Socket socket = this.delegate.createSocket(s, host, port, autoClose);

        ((SSLSocket)socket).setEnabledCipherSuites(getPreferredDefaultCipherSuites(delegate));
        ((SSLSocket)socket).setEnabledProtocols(getEnabledProtocols((SSLSocket)socket));

        return socket;
    }



   [.....]

        ((SSLSocket)socket).setEnabledCipherSuites(getPreferredDefaultCipherSuites(delegate));
        ((SSLSocket) socket).setEnabledProtocols(getEnabledProtocols((SSLSocket)socket));

        return socket;
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        final Socket socket = this.delegate.createSocket(host, port);

        ((SSLSocket)socket).setEnabledCipherSuites(getPreferredDefaultCipherSuites(delegate));
        ((SSLSocket) socket).setEnabledProtocols(getEnabledProtocols((SSLSocket)socket));

        return socket;
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        final Socket socket = this.delegate.createSocket(address, port, localAddress, localPort);

        ((SSLSocket)socket).setEnabledCipherSuites(getPreferredDefaultCipherSuites(delegate));
        ((SSLSocket) socket).setEnabledProtocols(getEnabledProtocols((SSLSocket)socket));

        return socket;
    }

    private String[] getPreferredDefaultCipherSuites(SSLSocketFactory sslSocketFactory) {
        return getCipherSuites(sslSocketFactory.getDefaultCipherSuites());
    }

    private String[] getPreferredSupportedCipherSuites(SSLSocketFactory sslSocketFactory) {
        return getCipherSuites(sslSocketFactory.getSupportedCipherSuites());
    }

    private String[] getCipherSuites(String[] cipherSuites) {
        final ArrayList<String> suitesList = new ArrayList<String>(Arrays.asList(cipherSuites));
        final Iterator<String> iterator = suitesList.iterator();
        while (iterator.hasNext()) {
            final String cipherSuite = iterator.next();
            if (cipherSuite.contains("SSL")) {
                iterator.remove();
            }
        }
        return suitesList.toArray(new String[suitesList.size()]);
    }

    private String[] getEnabledProtocols(SSLSocket socket) {
        final ArrayList<String> protocolList = new ArrayList<String>(Arrays.asList(socket.getSupportedProtocols()));
        final Iterator<String> iterator = protocolList.iterator();
        while (iterator.hasNext()) {
            final String protocl = iterator.next();
            if (protocl.contains("SSL")) {
                iterator.remove();
            }
        }
        return protocolList.toArray(new String[protocolList.size()]);
     }

}

Как видите, мои SSLSocketFactory делегируются в другой SSLSocketFactory, и он просто удаляет SSL из списка включенных протоколов.

Я устанавливаю этот завод как

final TLSOnlySocketFactory tlsOnlySocketFactory = new TLSOnlySocketFactory(HttpsURLConnection.getDefaultSSLSocketFactory());
HttpsURLConnection.setDefaultSSLSocketFactory(tlsOnlySocketFactory);

Это НЕ решает проблему. Время от времени я все еще вижу ошибку, когда соединение было установлено. Как ни странно, это не исправляет это, но это явно минимизирует возникновение проблемы.

Как я могу заставить HttpsUrlConnection в моем клиенте Android использовать только TLS?

Спасибо.

Ответы на вопрос(6)

Ваш ответ на вопрос