Javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: przerwano uzgadnianie SSL: błąd w bibliotece SSL, zwykle błąd protokołu


103

Próbuję uruchomić następujący kod w systemie Android

URLConnection l_connection = null;
        // Create connection
        uzip=new UnZipData(mContext);
        l_url = new URL(serverurl);

        if ("https".equals(l_url.getProtocol())) {
            System.out.println("<<<<<<<<<<<<< Before TLS >>>>>>>>>>>>");
            sslcontext = SSLContext.getInstance("TLS");
            System.out.println("<<<<<<<<<<<<< After TLS >>>>>>>>>>>>");
            sslcontext.init(null,
                    new TrustManager[] { new CustomTrustManager()},
                    new java.security.SecureRandom());
            HttpsURLConnection
                    .setDefaultHostnameVerifier(new CustomHostnameVerifier());
            HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext
                    .getSocketFactory());

            l_connection = (HttpsURLConnection) l_url.openConnection();
            ((HttpsURLConnection) l_connection).setRequestMethod("POST");
        } else {
            l_connection = (HttpURLConnection) l_url.openConnection();
            ((HttpURLConnection) l_connection).setRequestMethod("POST");
        }
        /*System.setProperty("http.agent", "Android_Phone");*/


        l_connection.setConnectTimeout(10000);
        l_connection.setRequestProperty("Content-Language", "en-US");
        l_connection.setUseCaches(false);
        l_connection.setDoInput(true);
        l_connection.setDoOutput(true);
        System.out.println("<<<<<<<<<<<<< Before Connection >>>>>>>>>>>>");
        l_connection.connect();

On l_connection.connect(), podaje ten wyjątek SSLhandshakeException. Czasami to działa, ale w większości przypadków daje wyjątek. Dzieje się to tylko na emulatorze Androida 4.0. Przetestowałem to na Androidzie 4.4 i 5.0, działa dobrze. Co może być przyczyną tego ? Proszę pomóż

ŚLAD STOSU

    04-28 15:51:13.143: W/System.err(2915): javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x870c918: Failure in SSL library, usually a protocol error
04-28 15:51:13.143: W/System.err(2915): error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:658 0xb7c393a1:0x00000000)
04-28 15:51:13.143: W/System.err(2915):     at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:460)
04-28 15:51:13.143: W/System.err(2915):     at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:257)
04-28 15:51:13.143: W/System.err(2915):     at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:210)
04-28 15:51:13.143: W/System.err(2915):     at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:477)
04-28 15:51:13.153: W/System.err(2915):     at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:441)
04-28 15:51:13.153: W/System.err(2915):     at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:282)
04-28 15:51:13.153: W/System.err(2915):     at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:232)
04-28 15:51:13.153: W/System.err(2915):     at libcore.net.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:80)
04-28 15:51:13.153: W/System.err(2915):     at libcore.net.http.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:164)
04-28 15:51:13.153: W/System.err(2915):     at com.ofss.fcdb.mobile.android.rms.helpers.NetworkConnector.getConnection(NetworkConnector.java:170)
04-28 15:51:13.153: W/System.err(2915):     at com.ofss.fcdb.mobile.android.rms.util.InitiateRMS$2.run(InitiateRMS.java:221)
04-28 15:51:13.153: W/System.err(2915):     at java.lang.Thread.run(Thread.java:856)
04-28 15:51:13.153: W/System.err(2915): Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x870c918: Failure in SSL library, usually a protocol error
04-28 15:51:13.153: W/System.err(2915): error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:658 0xb7c393a1:0x00000000)
04-28 15:51:13.153: W/System.err(2915):     at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method)
04-28 15:51:13.153: W/System.err(2915):     at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:410)
04-28 15:51:13.153: W/System.err(2915):     ... 11 more
04-28 16:42:44.139: W/ResourceType(3140): No package identifier when getting value for resource number 0x00000000

sprawdziłeś swoją aplikację na prawdziwym urządzeniu?
Paresh Mayani

Jaki wyjątek? Ślad stosu?
Markiz Lorne

@PareshMayani Tak, nawet na prawdziwym urządzeniu z Androidem 4.0 pokazuje wyjątek.
Bhavit S. Sengar

@EJP Zaktualizowałem pytanie, dodano stacktrace.
Bhavit S. Sengar

To jedyne pytanie otagowane Jellybean. Miałeś na myśli android-4.2-jelly-bean ?
Daniel Daranas

Odpowiedzi:


121

Znalazłem rozwiązanie tego problemu, analizując pakiety danych za pomocą programu wireshark. Odkryłem, że podczas nawiązywania bezpiecznego połączenia Android wracał do SSLv3 z TLSv1 . Jest to błąd w Androidzie w wersji <4.4 i można go rozwiązać, usuwając protokół SSLv3 z listy włączonych protokołów. Zrobiłem niestandardową klasę socketFactory o nazwie NoSSLv3SocketFactory.java. Użyj tego do stworzenia fabryki gniazd.

/*Copyright 2015 Bhavit Singh Sengar
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.*/

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;


