Javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake dibatalkan: Kegagalan di pustaka SSL, biasanya kesalahan protokol

102

Saya mencoba menjalankan kode berikut di 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();

Aktif l_connection.connect(), ini memberikan SSLhandshakeException ini. Kadang-kadang berhasil, tetapi sebagian besar waktu memberikan pengecualian. Ini hanya terjadi pada emulator Android 4.0. Saya mengujinya di Android 4.4 dan 5.0, itu berfungsi dengan baik. Apa penyebabnya? Tolong bantu

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
sumber
apakah Anda memeriksa aplikasi Anda di perangkat nyata?
Paresh Mayani
Ini memberikan pengecualian apa? Jejak tumpukan?
Marquis dari Lorne
@PareshMayani Ya, bahkan pada perangkat nyata yang memiliki Android 4.0, ini menunjukkan pengecualian.
Bhavit S. Sengar
@EJP Saya telah memperbarui pertanyaan, stacktrace ditambahkan.
Bhavit S. Sengar
Ini adalah satu - satunya pertanyaan yang diberi tag jellybean. Apakah maksud Anda android-4.2-jelly-bean ?
Daniel Daranas

Jawaban:

120

Saya menemukan solusinya dengan menganalisis paket data menggunakan wireshark. Apa yang saya temukan adalah bahwa saat membuat sambungan aman, android kembali ke SSLv3 dari TLSv1 . Ini adalah bug di versi android <4.4, dan dapat diatasi dengan menghapus protokol SSLv3 dari daftar Protokol yang Diaktifkan. Saya membuat kelas socketFactory khusus yang disebut NoSSLv3SocketFactory.java. Gunakan ini untuk membuat pabrik soket.

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

Gunakan kelas ini seperti ini saat menghubungkan:

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

PERBARUI:

Sekarang, solusi yang benar adalah menginstal penyedia keamanan yang lebih baru menggunakan Layanan Google Play :

    ProviderInstaller.installIfNeeded(getApplicationContext());

Ini secara efektif memberi aplikasi Anda akses ke versi OpenSSL dan Java Security Provider yang lebih baru, yang menyertakan dukungan untuk TLSv1.2 di SSLEngine. Setelah penyedia baru diinstal, Anda dapat membuat SSLEngine yang mendukung SSLv3, TLSv1, TLSv1.1, dan TLSv1.2 dengan cara biasa:

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

Atau Anda dapat membatasi protokol yang diaktifkan menggunakan engine.setEnabledProtocols.

Jangan lupa menambahkan dependensi berikut ( periksa versi terbaru di sini ):

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

Untuk info lebih lanjut, lihat tautan ini .

Bhavit S. Sengar
sumber
25
Bagaimana jika saya mendapatkan kesalahan ini pada perangkat Lollipop?
IgorGanapolsky
2
Saya mendapatkan ini di 5.0.1
Skynet
Pendekatan ini tampaknya gagal saat menghubungkan ke host virtual, karena secara efektif menonaktifkan SNI dengan tidak menyediakan metode SSLCertificateSocketFactory.setHostname (Socket, String). Saya telah mendapatkan 403 di mana openssl.exe dan browser terhubung tanpa masalah. Ternyata SNI itu hilang.
Jaroslav Záruba
2
bagaimana seseorang bisa menggunakan ini dengan voli?
uniruddh
1
Saya mendapatkan kesalahan ini di API Android 19 javax.net.ssl.SSLProtocolException: Kesalahan baca: ssl = 0xb83d7120: Kegagalan di pustaka SSL, biasanya kesalahan protokol
Rukmal Dias
117

Skenario

Saya mendapatkan pengecualian SSLHandshake pada perangkat yang menjalankan versi Android sebelum Android 5.0. Dalam kasus penggunaan saya, saya juga ingin membuat TrustManager untuk mempercayai sertifikat klien saya.

Saya menerapkan NoSSLv3SocketFactory dan NoSSLv3Factory untuk menghapus SSLv3 dari daftar protokol yang didukung klien saya, tetapi saya tidak bisa mendapatkan solusi ini untuk bekerja.

Beberapa hal yang saya pelajari:

  • Pada perangkat yang lebih lama dari Android 5.0, protokol TLSv1.1 dan TLSv1.2 tidak diaktifkan secara default.
  • Protokol SSLv3 tidak dinonaktifkan secara default pada perangkat yang lebih lama dari Android 5.0.
  • SSLv3 bukanlah protokol yang aman dan oleh karena itu diinginkan untuk menghapusnya dari daftar protokol yang didukung klien kami sebelum koneksi dibuat.

Apa yang berhasil untuk saya

Izinkan keamanan Android Providerdiperbarui saat memulai aplikasi Anda.

Penyedia default sebelum 5.0+ tidak menonaktifkan SSLv3. Asalkan Anda memiliki akses ke layanan Google Play, relatif mudah untuk menambal Penyedia keamanan Android dari aplikasi Anda.

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

