title |
Date |
Categories |
Tags |
Android 5.0 below tls1.x sslhandshakeexception |
2016-11-30 12:17:02-0800 |
|
|
Recently, all of the app's requests to HTTPS, in the test, some phone found that the request failed, the failure of the exception information as follows:
Javax.net.ssl.SSLHandshakeException:javax.net.ssl.SSLProtocolException:SSL Handshake Aborted:ssl=0x783e8e70: Failure in SSL Library, usually a protocol Errorerror:14077102:ssl routines:SSL23_GET_SERVER_HELLO:unsupported protocol (external/openssl/ssl/s23_clnt.c:714 0x71a20cf8:0x00000000)
The exception for the handshake failed, but why some mobile phone can be successful and the phone failed again, first check our server interface TLS support version of 1.x, and later found that the failure of the phone is 5.x below the version, presumably should be related to this, and then consult the official documents, The corresponding tables in Sslsocket that refer to the TLS version and the Android SDK version are as follows:
Protocol |
supported (API levels) |
Enabled By default (API levels) |
SSLv3 |
1 + |
1 + |
TLSv1 |
1 + |
1 + |
TLSv1.1 |
16+ |
20+ |
TLSv1.2 |
16+ |
20+ |
As seen from this table, tlsv1.x (1.1,1.2) Android is supported by default from API16, and is available by default from API20, which explains why the phones below 5.x failed when they made the request.
Know the cause of the problem, we have to solve, of course, the server can support the TLSV1 version, so we can all request success, but this is not the best solution, we certainly want to let our app support the new TLS protocol.
Cipher suites
There are also api20+ support or default available through official documentation, so if we want to support the tlsv1.x version, we may need to add the lower version, Cipher suites
so we need to customize the sslsocketfactory, The custom sslsocketfactory are as follows:
Public classSslextendsSslsocketfactory {Privatesslsocketfactory defaultfactory; //Android 5.0+ (API level21) provides reasonable default settings//But it still allows SSLv3// https://developer.android.com/about/versions/android-5.0-changes.html#ssl StaticString protocols[] =NULL, ciphersuites[] =NULL; Static { Try{sslsocket Socket=(Sslsocket) Sslsocketfactory.getdefault (). Createsocket (); if(Socket! =NULL) { /*set Reasonable protocol versions*/ //-Enable all supported protocols (enables TLSv1.1 and TLSv1.2 on Android <5.0)//-Remove all SSL versions (especially SSLv3) because they ' re insecure nowlist<string> protocols =NewLinkedlist<>(); for(String protocol:socket.getSupportedProtocols ())if(!protocol.touppercase (). Contains ("SSL") ) protocols.add (protocol); Ssl.protocols= Protocols.toarray (Newstring[protocols.size ()]); /*set up reasonable cipher suites*/ if(Build.VERSION.SDK_INT <Build.version_codes. LOLLIPOP) {//Choose known secure cipher suitesList<string> allowedciphers =Arrays.aslist (//TLS 1.2"tls_rsa_with_aes_256_gcm_sha384", "Tls_rsa_with_aes_128_gcm_sha256", "Tls_ecdhe_ecdsa_with_aes_128_cbc_sha256", "Tls_ecdhe_ecdsa_with_aes_128_gcm_sha256", "tls_ecdhe_ecdsa_with_aes_256_gcm_sha384", "Tls_ecdhe_rsa_with_aes_128_cbc_sha256", "Tls_echde_rsa_with_aes_128_gcm_sha256", //Maximum Interoperability"Tls_rsa_with_3des_ede_cbc_sha", "Tls_rsa_with_aes_128_cbc_sha", //additionally"Tls_rsa_with_aes_256_cbc_sha", "Tls_ecdhe_ecdsa_with_3des_ede_cbc_sha", "Tls_ecdhe_ecdsa_with_aes_128_cbc_sha", "Tls_ecdhe_rsa_with_3des_ede_cbc_sha", "Tls_ecdhe_rsa_with_aes_128_cbc_sha"); List<String> availableciphers =arrays.aslist (Socket.getsupportedciphersuites ()); //Take all allowed ciphers that is available and put them into preferredciphersHashset<string> preferredciphers =NewHashset<>(allowedciphers); Preferredciphers.retainall (availableciphers); /*for maximum security, preferredciphers should *replace* enabled ciphers (thus disabling * ciphers Which is enabled by default, but has become unsecure), but I guess for * The security level of DAVD Roid and maximum compatibility, disabling of insecure * ciphers should be a server-side task*/ //add preferred ciphers to Enabled ciphersHashset<string> enabledciphers =preferredciphers; Enabledciphers.addall (NewHashset<>(Arrays.aslist (Socket.getenabledciphersuites ()))); Ssl.ciphersuites= Enabledciphers.toarray (Newstring[enabledciphers.size ()]); } } } Catch(IOException e) {Throw NewRuntimeException (e); } } PublicSSL (X509trustmanager tm) {Try{sslcontext Sslcontext= Sslcontext.getinstance ("TLS"); Sslcontext.init (NULL, (tm! =NULL) ?NewX509TRUSTMANAGER[]{TM}:NULL,NULL); Defaultfactory=sslcontext.getsocketfactory (); } Catch(generalsecurityexception e) {Throw NewAssertionerror ();//The system has no TLS. Just give up. } } Private voidUpgradetls (Sslsocket SSL) {//Android 5.0+ (API level21) provides reasonable default settings//But it still allows SSLv3// https://developer.android.com/about/versions/android-5.0-changes.html#ssl if(Protocols! =NULL) {ssl.setenabledprotocols (protocols); } if(Build.VERSION.SDK_INT < Build.version_codes. LOLLIPOP && Ciphersuites! =NULL) {ssl.setenabledciphersuites (ciphersuites); }} @Override Publicstring[] Getdefaultciphersuites () {returnciphersuites; } @Override Publicstring[] Getsupportedciphersuites () {returnciphersuites; } @Override PublicSocket Createsocket (socket s, String host,intPortBooleanAutoClose)throwsIOException {Socket SSL=Defaultfactory.createsocket (S, host, Port, AutoClose); if(SSLinstanceofsslsocket) Upgradetls ((sslsocket) SSL); returnSSL; } @Override PublicSocket Createsocket (String host,intPortthrowsIOException, unknownhostexception {Socket SSL=Defaultfactory.createsocket (host, Port); if(SSLinstanceofsslsocket) Upgradetls ((sslsocket) SSL); returnSSL; } @Override PublicSocket Createsocket (String host,intPort, InetAddress LocalHost,intLocalPort)throwsIOException, unknownhostexception {Socket SSL=Defaultfactory.createsocket (host, Port, LocalHost, LocalPort); if(SSLinstanceofsslsocket) Upgradetls ((sslsocket) SSL); returnSSL; } @Override PublicSocket Createsocket (inetaddress host,intPortthrowsIOException {Socket SSL=Defaultfactory.createsocket (host, Port); if(SSLinstanceofsslsocket) Upgradetls ((sslsocket) SSL); returnSSL; } @Override PublicSocket Createsocket (inetaddress address,intPort, InetAddress localaddress,intLocalPort)throwsIOException {Socket SSL=Defaultfactory.createsocket (address, Port, localaddress, LocalPort); if(SSLinstanceofsslsocket) Upgradetls ((sslsocket) SSL); returnSSL; }}
Then we just need to set this sslsocketfactory on our request, we take okhttp as an example, as follows:
//define a TrustManager that trusts all certificatesFinalX509trustmanager Trustallcert =NewX509trustmanager () {@Override Public voidCheckclienttrusted (java.security.cert.x509certificate[] chain, String authtype)throwscertificateexception {} @Override Public voidCheckservertrusted (java.security.cert.x509certificate[] chain, String authtype)throwscertificateexception {} @Override Publicjava.security.cert.x509certificate[] Getacceptedissuers () {return Newjava.security.cert.x509certificate[]{}; }};//Set OkhttpclientOkhttpclient client =NewOkhttpclient.builder (). Sslsocketfactory (NewSSL (Trustallcert), Trustallcert). Build ();
After setting up, test HTTPS with a lower version of your phone and you can now test successfully.
Android uses HTTPS problem Resolution (sslhandshakeexception)