public class NoSSLv3SocketFactory extends SSLSocketFactory{
    private final SSLSocketFactory delegate;

public NoSSLv3SocketFactory() {
    this.delegate = HttpsURLConnection.getDefaultSSLSocketFactory();
}

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

@Override
public String[] getDefaultCipherSuites() {
    return delegate.getDefaultCipherSuites();
}

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

private Socket makeSocketSafe(Socket socket) {
    if (socket instanceof SSLSocket) {
        socket = new NoSSLv3SSLSocket((SSLSocket) socket);
    }
    return socket;
}

@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
    return makeSocketSafe(delegate.createSocket(s, host, port, autoClose));
}

@Override
public Socket createSocket(String host, int port) throws IOException {
    return makeSocketSafe(delegate.createSocket(host, port));
}

@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
    return makeSocketSafe(delegate.createSocket(host, port, localHost, localPort));
}

@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
    return makeSocketSafe(delegate.createSocket(host, port));
}

@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
    return makeSocketSafe(delegate.createSocket(address, port, localAddress, localPort));
}

private class NoSSLv3SSLSocket extends DelegateSSLSocket {

    private NoSSLv3SSLSocket(SSLSocket delegate) {
        super(delegate);

    }

    @Override
    public void setEnabledProtocols(String[] protocols) {
        if (protocols != null && protocols.length == 1 && "SSLv3".equals(protocols[0])) {

            List<String> enabledProtocols = new ArrayList<String>(Arrays.asList(delegate.getEnabledProtocols()));
            if (enabledProtocols.size() > 1) {
                enabledProtocols.remove("SSLv3");
                System.out.println("Removed SSLv3 from enabled protocols");
            } else {
                System.out.println("SSL stuck with protocol available for " + String.valueOf(enabledProtocols));
            }
            protocols = enabledProtocols.toArray(new String[enabledProtocols.size()]);
        }

        super.setEnabledProtocols(protocols);
    }
}

public class DelegateSSLSocket extends SSLSocket {

    protected final SSLSocket delegate;

    DelegateSSLSocket(SSLSocket delegate) {
        this.delegate = delegate;
    }

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

    @Override
    public String[] getEnabledCipherSuites() {
        return delegate.getEnabledCipherSuites();
    }

    @Override
    public void setEnabledCipherSuites(String[] suites) {
        delegate.setEnabledCipherSuites(suites);
    }

    @Override
    public String[] getSupportedProtocols() {
        return delegate.getSupportedProtocols();
    }

    @Override
    public String[] getEnabledProtocols() {
        return delegate.getEnabledProtocols();
    }

    @Override
    public void setEnabledProtocols(String[] protocols) {
        delegate.setEnabledProtocols(protocols);
    }

    @Override
    public SSLSession getSession() {
        return delegate.getSession();
    }

