Reputation: 737
Sorry for my english. I try using libruary OKhttp, and i use https for post reqest. Now i have error, when i try post my example, this is error:
java.net.UnknownServiceException: Unable to find acceptable protocols. isFallback=false, modes=[ConnectionSpec(cipherSuites=[TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA], tlsVersions=[TLS_1_2], supportsTlsExtensions=true)], supported protocols=[SSLv3, TLSv1]
I try fix it, but i cant do this. I dont know what i have this error
And bellow my code:
public class PostOKhttp extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String...ulr) {
Response response = null;
OkHttpClient client = new OkHttpClient();
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.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)
.build();
client.setConnectionSpecs(Collections.singletonList(spec));
RequestBody postForm = new FormEncodingBuilder()
.add("name", "name")
.build();
Request request = new Request.Builder()
.url(ulr[0])
.addHeader("id", "--")
.addHeader("key", "--")
.post(postForm)
.build();
try {
response = client.newCall(request).execute();
Log.e("post", response.body().string());
} catch (Exception e) {
Log.e("error", e.toString());
}
return null;
}
@Override
protected void onPostExecute(String result) {
}
UDP:
Use CertificatePinner
i add this code
String link = "example.net";
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(link, "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=")
.add(link, "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=")
.add(link, "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=")
.add(link, "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=")
.build();
client.setCertificatePinner(certificatePinner);
Now i have this error:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
Upvotes: 9
Views: 21059
Reputation: 525
You need to use ProviderInstaller
to check and install if needed TLS support. Use something like this on your entry Activity
and it should do the trick. Check more info here
protected void checkTls() {
if (android.os.Build.VERSION.SDK_INT < 21) {
try {
ProviderInstaller.installIfNeededAsync(this, new ProviderInstaller.ProviderInstallListener() {
@Override
public void onProviderInstalled() {
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null, null);
SSLEngine engine = sslContext.createSSLEngine();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
}
@Override
public void onProviderInstallFailed(int i, Intent intent) {
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
Upvotes: 6
Reputation: 3268
Actually the problem is TLSv1.1 and TLSv1.2 not enabled on Android <5 by default and to connect using these latest secure protocol we must have to enable in Android <5 devices.
Because by default android device pick the highest supported protocol to establish the connection but the highest/ newest secure protocol (eg. TLSV1.1 or TLSV1.2) are not enable by default (only enabled are SSLV3.0 or TLSV1.0).
Enabling the TLSV1.1 and TLSV1.2 in android < 5
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 javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
/**
* @author Bajrang Hudda
*/
public class MyTLSSocketFactory extends SSLSocketFactory {
private SSLSocketFactory internalSSLSocketFactory;
public MyTLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null);
internalSSLSocketFactory = context.getSocketFactory();
}
@Override
public String[] getDefaultCipherSuites() {
return internalSSLSocketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return internalSSLSocketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket() throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket());
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.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;
}
}
And now add it in your Okhttpclient -
protected static OkHttpClient getHttpClient(long timeout){
String hostname = Constants.HOST_NAME_DEBUG;
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha1/mBN/TTGneHe2Hq0yFG+SRt5nMZQ=")
.add(hostname, "sha1/6CgvsAgBlX3PYiYRGedC0NZw7ys=")
.build();
//specifying the specs; this is impotent otherwise android <5 won't work
//And do note to include the android < 5 supported specs.
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_1, TlsVersion.TLS_1_2)
.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_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
.build();
final OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setCertificatePinner(certificatePinner);
okHttpClient.setConnectionSpecs(Collections.singletonList(spec));
try
{
// enabling the tlsv1.1 and tlsv.2
okHttpClient.setSslSocketFactory(new MyTLSSocketFactory());
} catch (KeyManagementException e)
{
e.printStackTrace();
} catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
return okHttpClient;
}
And now finally add it in your retrofit -
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(Constants.API_URL)
.setLogLevel(RestAdapter.LogLevel.FULL)
.setErrorHandler(new ErrorHandler())
.setClient(getHttpClient())
.setRequestInterceptor(new SecureHeaderInterceptor(null))
.build();
That's it, Happy Coding :-)
Upvotes: 13