Popular Posts
Enable SSL connection for Jsoup import org.jsoup.Connection; import org.jsoup.Jsoup; import javax.net.ssl.*; import java.io.IOException; import java.security.KeyManagement... Tired of Hibernate? Try JDBI in your code JDBI Quick sample ICategoryDAO.java : create a data access interface (implement is not required) package com.prhythm.erotic.task.data.... Simple Web snapshot import java.util.Date; import org.eclipse.swt.SWT; import org.eclipse.swt.browser.Browser; import org.eclipse.swt.graphics.GC; import org.e...
Stats
javax.net.ssl.SSLHandshakeException: Connection closed by peer in Android 5.0 Lollipop

Recently, there is a error occurs when access website via ssl connection like below although it worked fine several days ago.

// Enable SSL connection
HttpsURLConnection.setDefaultSSLSocketFactory(new MySocketFactory1());
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
    public boolean verify(String hostname, SSLSession session) {
        return true;
    }
});

URL address = new URL(url);
HttpsURLConnection urlConnection = (HttpsURLConnection) address.openConnection();
InputStream in = urlConnection.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[128];
int len = 0;
while ((len = in.read(buffer)) > 0) {
    baos.write(buffer, 0, len);
}
baos.close();
in.close();
MySocketFactory1.java
package com.prhythm.google.myapplication2.app;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * Created by nanashi07 on 15/5/23.
 */
public class MySocketFactory1 extends SSLSocketFactory {

    SSLContext context;
    SSLSocketFactory factory;

    public MySocketFactory1() {
        try {
            init();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }

    void init() throws KeyManagementException, NoSuchAlgorithmException {
        context = SSLContext.getInstance("SSL");
        context.init(null, new X509TrustManager[]{new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        }}, new SecureRandom());
        factory = context.getSocketFactory();
    }

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

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

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

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

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

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

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        return factory.createSocket(address, port, localAddress, localPort);
    }
}
javax.net.ssl.SSLHandshakeException: Connection closed by peer
05-24 07:54:11.626    1697-1716/com.prhythm.google.myapplication2.app E/Test﹕ error
    javax.net.ssl.SSLHandshakeException: Connection closed by peer
            at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
            at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:302)
            at com.android.okhttp.Connection.upgradeToTls(Connection.java:197)
            at com.android.okhttp.Connection.connect(Connection.java:151)
            at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:276)
            at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:211)
            at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:373)
            at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:323)
            at com.android.okhttp.internal.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:190)
            at com.android.okhttp.internal.http.DelegatingHttpsURLConnection.getInputStream(DelegatingHttpsURLConnection.java:210)
            at com.android.okhttp.internal.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:25)
            at com.prhythm.google.myapplication2.app.MainActivity$1.doInBackground(MainActivity.java:41)
            at com.prhythm.google.myapplication2.app.MainActivity$1.doInBackground(MainActivity.java:26)
            at android.os.AsyncTask$2.call(AsyncTask.java:288)
            at java.util.concurrent.FutureTask.run(FutureTask.java:237)
            at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
            at java.lang.Thread.run(Thread.java:818)

After track and search from some technical articles, I found this TLS/SSL Default Configuration Changes, finally. Base on this, I've made some changes for my SSLSocketFactory to add cipher suites setting. Now it works fine again.

MySocketFactory2.java
package com.prhythm.google.myapplication2.app;

import com.prhythm.core.generic.util.Cube;

import javax.net.ssl.*;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Set;

/**
 * Created by nanashi07 on 15/5/23.
 */
public class MySocketFactory2 extends SSLSocketFactory {

    SSLContext context;
    SSLSocketFactory factory;
    String type;

    public MySocketFactory2() {
        try {
            init();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }

    void init() throws KeyManagementException, NoSuchAlgorithmException {
        context = SSLContext.getInstance(type = Cube.from("SSL", "TLS").random());
        context.init(null, new TrustManager[]{new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                System.out.printf("[CLIENT] chain = %s, authType = %s%n", Arrays.toString(chain), authType);
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                System.out.printf("[SERVER] chain = %s, authType = %s%n", Arrays.toString(chain), authType);
            }

            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        }}, new SecureRandom());
        factory = context.getSocketFactory();
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return getCipherSuites().toArray(String.class);
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return getCipherSuites().toArray(String.class);
    }

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

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

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

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

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

    Cube<String> getCipherSuites() {
        return Cube.from("SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
                "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
                "SSL_DHE_DSS_WITH_DES_CBC_SHA",
                "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
                "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
                "SSL_DHE_RSA_WITH_DES_CBC_SHA",
                "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
                "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
                "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA",
                "SSL_DH_anon_WITH_DES_CBC_SHA",
                "SSL_DH_anon_WITH_RC4_128_MD5",
                "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
                "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
                "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
                "SSL_RSA_WITH_DES_CBC_SHA",
                "SSL_RSA_WITH_NULL_MD5",
                "SSL_RSA_WITH_NULL_SHA",
                "SSL_RSA_WITH_RC4_128_MD5",
                "SSL_RSA_WITH_RC4_128_SHA",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_DH_anon_WITH_AES_128_CBC_SHA",
                "TLS_DH_anon_WITH_AES_128_CBC_SHA256",
                "TLS_DH_anon_WITH_AES_128_GCM_SHA256",
                "TLS_DH_anon_WITH_AES_256_CBC_SHA",
                "TLS_DH_anon_WITH_AES_256_CBC_SHA256",
                "TLS_DH_anon_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_NULL_SHA",
                "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
                "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_RSA_WITH_NULL_SHA",
                "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
                "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
                "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDH_ECDSA_WITH_NULL_SHA",
                "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
                "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
                "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDH_RSA_WITH_NULL_SHA",
                "TLS_ECDH_RSA_WITH_RC4_128_SHA",
                "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA",
                "TLS_ECDH_anon_WITH_AES_128_CBC_SHA",
                "TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
                "TLS_ECDH_anon_WITH_NULL_SHA",
                "TLS_ECDH_anon_WITH_RC4_128_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV",
                "TLS_FALLBACK_SCSV",
                "TLS_PSK_WITH_3DES_EDE_CBC_SHA",
                "TLS_PSK_WITH_AES_128_CBC_SHA",
                "TLS_PSK_WITH_AES_256_CBC_SHA",
                "TLS_PSK_WITH_RC4_128_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_RSA_WITH_NULL_SHA256")
                .where(new Cube.Selection<String>() {
                    @Override
                    public boolean predicate(String item, int index) {
                        return item.startsWith(type);
                    }
                });
    }

    Socket configCipherSuites(Socket socket) {
        if (!(socket instanceof SSLSocket)) return socket;
        SSLSocket sslsocket = (SSLSocket) socket;
        // 處理不合的cipher suite
        Set<String> suites = getCipherSuites().toSet();
        while (suites.size() > 0) {
            try {
                sslsocket.setEnabledCipherSuites(suites.toArray(new String[suites.size()]));
                break;
            } catch (Throwable e) {
                String message = e.getMessage();
                for (String cipher : suites) {
                    if (message.toLowerCase().contains(cipher.toLowerCase())) {
                        suites.remove(cipher);
                        System.out.printf("Cipher suite %s has been removed.%n", cipher);
                        break;
                    }
                }
            }
        }
        return socket;
    }
}