    @Override
    public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
        delegate.addHandshakeCompletedListener(listener);
    }

    @Override
    public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
        delegate.removeHandshakeCompletedListener(listener);
    }

    @Override
    public void startHandshake() throws IOException {
        delegate.startHandshake();
    }

    @Override
    public void setUseClientMode(boolean mode) {
        delegate.setUseClientMode(mode);
    }

    @Override
    public boolean getUseClientMode() {
        return delegate.getUseClientMode();
    }

    @Override
    public void setNeedClientAuth(boolean need) {
        delegate.setNeedClientAuth(need);
    }

    @Override
    public void setWantClientAuth(boolean want) {
        delegate.setWantClientAuth(want);
    }

    @Override
    public boolean getNeedClientAuth() {
        return delegate.getNeedClientAuth();
    }

    @Override
    public boolean getWantClientAuth() {
        return delegate.getWantClientAuth();
    }

    @Override
    public void setEnableSessionCreation(boolean flag) {
        delegate.setEnableSessionCreation(flag);
    }

    @Override
    public boolean getEnableSessionCreation() {
        return delegate.getEnableSessionCreation();
    }

    @Override
    public void bind(SocketAddress localAddr) throws IOException {
        delegate.bind(localAddr);
    }

    @Override
    public synchronized void close() throws IOException {
        delegate.close();
    }

    @Override
    public void connect(SocketAddress remoteAddr) throws IOException {
        delegate.connect(remoteAddr);
    }

    @Override
    public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
        delegate.connect(remoteAddr, timeout);
    }

    @Override
    public SocketChannel getChannel() {
        return delegate.getChannel();
    }

    @Override
    public InetAddress getInetAddress() {
        return delegate.getInetAddress();
    }

    @Override
    public InputStream getInputStream() throws IOException {
        return delegate.getInputStream();
    }

    @Override
    public boolean getKeepAlive() throws SocketException {
        return delegate.getKeepAlive();
    }

    @Override
    public InetAddress getLocalAddress() {
        return delegate.getLocalAddress();
    }

    @Override
    public int getLocalPort() {
        return delegate.getLocalPort();
    }

    @Override
    public SocketAddress getLocalSocketAddress() {
        return delegate.getLocalSocketAddress();
    }

    @Override
    public boolean getOOBInline() throws SocketException {
        return delegate.getOOBInline();
    }

    @Override
    public OutputStream getOutputStream() throws IOException {
        return delegate.getOutputStream();
    }

    @Override
    public int getPort() {
        return delegate.getPort();
    }

    @Override
    public synchronized int getReceiveBufferSize() throws SocketException {
        return delegate.getReceiveBufferSize();
    }

    @Override
    public SocketAddress getRemoteSocketAddress() {
        return delegate.getRemoteSocketAddress();
    }

    @Override
    public boolean getReuseAddress() throws SocketException {
        return delegate.getReuseAddress();
    }

    @Override
    public synchronized int getSendBufferSize() throws SocketException {
        return delegate.getSendBufferSize();
    }

    @Override
    public int getSoLinger() throws SocketException {
        return delegate.getSoLinger();
    }

    @Override
    public synchronized int getSoTimeout() throws SocketException {
        return delegate.getSoTimeout();
    }

    @Override
    public boolean getTcpNoDelay() throws SocketException {
        return delegate.getTcpNoDelay();
    }

    @Override
    public int getTrafficClass() throws SocketException {
        return delegate.getTrafficClass();
    }

    @Override
    public boolean isBound() {
        return delegate.isBound();
    }

    @Override
    public boolean isClosed() {
        return delegate.isClosed();
    }

    @Override
    public boolean isConnected() {
        return delegate.isConnected();
    }

    @Override
    public boolean isInputShutdown() {
        return delegate.isInputShutdown();
    }

    @Override
    public boolean isOutputShutdown() {
        return delegate.isOutputShutdown();
    }

    @Override
    public void sendUrgentData(int value) throws IOException {
        delegate.sendUrgentData(value);
    }

    @Override
    public void setKeepAlive(boolean keepAlive) throws SocketException {
        delegate.setKeepAlive(keepAlive);
    }

    @Override
    public void setOOBInline(boolean oobinline) throws SocketException {
        delegate.setOOBInline(oobinline);
    }

    @Override
    public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
        delegate.setPerformancePreferences(connectionTime, latency, bandwidth);
    }

    @Override
    public synchronized void setReceiveBufferSize(int size) throws SocketException {
        delegate.setReceiveBufferSize(size);
    }

    @Override
    public void setReuseAddress(boolean reuse) throws SocketException {
        delegate.setReuseAddress(reuse);
    }

    @Override
    public synchronized void setSendBufferSize(int size) throws SocketException {
        delegate.setSendBufferSize(size);
    }

    @Override
    public void setSoLinger(boolean on, int timeout) throws SocketException {
        delegate.setSoLinger(on, timeout);
    }

    @Override
    public synchronized void setSoTimeout(int timeout) throws SocketException {
        delegate.setSoTimeout(timeout);
    }

    @Override
    public void setTcpNoDelay(boolean on) throws SocketException {
        delegate.setTcpNoDelay(on);
    }

    @Override
    public void setTrafficClass(int value) throws SocketException {
        delegate.setTrafficClass(value);
    }

    @Override
    public void shutdownInput() throws IOException {
        delegate.shutdownInput();
    }

    @Override
    public void shutdownOutput() throws IOException {
        delegate.shutdownOutput();
    }

    @Override
    public String toString() {
        return delegate.toString();
    }

    @Override
    public boolean equals(Object o) {
        return delegate.equals(o);
    }
}
}

Użyj tej klasy w ten sposób podczas łączenia:

SSLContext sslcontext = SSLContext.getInstance("TLSv1");
sslcontext.init(null, null, null);
SSLSocketFactory NoSSLv3Factory = new NoSSLv3SocketFactory(sslcontext.getSocketFactory());

HttpsURLConnection.setDefaultSSLSocketFactory(NoSSLv3Factory);
l_connection = (HttpsURLConnection) l_url.openConnection();
l_connection.connect();

AKTUALIZACJA :

Teraz poprawnym rozwiązaniem byłoby zainstalowanie nowszego dostawcy zabezpieczeń za pomocą usług Google Play :

    ProviderInstaller.installIfNeeded(getApplicationContext());

To efektywnie daje Twojej aplikacji dostęp do nowszej wersji OpenSSL i Java Security Provider, która obejmuje obsługę TLSv1.2 w SSLEngine. Po zainstalowaniu nowego dostawcy możesz utworzyć SSLEngine, który obsługuje SSLv3, TLSv1, TLSv1.1 i TLSv1.2 w zwykły sposób:

    SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
    sslContext.init(null, null, null);
    SSLEngine engine = sslContext.createSSLEngine();

Lub możesz ograniczyć włączone protokoły za pomocą engine.setEnabledProtocols.

Nie zapomnij dodać następującej zależności ( sprawdź najnowszą wersję tutaj ):

implementation 'com.google.android.gms:play-services-auth:17.0.0'

Aby uzyskać więcej informacji, sprawdź ten link .


25
Co się stanie, jeśli otrzymam ten błąd na urządzeniu Lollipop?
IgorGanapolsky

2
Dostaję to 5.0.1
Skynet

Wydaje się, że to podejście zawodzi podczas łączenia się z hostem wirtualnym, ponieważ skutecznie wyłącza SNI, nie udostępniając metody SSLCertificateSocketFactory.setHostname (Socket, String). Otrzymuję błędy 403, w których openssl.exe i przeglądarka łączą się bez problemów. Okazuje się, że to brakujący SNI.
Jaroslav Záruba

2
jak można tego używać z salwą?
uniruddh

