Javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL-Handshake abgebrochen: Fehler in der SSL-Bibliothek, normalerweise ein Protokollfehler

101

Ich versuche, den folgenden Code in Android auszuführen

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();

Ein l_connection.connect(), es gibt diese SSLhandshakeException. Manchmal funktioniert es, aber meistens gibt es die Ausnahme. Es passiert nur auf Android 4.0 Emulator. Ich habe es auf Android 4.4 und 5.0 getestet, es funktioniert gut. Was könnte die Ursache dafür sein? Bitte helfen Sie

STACKTRACE

    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
Bhavit S. Sengar
quelle
Hast du deine App auf einem echten Gerät überprüft?
Paresh Mayani
Es gibt welche Ausnahme? Stapelspur?
Marquis von Lorne
@PareshMayani Ja, selbst auf realen Geräten mit Android 4.0 wird eine Ausnahme angezeigt.
Bhavit S. Sengar
@EJP Ich habe die Frage aktualisiert, Stacktrace wurde hinzugefügt.
Bhavit S. Sengar
Dies ist die einzige Frage, die mit Jellybean gekennzeichnet ist. Meinten Sie Android-4.2-Jelly-Bean ?
Daniel Daranas

Antworten:

120

Ich fand die Lösung dafür, indem ich die Datenpakete mit Wireshark analysierte. Was ich fand, war, dass Android beim Herstellen einer sicheren Verbindung von TLSv1 auf SSLv3 zurückfiel . Es ist ein Fehler in Android-Versionen <4.4 und kann behoben werden, indem das SSLv3-Protokoll aus der Liste der aktivierten Protokolle entfernt wird. Ich habe eine benutzerdefinierte socketFactory-Klasse namens NoSSLv3SocketFactory.java erstellt. Verwenden Sie dies, um eine Socketfactory zu erstellen.