Jika Anda sekarang membuat OkHttpClient atau HttpURLConnection TLSv1.1 dan TLSv1.2 harus tersedia sebagai protokol dan SSLv3 harus dihapus. Jika klien / koneksi (atau lebih khusus itu SSLContext) itu dijalankan sebelum memanggil ProviderInstaller.installIfNeeded(...)maka perlu diciptakan.

Jangan lupa untuk menambahkan dependensi berikut ( versi terbaru ditemukan di sini ):

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

Sumber:

Ke samping

Saya tidak perlu secara eksplisit mengatur algoritma cipher mana yang harus digunakan klien saya, tetapi saya menemukan posting SO yang merekomendasikan yang dianggap paling aman pada saat penulisan: Cipher Suites mana yang harus diaktifkan untuk SSL Socket?

Maurice Gavin
sumber
1
Saya tidak tahu tentang statistik yang dipublikasikan untuk jumlah perangkat yang menjalankan Layanan Google Play. Angka terbaru yang dapat saya temukan adalah pembicaraan Sundar Pichai IO 2014 di mana dia mengatakan bahwa 93% perangkat Android memiliki versi terbaru dari Layanan Play. Mengenai metode manual, saya sampai pada solusi ini ketika semua metode manual gagal bekerja untuk saya. Jika keamanan jaringan tidak penting bagi pengguna Anda, Anda selalu dapat kembali ke HTTP jika pengguna menolak untuk menginstal Layanan Play. Untuk aplikasi kami sendiri, kami mempertimbangkan trade-off antara kompatibilitas dan keamanan. Kami memberlakukan pembaruan Layanan Play.
Maurice Gavin
3
Dukungan Layanan Google Play di China sangat terbatas. Bisa jadi bagus untuk diketahui tergantung pada target audiens Anda. Tapi yah, jika Anda mendistribusikan melalui Google Play, audiens ini sudah ketinggalan.
jayeffkay
3
Harap tambahkan ketergantungan ini agar solusi berfungsi. kompilasi 'com.google.android.gms: play-services-auth: 10.2.0',
Pradeep Chakravarti Gudipati
3
Menambahkan ProviderInstaller.installIfNeeded (ini); bagian dari Aplikasi onCreate () berhasil untuk saya! Terima kasih :-)
Kelevandos
1
Pasti jawaban yang paling berguna di sini. Terima kasih atas bantuan yang sangat berharga.
Burak Karakuş
50

Anda juga harus tahu bahwa Anda dapat memaksa TLS v1.2 untuk perangkat Android 4.0 yang tidak mengaktifkannya secara default:

Letakkan kode ini di onCreate () dari file Aplikasi Anda :

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
sumber
7
Bekerja untuk saya dengan rxJava OkHttpClient.Builder, terima kasih banyak.
Gweltaz Niquel
2
Bekerja di Android 7.0.
CoolMind
1
terima kasih @mayur gangurde, ini untuk pekerjaan saya setelah menambahkan layanan play auth gradle, implementasi 'com.google.android.gms: play-services-auth: 16.0.1'
Basant
Bekerja dengan sempurna. Terima kasih banyak telah menghemat waktu berjam-jam untuk menguji dan mencoba.
Ninad Desai
Ini sangat nyaman. Terima kasih banyak!
Caspar Geerlings
15

Sebelumnya, saya juga telah memecahkan masalah ini dengan SSLFactoryimplementasi khusus , tetapi menurut dokumen OkHttp solusinya jauh lebih mudah.

Solusi terakhir saya dengan TLScipher yang dibutuhkan untuk 4.2+ perangkat terlihat seperti ini:

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

Perhatikan bahwa kumpulan protokol yang didukung bergantung pada konfigurasi di server Anda.

Pecahan
sumber
Apakah solusi Anda mendukung API 14?
CoolMind
Tidak bisa memastikan, menguji ini hanya pada 16+ perangkat API, berfungsi dengan baik.
Fragmen
1
Fragmen, nama panggilan yang lucu :) Saya setuju, bahkan tidak dapat memulai emulator API 14, tetapi solusi Anda berfungsi pada Android 7.0 (yang memiliki masalah) dan pada API 19.
CoolMind
1
Terima kasih, saya menguji, itu berhasil. Hari ini meluncurkan emulator API 19 lagi dan harus menambahkan solusi stackoverflow.com/a/51285550/2914140 .
CoolMind
12

Saya menemukan solusinya di sini di tautan ini .

Anda hanya perlu menempatkan kode di bawah ini di kelas aplikasi Android Anda. Dan itu sudah cukup. Tidak perlu melakukan perubahan apa pun dalam pengaturan Retrofit Anda. Ini menyelamatkan hari saya.

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

Semoga ini bisa membantu. Terima kasih.

Taz
sumber
tidak bekerja untukku!
morteza moradi
4

Ini menyelesaikannya untuk saya:

Dokumentasi Android untuk SSLSocket menyatakan bahwa TLS 1.1 dan TLS 1.2 didukung dalam Android yang memulai API level 16+ (Android 4.1, Jelly Bean). Tetapi secara default dinonaktifkan tetapi dimulai dengan API level 20+ (Android 4.4 untuk jam tangan, Kitkat Watch dan Android 5.0 untuk ponsel, Lollipop) mereka diaktifkan. Tetapi sangat sulit untuk menemukan dokumentasi apa pun tentang cara mengaktifkannya untuk ponsel yang menjalankan 4.1 misalnya. Untuk mengaktifkan TLS 1.1 dan 1.2 Anda perlu membuat SSLSocketFactory khusus yang akan mem-proxy semua panggilan ke implementasi SSLSocketFactory default. Selain itu, kita harus mengganti semua metode createSocket dan callsetEnabledProtocols di SSLSocket yang dikembalikan untuk mengaktifkan TLS 1.1 dan TLS 1.2. Untuk implementasi contoh, ikuti saja tautan di bawah ini.

android 4.1. aktifkan tls1.1 dan tls 1.2

littlenoname
sumber
2

Saya juga mendapat masalah laporan kesalahan ini. Kode saya ada di bawah.

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

Saya memiliki Springboot sebagai backend dan menggunakan Android OKHttp untuk mendapatkan informasi. Kesalahan kritis yang saya buat adalah saya menggunakan .url ( "https : //10.0.2.2: 8010 / getShopInfo / aaa") dalam kode Android. Tapi backend saya tidak diperbolehkan meminta https. Setelah saya menggunakan .url (" http : //10.0.2.2: 8010 / getShopInfo / aaa") , maka kode saya berjalan dengan baik. Jadi, saya ingin mengatakan kesalahan saya bukanlah versi emulator, ini tentang protokol permintaan. Saya menemui masalah lain setelah melakukan apa yang saya katakan, tetapi itu masalah lain, dan saya melampirkan metode penyelesaian masalah baru .
Semoga berhasil!

X. Profesor
sumber
1

Itu dapat direproduksi hanya ketika saya menggunakan proxy pada genymotion (<4.4).

Periksa pengaturan proxy Anda di Pengaturan-> Nirkabel & Jaringan-> WiFi -> (Tekan Lama WiredSSID) -> Ubah Jaringan

Pilih tampilkan opsi lanjutan: setel setelan Proksi ke TIDAK ADA.

yifan
sumber
1

Ketika saya mendapat kesalahan ini, itu karena protokol (versi TLS) dan / atau cipher suite yang didukung oleh server tidak diaktifkan (dan mungkin bahkan tidak didukung oleh) perangkat. Untuk API 16-19, TLSv1.1 dan TLSv1.2 didukung tetapi tidak diaktifkan secara default. Setelah saya mengaktifkannya untuk versi ini, saya masih mendapatkan kesalahan karena versi ini tidak mendukung cipher apa pun di instans AWS CloudFront kami.

Karena tidak mungkin menambahkan cipher ke Android, kami harus mengganti versi CloudFront kami dari TLSv1.2_2018 ke TLSv1.1_2016 (yang masih mendukung TLSv1.2; itu tidak memerlukannya), yang memiliki empat cipher yang didukung oleh versi Android sebelumnya, dua di antaranya masih dianggap kuat.

Pada saat itu, kesalahan menghilang dan panggilan tetap berjalan (dengan TLSv1.2) karena setidaknya ada satu protokol dan setidaknya satu sandi yang dibagikan perangkat dan server.

Lihat tabel di halaman ini untuk mengetahui protokol dan cipher yang didukung dan diaktifkan pada versi Android mana.

Sekarang apakah Android benar-benar mencoba menggunakan SSLv3 seperti yang tersirat oleh bagian "kegagalan jabat tangan peringatan sslv3" dari pesan kesalahan? Aku meragukan itu; Saya menduga ini adalah jaring laba-laba lama di pustaka SSL yang belum dibersihkan tetapi saya tidak bisa memastikannya.

Untuk mengaktifkan TLSv1.2 (dan TLSv1.1), saya dapat menggunakan yang jauh lebih sederhana SSLSocketFactorydaripada yang terlihat di tempat lain (seperti NoSSLv3SocketFactory). Ini hanya memastikan bahwa protokol yang diaktifkan menyertakan semua protokol yang didukung dan bahwa cipher yang diaktifkan menyertakan semua cipher yang didukung (yang terakhir tidak diperlukan untuk saya tetapi bisa untuk orang lain) - lihat configure()di bagian bawah. Jika Anda lebih suka mengaktifkan hanya protokol terbaru, Anda dapat mengganti socket.supportedProtocolsdengan sesuatu seperti arrayOf("TLSv1.1", "TLSv1.2")(juga untuk cipher):

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
sumber
Bagaimana kamu menggunakannya?
CoolMind
0

Saya memecahkan masalah dengan ini: 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);
        }
    }
}

Kelas utama:

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
sumber
0

Jawaban Saya mendekati jawaban di atas tetapi Anda perlu menulis kelas dengan tepat tanpa mengubah apa pun.

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

}

dan menggunakannya dengan 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();
                    }
                }
            }
Wowo Ot
sumber