1
Otrzymuję ten błąd w Android API 19 javax.net.ssl.SSLProtocolException: Błąd odczytu: ssl = 0xb83d7120: Błąd w bibliotece SSL, zwykle błąd protokołu
Rukmal Dias

118

Scenariusz

Otrzymywałem wyjątki SSLHandshake na urządzeniach z wersjami Androida wcześniejszymi niż Android 5.0. W moim przypadku chciałem również utworzyć TrustManager, aby ufać certyfikatowi mojego klienta.

Zaimplementowałem NoSSLv3SocketFactory i NoSSLv3Factory, aby usunąć SSLv3 z listy obsługiwanych protokołów mojego klienta, ale żadne z tych rozwiązań nie działało.

Kilka rzeczy, których się nauczyłem:

  • Na urządzeniach starszych niż Android 5.0 protokoły TLSv1.1 i TLSv1.2 nie są domyślnie włączone.
  • Protokół SSLv3 nie jest domyślnie wyłączony na urządzeniach starszych niż Android 5.0.
  • SSLv3 nie jest bezpieczny protokołem i dlatego wskazane jest usunięcie go z listy obsługiwanych protokołów naszego klienta przed nawiązaniem połączenia.

Co mi pomogło

Zezwól zabezpieczeniom Androida na Provideraktualizację podczas uruchamiania aplikacji.

Domyślny dostawca przed wersją 5.0+ nie wyłącza SSLv3. Pod warunkiem, że masz dostęp do usług Google Play, stosunkowo łatwo jest załatać dostawcę zabezpieczeń Androida z poziomu swojej aplikacji.

private void updateAndroidSecurityProvider(Activity callingActivity) {
    try {
        ProviderInstaller.installIfNeeded(this);
    } catch (GooglePlayServicesRepairableException 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.
        GooglePlayServicesUtil.getErrorDialog(e.getConnectionStatusCode(), callingActivity, 0);
    } catch (GooglePlayServicesNotAvailableException e) {
        Log.e("SecurityException", "Google Play Services not available.");
    }
}

Jeśli teraz utworzysz OkHttpClient lub HttpURLConnection, TLSv1.1 i TLSv1.2 powinny być dostępne jako protokoły, a SSLv3 powinien zostać usunięty. Jeśli klient / połączenie (a dokładniej jest to SSLContext) zostało zainicjowane przed wywołaniem ProviderInstaller.installIfNeeded(...), należy je odtworzyć.

Nie zapomnij dodać następującej zależności ( najnowszą wersję można znaleźć tutaj ):

compile 'com.google.android.gms:play-services-auth:16.0.1'

Źródła:

Na bok

Nie musiałem jawnie określać algorytmów szyfrowania, których powinien używać mój klient, ale znalazłem post SO zalecający te uważane za najbezpieczniejsze w momencie pisania: Które zestawy szyfrów włączyć dla gniazda SSL?


1
Nie znam żadnych opublikowanych statystyk dotyczących liczby urządzeń z uruchomionymi usługami Google Play. Ostatnią liczbą, jaką udało mi się znaleźć, była rozmowa Sundara Pichai IO 2014, w której powiedział, że 93% urządzeń z Androidem ma najnowszą wersję usług Play. Jeśli chodzi o metodę ręczną, doszedłem do tego rozwiązania, gdy wszystkie metody ręczne zawiodły. Jeśli bezpieczeństwo sieci nie jest istotne dla użytkowników, zawsze możesz wrócić do protokołu HTTP, jeśli użytkownik odmówi zainstalowania usług Play. W przypadku naszej własnej aplikacji rozważaliśmy kompromis między kompatybilnością a bezpieczeństwem. Wymuszamy aktualizację usług Play.
Maurice Gavin

3
Obsługa usług Google Play w Chinach jest bardzo ograniczona. Warto wiedzieć, w zależności od docelowych odbiorców. Ale cóż, jeśli rozpowszechniasz przez Google Play, ta publiczność już traci.
jayeffkay

3
Dodaj tę zależność, aby rozwiązanie działało. kompiluj „com.google.android.gms: play-services-auth: 10.2.0”,
Pradeep Chakravarti Gudipati

3
Dodanie ProviderInstaller.installIfNeeded (this); część w aplikacji onCreate () zadziałała dla mnie! Dzięki :-)
Kelevandos

1
Zdecydowanie najbardziej przydatna odpowiedź tutaj. Dzięki za tak nieocenioną pomoc.
Burak Karakuş

50

Powinieneś także wiedzieć, że możesz wymusić TLS v1.2 na urządzeniach z Androidem 4.0, które nie mają go domyślnie włączonego:

Umieść ten kod w onCreate () pliku aplikacji :

try {
        ProviderInstaller.installIfNeeded(getApplicationContext());
        SSLContext sslContext;
        sslContext = SSLContext.getInstance("TLSv1.2");
        sslContext.init(null, null, null);
        sslContext.createSSLEngine();
    } catch (GooglePlayServicesRepairableException | GooglePlayServicesNotAvailableException
            | NoSuchAlgorithmException | KeyManagementException e) {
        e.printStackTrace();
    }

7
Pracował dla mnie z rxJava OkHttpClient.Builder, wielkie dzięki.
Gweltaz Niquel