/*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);
    }
}
}

Verwenden Sie diese Klasse beim Verbinden wie folgt:

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();

UPDATE:

Die richtige Lösung wäre nun die Installation eines neueren Sicherheitsanbieters mithilfe von Google Play Services :

    ProviderInstaller.installIfNeeded(getApplicationContext());

Auf diese Weise erhält Ihre App effektiv Zugriff auf eine neuere Version von OpenSSL und Java Security Provider, die TLSv1.2 in SSLEngine unterstützt. Sobald der neue Anbieter installiert ist, können Sie eine SSLEngine erstellen, die SSLv3, TLSv1, TLSv1.1 und TLSv1.2 auf die übliche Weise unterstützt:

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

Oder Sie können die aktivierten Protokolle mit einschränken engine.setEnabledProtocols .

Vergessen Sie nicht, die folgende Abhängigkeit hinzuzufügen ( überprüfen Sie die neueste Version hier ):

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

Weitere Informationen finden Sie unter diesem Link .

Bhavit S. Sengar
quelle
25
Was ist, wenn ich diesen Fehler auf einem Lollipop-Gerät erhalte?
IgorGanapolsky
2
Ich bekomme dies am 5.0.1
Skynet
Dieser Ansatz scheint bei der Verbindung mit einem virtuellen Host fehlzuschlagen, da SNI effektiv deaktiviert wird, indem die Methode SSLCertificateSocketFactory.setHostname (Socket, String) nicht angegeben wird. Ich habe 403s bekommen, bei denen openssl.exe und Browser ohne Probleme eine Verbindung herstellen. Es stellt sich heraus, dass es sich um das fehlende SNI handelt.
Jaroslav Záruba
2
Wie kann man das mit Volleyball machen?
Uniruddh
1
Ich erhalte diesen Fehler in der Android API 19 javax.net.ssl.SSLProtocolException: Lesefehler: ssl = 0xb83d7120: Fehler in der SSL-Bibliothek, normalerweise ein Protokollfehler
Rukmal Dias
117

Szenario

Ich habe SSLHandshake-Ausnahmen auf Geräten erhalten, auf denen Versionen von Android vor Android 5.0 ausgeführt wurden. In meinem Anwendungsfall wollte ich auch einen TrustManager erstellen, um meinem Client-Zertifikat zu vertrauen.

Ich habe NoSSLv3SocketFactory und NoSSLv3Factory implementiert , um SSLv3 aus der Liste der unterstützten Protokolle meines Clients zu entfernen, aber ich konnte keine dieser Lösungen zum .

Einige Dinge, die ich gelernt habe:

  • Auf Geräten, die älter als Android 5.0 sind, sind die Protokolle TLSv1.1 und TLSv1.2 standardmäßig nicht aktiviert.
  • Das SSLv3-Protokoll ist auf Geräten, die älter als Android 5.0 sind, standardmäßig nicht deaktiviert.
  • SSLv3 ist kein sicheres Protokoll und es ist daher wünschenswert, es aus der Liste der unterstützten Protokolle unseres Clients zu entfernen, bevor eine Verbindung hergestellt wird.

Was hat bei mir funktioniert?

Lassen Sie die Sicherheit Providervon Android beim Starten Ihrer App aktualisieren.

Der Standardanbieter vor 5.0+ deaktiviert SSLv3 nicht. Vorausgesetzt, Sie haben Zugriff auf Google Play-Dienste, ist es relativ einfach, den Sicherheitsanbieter von Android über Ihre App zu patchen.

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.");
    }
}

Wenn Sie jetzt Ihren OkHttpClient oder HttpURLConnection erstellen, sollten TLSv1.1 und TLSv1.2 als Protokolle verfügbar sein und SSLv3 sollte entfernt werden. Wenn der Client / die Verbindung (oder genauer gesagt der SSLContext) vor dem Aufruf initialisiert wurde, ProviderInstaller.installIfNeeded(...)muss sie neu erstellt werden.

Vergessen Sie nicht, die folgende Abhängigkeit hinzuzufügen ( neueste Version hier ):

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

Quellen:

Beiseite

Ich musste nicht explizit festlegen, welche Verschlüsselungsalgorithmen mein Client verwenden soll, aber ich fand einen SO-Beitrag, in dem diejenigen empfohlen wurden, die zum Zeitpunkt des Schreibens als am sichersten galten: Welche Cipher Suites sollten für SSL Socket aktiviert werden?

Maurice Gavin
quelle
1
Ich kenne keine veröffentlichten Statistiken für die Anzahl der Geräte, auf denen Google Play Services ausgeführt wird. Die neueste Zahl, die ich finden konnte, war Sundar Pichais IO 2014-Vortrag, in dem er sagte, dass 93% der Android-Geräte die neueste Version von Play Services hatten. In Bezug auf eine manuelle Methode kam ich zu dieser Lösung, als alle manuellen Methoden für mich nicht funktionierten. Wenn die Netzwerksicherheit für Ihre Benutzer nicht wichtig ist, können Sie jederzeit auf HTTP zurückgreifen, wenn der Benutzer die Installation von Play Services ablehnt. Für unsere eigene App haben wir den Kompromiss zwischen Kompatibilität und Sicherheit berücksichtigt. Wir erzwingen ein Update der Play Services.
Maurice Gavin
3
Der Support für Google Play Services in China ist sehr begrenzt. Könnte je nach Zielgruppe gut zu wissen sein. Wenn Sie über Google Play vertreiben, fehlt diese Zielgruppe bereits.
Jayeffkay
3
Bitte fügen Sie diese Abhängigkeit hinzu, damit die Lösung funktioniert. kompiliere 'com.google.android.gms: play-services-auth: 10.2.0',
Pradeep Chakravarti Gudipati
3
Hinzufügen des ProviderInstaller.installIfNeeded (this); Teil in Application's onCreate () hat bei mir funktioniert! Danke :-)
Kelevandos
1
Auf jeden Fall die nützlichste Antwort hier. Vielen Dank für diese unschätzbare Hilfe.
Burak Karakuş
50

Außerdem sollten Sie wissen, dass Sie TLS v1.2 für Android 4.0-Geräte erzwingen können, für die es standardmäßig nicht aktiviert ist:

Fügen Sie diesen Code in onCreate () Ihrer Anwendungsdatei ein :

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();
    }
Mayur Gangurde
quelle
7
Arbeitete für mich mit rxJava OkHttpClient.Builder, vielen Dank.
Gweltaz Niquel
2
Funktioniert unter Android 7.0.
CoolMind
1
danke @mayur gangurde, dies ist für mich nach dem Hinzufügen von Play Services Auth Gradle, Implementierung 'com.google.android.gms: Play-Services-Auth: 16.0.1'
Basant
Hat perfekt funktioniert. Vielen Dank, dass Sie mir Stunden beim Testen und Ausprobieren erspart haben.
Ninad Desai
Das ist sehr praktisch. Vielen Dank!
Caspar Geerlings
15

Früher habe ich dieses Problem auch mit einer benutzerdefinierten SSLFactoryImplementierung gelöst , aber laut OkHttp-Dokumenten ist die Lösung viel einfacher.

Meine endgültige Lösung mit den erforderlichen TLSChiffren für Geräte ab 4.2 sieht folgendermaßen aus:

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);
}

Beachten Sie, dass der Satz unterstützter Protokolle von der Konfiguration auf Ihrem Server abhängt.

Fragment
quelle
Unterstützt Ihre Lösung API 14?
CoolMind
Kann nicht sicher sagen, dies nur auf 16+ API-Geräten getestet, funktioniert gut.
Fragment
1
Fragment, ein süßer Spitzname :) Ich stimme zu, konnte nicht einmal den API 14-Emulator starten, aber Ihre Lösung funktioniert unter Android 7.0 (das hatte das Problem) und unter API 19.
CoolMind
1
Danke, ich habe getestet, es hat funktioniert. Heute wurde erneut ein API 19-Emulator gestartet, und es musste eine Lösung stackoverflow.com/a/51285550/2914140 hinzugefügt werden .
CoolMind
12

Ich habe die Lösung hier in diesem Link gefunden .

Sie müssen nur den folgenden Code in Ihre Android-Anwendungsklasse einfügen. Und das ist genug. Sie müssen keine Änderungen an Ihren Retrofit-Einstellungen vornehmen. Es hat meinen Tag gerettet.

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();
        }
    }
}

Hoffe das wird hilfreich sein. Danke dir.

Taz
quelle
4

Das hat es für mich gelöst:

Die Android-Dokumentation für SSLSocket besagt, dass TLS 1.1 und TLS 1.2 in Android ab API Level 16+ (Android 4.1, Jelly Bean) unterstützt werden. Es ist jedoch standardmäßig deaktiviert, aber ab API-Level 20+ (Android 4.4 für Watch, Kitkat Watch und Android 5.0 für Telefon, Lollipop) sind sie aktiviert. Es ist jedoch sehr schwierig, eine Dokumentation zu finden, wie diese beispielsweise für Telefone mit 4.1 aktiviert werden kann. Um TLS 1.1 und 1.2 zu aktivieren, müssen Sie eine benutzerdefinierte SSLSocketFactory erstellen, die alle Aufrufe an eine Standardimplementierung von SSLSocketFactory weiterleitet. Darüber hinaus müssen wir alle createSocket-Methoden und callsetEnabledProtocols auf dem zurückgegebenen SSLSocket überschreiben, um TLS 1.1 und TLS 1.2 zu aktivieren. Für eine Beispielimplementierung folgen Sie einfach dem Link unten.

Android 4.1. Aktivieren Sie tls1.1 und tls 1.2

Littlenoname
quelle
2

Ich habe auch dieses Problem mit der Fehlermeldung. Mein Code ist unten.

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();
}

Ich habe meinen Springboot als Backend und verwende Android OKHttp, um Informationen zu erhalten. Der kritische Fehler, den ich gemacht habe, war, dass ich eine .url ( "https : //10.0.2.2: 8010 / getShopInfo / aaa") im Code von Android verwende. Aber die my backend ist keine https Anfrage erlaubt. Nachdem ich .url (" http : //10.0.2.2: 8010 / getShopInfo / aaa") verwendet habe , ist mein Code gut gelaufen . Ich möchte also sagen, dass mein Fehler nicht die Version des Emulators ist, sondern das Anforderungsprotokoll. Ich treffe ein anderes Problem, nachdem ich getan habe, was ich gesagt habe, aber es ist ein anderes Problem, und ich füge die Lösungsmethode des neuen Problems hinzu .
Viel Glück!

X. Professor
quelle
1

Es war nur reproduzierbar, wenn ich Proxy für Genymotion verwende (<4.4).

Überprüfen Sie Ihre Proxy-Einstellungen unter Einstellungen-> Drahtlos & Netzwerke-> WiFi -> (lange auf WiredSSID drücken) -> Netzwerk ändern

Wählen Sie Erweiterte Optionen anzeigen: Setzen Sie die Proxy-Einstellungen auf KEINE.

Yifan
quelle
1

Als ich diesen Fehler bekam, lag es daran, dass die vom Server unterstützten Protokolle (TLS-Versionen) und / oder Cipher Suites auf dem Gerät nicht aktiviert waren (und möglicherweise nicht einmal vom Gerät unterstützt wurden). Für API 16-19 werden TLSv1.1 und TLSv1.2 unterstützt, aber standardmäßig nicht aktiviert. Nachdem ich sie für diese Versionen aktiviert habe, wurde immer noch der Fehler angezeigt, da diese Versionen keine der Chiffren in unserer Instanz von AWS CloudFront unterstützen.

Da es nicht möglich ist, Chiffren zu Android hinzuzufügen, mussten wir unsere CloudFront-Version von TLSv1.2_2018 auf TLSv1.1_2016 (das TLSv1.2 weiterhin unterstützt; es erfordert es nur nicht) umstellen, wobei vier der Chiffren von unterstützt werden die früheren Android-Versionen, von denen zwei immer noch als stark gelten.

Zu diesem Zeitpunkt verschwand der Fehler und die Anrufe wurden (mit TLSv1.2) weitergeleitet, da mindestens ein Protokoll und mindestens eine Verschlüsselung von Gerät und Server gemeinsam genutzt wurden.

Beachten Sie die Tabellen auf dieser Seite welche Protokolle und Chiffren von welchen Android-Versionen unterstützt und aktiviert werden.

Versuchte Android nun wirklich, SSLv3 zu verwenden, wie dies durch den Teil "sslv3-Alarm-Handshake-Fehler" in der Fehlermeldung impliziert wird? Ich bezweifle das; Ich vermute, dass dies ein altes Spinnennetz in der SSL-Bibliothek ist, das nicht bereinigt wurde, aber ich kann es nicht sicher sagen.

Um TLSv1.2 (und TLSv1.1) zu aktivieren, konnte ich eine viel einfachere SSLSocketFactoryals die an anderer Stelle gezeigten verwenden (wie NoSSLv3SocketFactory). Es wird lediglich sichergestellt, dass die aktivierten Protokolle alle unterstützten Protokolle enthalten und dass die aktivierten Chiffren alle unterstützten Chiffren enthalten (letzteres war für mich nicht erforderlich, könnte aber für andere sein) - siehe configure()unten. Wenn Sie lieber nur die neuesten Protokolle aktivieren möchten, können Sie diese durch socket.supportedProtocolsFolgendes ersetzen arrayOf("TLSv1.1", "TLSv1.2")(ebenfalls für die Chiffren):

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
    }
}
Gumby The Green
quelle
Wie benutzt man es?
CoolMind
0

Ich habe das Problem damit gelöst: 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);
        }
    }
}

Hauptklasse :

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());
Girish Patel
quelle
0

Meine Antwort entspricht in etwa den obigen Antworten, aber Sie müssen die Klasse genau schreiben, ohne etwas zu ändern.

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;
}

}}

und um es mit HttpsURLConnection zu verwenden

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();
                    }
                }
            }
Wowo Ot
quelle