2
Działa na Androidzie 7.0.
CoolMind

1
dzięki @mayur gangurde, to jest do pracy dla mnie po dodaniu usług Play auth gradle, wdrożenie „com.google.android.gms: play-services-auth: 16.0.1”
Basant

Działał doskonale. Dziękuję bardzo za zaoszczędzenie godzin testowania i próbowania.
Ninad Desai

To bardzo wygodne. Wielkie dzięki!
Caspar Geerlings,

15

Wcześniej rozwiązałem również ten problem za pomocą niestandardowej SSLFactoryimplementacji, ale zgodnie z dokumentacją OkHttp rozwiązanie jest znacznie łatwiejsze.

Moje ostateczne rozwiązanie z potrzebnymi TLSszyframi dla urządzeń 4.2+ wygląda następująco:

public UsersApi provideUsersApi() {

    private ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.COMPATIBLE_TLS)
        .supportsTlsExtensions(true)
        .tlsVersions(TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0)
        .cipherSuites(
                CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
                CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
                CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
                CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
                CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
                CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
                CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
                CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
                CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
                CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
                CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
                CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA)
        .build();

    OkHttpClient client = new OkHttpClient.Builder()
            .connectionSpecs(Collections.singletonList(spec))
            .build();

    return new Retrofit.Builder()
            .baseUrl(USERS_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .build()
            .create(UsersApi.class);
}

Należy pamiętać, że zestaw obsługiwanych protokołów zależy od konfiguracji na serwerze.


Czy Twoje rozwiązanie obsługuje API 14?
CoolMind

Nie mogę powiedzieć na pewno, przetestowałem to tylko na urządzeniach z 16+ API, działa dobrze.
Fragment

1
Fragment, uroczy pseudonim :) Zgadzam się, nie mogłem nawet uruchomić emulatora API 14, ale Twoje rozwiązanie działa na Androidzie 7.0 (który miał problem) i na API 19.
CoolMind

1
Dzięki, przetestowałem, zadziałało. Dzisiaj ponownie uruchomiliśmy emulator API 19 i musiałem dodać rozwiązanie stackoverflow.com/a/51285550/2914140 .
CoolMind

13

Znalazłem rozwiązanie tutaj w tym linku .

Musisz tylko umieścić poniższy kod w swojej klasie aplikacji na Androida. I to wystarczy. Nie musisz wprowadzać żadnych zmian w ustawieniach modernizacji. To uratowało mi dzień.

public class MyApplication extends Application {
@Override
public void onCreate() {
    super.onCreate();
    try {
      // Google Play will install latest OpenSSL 
      ProviderInstaller.installIfNeeded(getApplicationContext());
      SSLContext sslContext;
      sslContext = SSLContext.getInstance("TLSv1.2");
      sslContext.init(null, null, null);
      sslContext.createSSLEngine();
    } catch (GooglePlayServicesRepairableException | GooglePlayServicesNotAvailableException
        | NoSuchAlgorithmException | KeyManagementException e) {
        e.printStackTrace();
        }
    }
}

Mam nadzieję, że to pomoże. Dziękuję Ci.



4

To rozwiązało to dla mnie:

Dokumentacja systemu Android dla SSLSocket mówi, że protokoły TLS 1.1 i TLS 1.2 są obsługiwane w systemie Android od poziomu interfejsu API 16+ (Android 4.1, Jelly Bean). Ale jest domyślnie wyłączony, ale począwszy od poziomu API 20+ (Android 4.4 na zegarek, Kitkat Watch i Android 5.0 na telefon, Lollipop) są włączone. Ale bardzo trudno jest znaleźć jakąkolwiek dokumentację, jak włączyć to na przykład na telefonach z systemem 4.1. Aby włączyć TLS 1.1 i 1.2, musisz utworzyć niestandardowy SSLSocketFactory, który będzie proxy wszystkie wywołania do domyślnej implementacji SSLSocketFactory. Oprócz tego musimy nadpisać wszystkie metody createSocket i callsetEnabledProtocols na zwróconym SSLSocket, aby włączyć TLS 1.1 i TLS 1.2. Aby zobaczyć przykładową implementację, kliknij poniższy link.

android 4.1. włącz tls1.1 i tls 1.2


2

Mam też ten problem z raportem błędów. Mój kod jest poniżej.

public static void getShop() throws Exception {
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                OkHttpClient client = new OkHttpClient();
                Request request = new Request.Builder()
                        .url("https://10.0.2.2:8010/getShopInfo/aaa")
                        .build();
                Response response = client.newCall(request).execute();
                Log.d("response", response.body().string());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }).start();
}

Mam swojego Springboota jako moje zaplecze i używam Androida OKHttp, aby uzyskać informacje. Krytyczny błąd, jaki popełniłem, polegał na tym, że używam .url ( „https : //10.0.2.2: 8010 / getShopInfo / aaa”) w kodzie Androida. Ale mój backend nie jest dozwolony dla żądania https. Po użyciu .url („ http : //10.0.2.2: 8010 / getShopInfo / aaa”) kod poszedł dobrze. Więc chcę powiedzieć, że moim błędem nie jest wersja emulatora, a protokół żądania. Po wykonaniu tego, co powiedziałem, napotykam inny problem, ale jest to inny problem i dołączam metodę rozwiązania nowego problemu .
Powodzenia!


1

Było odtwarzalne tylko wtedy, gdy używam proxy na genymotion (<4.4).

Sprawdź ustawienia proxy w Ustawienia-> Sieci zwykłe i bezprzewodowe-> Wi-Fi -> (Długie naciśnięcie WiredSSID) -> Modyfikuj sieć

Wybierz pokaż opcje zaawansowane: ustaw Ustawienia proxy na BRAK.


1

Kiedy otrzymałem ten błąd, było to spowodowane tym, że protokoły (wersje TLS) i / lub mechanizmy szyfrowania obsługiwane przez serwer nie były włączone (i prawdopodobnie nawet nie były obsługiwane) na urządzeniu. W przypadku interfejsu API 16-19 protokoły TLSv1.1 i TLSv1.2 są obsługiwane, ale nie są domyślnie włączone. Po włączeniu ich dla tych wersji nadal otrzymuję błąd, ponieważ te wersje nie obsługują żadnego z szyfrów w naszej instancji AWS CloudFront.

Ponieważ nie jest możliwe dodanie szyfrów do Androida, musieliśmy zmienić naszą wersję CloudFront z TLSv1.2_2018 na TLSv1.1_2016 (który nadal obsługuje TLSv1.2; po prostu go nie wymaga), która ma cztery z nich obsługiwane przez wcześniejsze wersje Androida, z których dwie są nadal uważane za mocne.

W tym momencie błąd zniknął i połączenia były realizowane (z TLSv1.2), ponieważ istniał co najmniej jeden protokół i co najmniej jeden szyfr, który współużytkował urządzenie i serwer.

Zapoznaj się z tabelami na tej stronie, aby zobaczyć, które protokoły i szyfry są obsługiwane i włączone w poszczególnych wersjach Androida.

Czy system Android naprawdę próbował używać protokołu SSLv3, jak sugeruje to część komunikatu o błędzie „błąd uzgadniania alertu sslv3”? Wątpię; Podejrzewam, że to stara pajęczyna w bibliotece SSL, która nie została wyczyszczona, ale nie mogę powiedzieć na pewno.

Aby włączyć TLSv1.2 (i TLSv1.1), mogłem użyć znacznie prostszego SSLSocketFactoryniż te widziane gdzie indziej (jak NoSSLv3SocketFactory). Po prostu upewnia się, że włączone protokoły obejmują wszystkie obsługiwane protokoły, a włączone szyfry obejmują wszystkie obsługiwane szyfry (te ostatnie nie były dla mnie konieczne, ale mogą być dla innych) - patrz configure()na dole. Jeśli wolisz włączyć tylko najnowsze protokoły, możesz zastąpić socket.supportedProtocolsczymś takim arrayOf("TLSv1.1", "TLSv1.2")(podobnie jak szyfry):

class TLSSocketFactory : SSLSocketFactory() {

    private val socketFactory: SSLSocketFactory

    init {
        val sslContext = SSLContext.getInstance("TLS")
        sslContext.init(null, null, null)
        socketFactory = sslContext.socketFactory
    }

    override fun getDefaultCipherSuites(): Array<String> {
        return socketFactory.defaultCipherSuites
    }

    override fun getSupportedCipherSuites(): Array<String> {
        return socketFactory.supportedCipherSuites
    }

    override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket {
        return configure(socketFactory.createSocket(s, host, port, autoClose) as SSLSocket)
    }

    override fun createSocket(host: String, port: Int): Socket {
        return configure(socketFactory.createSocket(host, port) as SSLSocket)
    }

    override fun createSocket(host: InetAddress, port: Int): Socket {
        return configure(socketFactory.createSocket(host, port) as SSLSocket)
    }

    override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket {
        return configure(socketFactory.createSocket(host, port, localHost, localPort) as SSLSocket)
    }

    override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket {
        return configure(socketFactory.createSocket(address, port, localAddress, localPort) as SSLSocket)
    }

    private fun configure(socket: SSLSocket): SSLSocket {
        socket.enabledProtocols = socket.supportedProtocols
        socket.enabledCipherSuites = socket.supportedCipherSuites
        return socket
    }
}

Jak tego używasz?
CoolMind

0

Rozwiązałem problem w ten sposób: NoSSLv3SocketFactory.java

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

public class NoSSLv3SocketFactory extends SSLSocketFactory {
    private final SSLSocketFactory delegate;

    public NoSSLv3SocketFactory() {
        this.delegate = HttpsURLConnection.getDefaultSSLSocketFactory();
    }

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

    @Override
    public String[] getDefaultCipherSuites() {
        return delegate.getDefaultCipherSuites();
    }

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

    private Socket makeSocketSafe(Socket socket) {
        if (socket instanceof SSLSocket) {
            socket = new NoSSLv3SSLSocket((SSLSocket) socket);
        }
        return socket;
    }

    @Override
    public Socket createSocket(Socket s, String host, int port,
            boolean autoClose) throws IOException {
        return makeSocketSafe(delegate.createSocket(s, host, port, autoClose));
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException {
        return makeSocketSafe(delegate.createSocket(host, port));
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost,
            int localPort) throws IOException {
        return makeSocketSafe(delegate.createSocket(host, port, localHost,
                localPort));
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        return makeSocketSafe(delegate.createSocket(host, port));
    }

    @Override
    public Socket createSocket(InetAddress address, int port,
            InetAddress localAddress, int localPort) throws IOException {
        return makeSocketSafe(delegate.createSocket(address, port,
                localAddress, localPort));
    }

    private class NoSSLv3SSLSocket extends DelegateSSLSocket {

        private NoSSLv3SSLSocket(SSLSocket delegate) {
            super(delegate);

        }

        @Override
        public void setEnabledProtocols(String[] protocols) {
            if (protocols != null && protocols.length == 1
                    && "SSLv3".equals(protocols[0])) {

                List<String> enabledProtocols = new ArrayList<String>(
                        Arrays.asList(delegate.getEnabledProtocols()));
                if (enabledProtocols.size() > 1) {
                    enabledProtocols.remove("SSLv3");
                    System.out.println("Removed SSLv3 from enabled protocols");
                } else {
                    System.out.println("SSL stuck with protocol available for "
                            + String.valueOf(enabledProtocols));
                }
                protocols = enabledProtocols
                        .toArray(new String[enabledProtocols.size()]);
            }

//          super.setEnabledProtocols(protocols);
            super.setEnabledProtocols(new String[]{"TLSv1.2"});
        }
    }

    public class DelegateSSLSocket extends SSLSocket {

        protected final SSLSocket delegate;

        DelegateSSLSocket(SSLSocket delegate) {
            this.delegate = delegate;
        }

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

        @Override
        public String[] getEnabledCipherSuites() {
            return delegate.getEnabledCipherSuites();
        }

        @Override
        public void setEnabledCipherSuites(String[] suites) {
            delegate.setEnabledCipherSuites(suites);
        }

        @Override
        public String[] getSupportedProtocols() {
            return delegate.getSupportedProtocols();
        }

        @Override
        public String[] getEnabledProtocols() {
            return delegate.getEnabledProtocols();
        }

        @Override
        public void setEnabledProtocols(String[] protocols) {
            delegate.setEnabledProtocols(protocols);
        }

        @Override
        public SSLSession getSession() {
            return delegate.getSession();
        }

        @Override
        public void addHandshakeCompletedListener(
                HandshakeCompletedListener listener) {
            delegate.addHandshakeCompletedListener(listener);
        }

        @Override
        public void removeHandshakeCompletedListener(
                HandshakeCompletedListener listener) {
            delegate.removeHandshakeCompletedListener(listener);
        }

        @Override
        public void startHandshake() throws IOException {
            delegate.startHandshake();
        }

        @Override
        public void setUseClientMode(boolean mode) {
            delegate.setUseClientMode(mode);
        }

        @Override
        public boolean getUseClientMode() {
            return delegate.getUseClientMode();
        }

        @Override
        public void setNeedClientAuth(boolean need) {
            delegate.setNeedClientAuth(need);
        }

        @Override
        public void setWantClientAuth(boolean want) {
            delegate.setWantClientAuth(want);
        }

        @Override
        public boolean getNeedClientAuth() {
            return delegate.getNeedClientAuth();
        }

        @Override
        public boolean getWantClientAuth() {
            return delegate.getWantClientAuth();
        }

        @Override
        public void setEnableSessionCreation(boolean flag) {
            delegate.setEnableSessionCreation(flag);
        }

        @Override
        public boolean getEnableSessionCreation() {
            return delegate.getEnableSessionCreation();
        }

        @Override
        public void bind(SocketAddress localAddr) throws IOException {
            delegate.bind(localAddr);
        }

        @Override
        public synchronized void close() throws IOException {
            delegate.close();
        }

        @Override
        public void connect(SocketAddress remoteAddr) throws IOException {
            delegate.connect(remoteAddr);
        }

        @Override
        public void connect(SocketAddress remoteAddr, int timeout)
                throws IOException {
            delegate.connect(remoteAddr, timeout);
        }

        @Override
        public SocketChannel getChannel() {
            return delegate.getChannel();
        }

        @Override
        public InetAddress getInetAddress() {
            return delegate.getInetAddress();
        }

        @Override
        public InputStream getInputStream() throws IOException {
            return delegate.getInputStream();
        }

        @Override
        public boolean getKeepAlive() throws SocketException {
            return delegate.getKeepAlive();
        }

        @Override
        public InetAddress getLocalAddress() {
            return delegate.getLocalAddress();
        }

        @Override
        public int getLocalPort() {
            return delegate.getLocalPort();
        }

        @Override
        public SocketAddress getLocalSocketAddress() {
            return delegate.getLocalSocketAddress();
        }

        @Override
        public boolean getOOBInline() throws SocketException {
            return delegate.getOOBInline();
        }

        @Override
        public OutputStream getOutputStream() throws IOException {
            return delegate.getOutputStream();
        }

        @Override
        public int getPort() {
            return delegate.getPort();
        }

        @Override
        public synchronized int getReceiveBufferSize() throws SocketException {
            return delegate.getReceiveBufferSize();
        }

        @Override
        public SocketAddress getRemoteSocketAddress() {
            return delegate.getRemoteSocketAddress();
        }

        @Override
        public boolean getReuseAddress() throws SocketException {
            return delegate.getReuseAddress();
        }

        @Override
        public synchronized int getSendBufferSize() throws SocketException {
            return delegate.getSendBufferSize();
        }

        @Override
        public int getSoLinger() throws SocketException {
            return delegate.getSoLinger();
        }

        @Override
        public synchronized int getSoTimeout() throws SocketException {
            return delegate.getSoTimeout();
        }

        @Override
        public boolean getTcpNoDelay() throws SocketException {
            return delegate.getTcpNoDelay();
        }

        @Override
        public int getTrafficClass() throws SocketException {
            return delegate.getTrafficClass();
        }

        @Override
        public boolean isBound() {
            return delegate.isBound();
        }

        @Override
        public boolean isClosed() {
            return delegate.isClosed();
        }

        @Override
        public boolean isConnected() {
            return delegate.isConnected();
        }

        @Override
        public boolean isInputShutdown() {
            return delegate.isInputShutdown();
        }

        @Override
        public boolean isOutputShutdown() {
            return delegate.isOutputShutdown();
        }

        @Override
        public void sendUrgentData(int value) throws IOException {
            delegate.sendUrgentData(value);
        }

        @Override
        public void setKeepAlive(boolean keepAlive) throws SocketException {
            delegate.setKeepAlive(keepAlive);
        }

        @Override
        public void setOOBInline(boolean oobinline) throws SocketException {
            delegate.setOOBInline(oobinline);
        }

        @Override
        public void setPerformancePreferences(int connectionTime, int latency,
                int bandwidth) {
            delegate.setPerformancePreferences(connectionTime, latency,
                    bandwidth);
        }

        @Override
        public synchronized void setReceiveBufferSize(int size)
                throws SocketException {
            delegate.setReceiveBufferSize(size);
        }

        @Override
        public void setReuseAddress(boolean reuse) throws SocketException {
            delegate.setReuseAddress(reuse);
        }

        @Override
        public synchronized void setSendBufferSize(int size)
                throws SocketException {
            delegate.setSendBufferSize(size);
        }

        @Override
        public void setSoLinger(boolean on, int timeout) throws SocketException {
            delegate.setSoLinger(on, timeout);
        }

        @Override
        public synchronized void setSoTimeout(int timeout)
                throws SocketException {
            delegate.setSoTimeout(timeout);
        }

        @Override
        public void setTcpNoDelay(boolean on) throws SocketException {
            delegate.setTcpNoDelay(on);
        }

        @Override
        public void setTrafficClass(int value) throws SocketException {
            delegate.setTrafficClass(value);
        }

        @Override
        public void shutdownInput() throws IOException {
            delegate.shutdownInput();
        }

        @Override
        public void shutdownOutput() throws IOException {
            delegate.shutdownOutput();
        }

        @Override
        public String toString() {
            return delegate.toString();
        }

        @Override
        public boolean equals(Object o) {
            return delegate.equals(o);
        }
    }
}

Klasa główna:

URL url = new URL("https://www.example.com/test.png");
URLConnection l_connection = null;
SSLContext sslcontext = SSLContext.getInstance("TLSv1.2");
sslcontext.init(null, null, null);
SSLSocketFactory NoSSLv3Factory = new NoSSLv3SocketFactory(sslcontext.getSocketFactory());

0

Moja odpowiedź jest zbliżona do powyższych odpowiedzi, ale musisz dokładnie napisać zajęcia, nie zmieniając niczego.

public class TLSSocketFactory extends SSLSocketFactory {

private SSLSocketFactory delegate;

public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
    SSLContext context = SSLContext.getInstance("TLS");
    context.init(null, null, null);
    delegate = context.getSocketFactory();
}

@Override
public String[] getDefaultCipherSuites() {
    return delegate.getDefaultCipherSuites();
}

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

@Override
public Socket createSocket() throws IOException {
    return enableTLSOnSocket(delegate.createSocket());
}

@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
    return enableTLSOnSocket(delegate.createSocket(s, host, port, autoClose));
}

@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
    return enableTLSOnSocket(delegate.createSocket(host, port));
}

@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
    return enableTLSOnSocket(delegate.createSocket(host, port, localHost, localPort));
}

@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
    return enableTLSOnSocket(delegate.createSocket(host, port));
}

@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
    return enableTLSOnSocket(delegate.createSocket(address, port, localAddress, localPort));
}

private Socket enableTLSOnSocket(Socket socket) {
    if(socket != null && (socket instanceof SSLSocket)) {
        ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
    }
    return socket;
}

}

i używać go z HttpsURLConnection

HttpsURLConnection  conn = (HttpsURLConnection) url.openConnection();

int sdk = android.os.Build.VERSION.SDK_INT;
            if (sdk < Build.VERSION_CODES.LOLLIPOP) {
                if (url.toString().startsWith("https")) {
                    try {
                        TLSSocketFactory sc = new TLSSocketFactory();
                        conn.setSSLSocketFactory(sc);
                    } catch (Exception e) {
                        String sss = e.toString();
                    }
                }
            }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.