Does Android Volley Support Ssl

Does Android Volley support SSL?

You can refer to my working sample code. Hope this helps!

public class MainActivity extends AppCompatActivity {

private TextView mTextView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mTextView = (TextView) findViewById(R.id.textView);

String url = "https://192.168.1.100/testvolley";

HurlStack hurlStack = new HurlStack() {
@Override
protected HttpURLConnection createConnection(URL url) throws IOException {
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) super.createConnection(url);
try {
httpsURLConnection.setSSLSocketFactory(getSSLSocketFactory());
httpsURLConnection.setHostnameVerifier(getHostnameVerifier());
} catch (Exception e) {
e.printStackTrace();
}
return httpsURLConnection;
}
};

final JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, url, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
try {
mTextView.setText(response.toString(5));
} catch (JSONException e) {
mTextView.setText(e.toString());
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mTextView.setText(error.toString());
}
});

final RequestQueue requestQueue = Volley.newRequestQueue(this, hurlStack);

requestQueue.add(jsonObjectRequest);
}

// Let's assume your server app is hosting inside a server machine
// which has a server certificate in which "Issued to" is "localhost",for example.
// Then, inside verify method you can verify "localhost".
// If not, you can temporarily return true
private HostnameVerifier getHostnameVerifier() {
return new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
//return true; // verify always returns true, which could cause insecure network traffic due to trusting TLS/SSL server certificates for wrong hostnames
HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
return hv.verify("localhost", session);
}
};
}

private TrustManager[] getWrappedTrustManagers(TrustManager[] trustManagers) {
final X509TrustManager originalTrustManager = (X509TrustManager) trustManagers[0];
return new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return originalTrustManager.getAcceptedIssuers();
}

public void checkClientTrusted(X509Certificate[] certs, String authType) {
try {
if (certs != null && certs.length > 0){
certs[0].checkValidity();
} else {
originalTrustManager.checkClientTrusted(certs, authType);
}
} catch (CertificateException e) {
Log.w("checkClientTrusted", e.toString());
}
}

public void checkServerTrusted(X509Certificate[] certs, String authType) {
try {
if (certs != null && certs.length > 0){
certs[0].checkValidity();
} else {
originalTrustManager.checkServerTrusted(certs, authType);
}
} catch (CertificateException e) {
Log.w("checkServerTrusted", e.toString());
}
}
}
};
}

private SSLSocketFactory getSSLSocketFactory()
throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, KeyManagementException {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = getResources().openRawResource(R.raw.my_cert); // this cert file stored in \app\src\main\res\raw folder path

Certificate ca = cf.generateCertificate(caInput);
caInput.close();

KeyStore keyStore = KeyStore.getInstance("BKS");
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);

String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);

TrustManager[] wrappedTrustManagers = getWrappedTrustManagers(tmf.getTrustManagers());

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, wrappedTrustManagers, null);

return sslContext.getSocketFactory();
}
}

IMO, you should also read more at Google's Documentation - Security with HTTPS and SSL

how to increase security while using ssl in android volley

The way that app works is as follows (summarized):

  1. Your app tries to establish a connection to your server
  2. The app captures the request and forwards it
  3. The app captures the answer from the server and replaces its certificate

It's a classic man-in-the-middle attack which works because the certificate of the app you are using is trusted. You cannot prevent rooted users from breaking your encryption.


The only thing you could do is security by obscurity. Try encoding your values (complementary values, or by encrypting them with a key stored in your app) or sending a hash of the entire request. This will lock most people out (the non-technical ones), but there is no absolute way of stopping users from cheating this way. What you could do as well is using one-time tokens (kind of how salts work) to encode your important values. Obfuscating your source code would help as well. But again, there is no absolute way, this is only laying stones in the way of users which might even be able to cheat. It significantly increases the time needed to figure out how to do so.

I wouldn't waste to much time on it, here's why:

  • About 20-25% of Android devices are rooted (according to some older data. I suppose it's actually less)
  • Some rooted devices are test or store devices (Kiosk mode); let that be 15% of all rooted devices
  • If 50% of the other rooted users know about the app (it won't be that high), we are down to 8.5% of all Android users
  • Using the security by obscurity principle only very tech-savvy users will be able to figure how to cheat. If that'd be true for 20% (again, that's a very high number) of all users which fit into the above we are at 1.7% of all Android users.
  • Considering that not all of those users might use your app, some might just not care, I would put the percentage of users which have the ability to cheat AND use your app to 0.5% at max.


A more drastic method would be blocking all rooted users.

Edit: As another answer suggests you could be using public-key pinning. Just remember that not all users tend to update as soon as you publish a new version effectively rendering your app useless if they don't. Thus it comes with a small risk.

android volley verify ssl certificate

Create an HTTP client which can handle the scenario with SSL

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.security.KeyStore;
import java.util.ArrayList;

import javax.net.ssl.HostnameVerifier;

import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;

import com.example.firstapp.R;

public class SimpleHttpClient {
/** The time it takes for our client to timeout */
public static final int HTTP_TIMEOUT = 30 * 1000; // milliseconds

/** Single instance of our HttpClient */
private static HttpClient mHttpClient;

/**
* Get our single instance of our HttpClient object.
*
* @return an HttpClient object with connection parameters set
*/
private static HttpClient getHttpClient() {
if (mHttpClient == null) {
//sets up parameters
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, "utf-8");
params.setBooleanParameter("http.protocol.expect-continue", false);
//registers schemes for both http and https
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", newSslSocketFactory(), 443));
ClientConnectionManager manager = new ThreadSafeClientConnManager(params, registry);
mHttpClient = new DefaultHttpClient(manager, params);
}
return mHttpClient;
}

private static SSLSocketFactory newSslSocketFactory() {
try {
KeyStore trusted = KeyStore.getInstance("BKS");
InputStream in = LoginLayout.getAppContext().getResources().openRawResource(R.raw.keystore);
try {
// Keystore password comes in place of 222222
trusted.load(in, "222222".toCharArray());
} finally {
in.close();
}
/*
* If you use STRICT_HOSTNAME_VERIFIER, the the host name in the URL should match with
* the host name in the server certificate. In this application it is 192.168.1.3
*
* If you do not want to check the host name and simply want to connect to the URL, then use ALLOW_ALL_HOSTNAME_VERIFIER
*
*/
//HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.STRICT_HOSTNAME_VERIFIER;
SSLSocketFactory socketFactory = new SSLSocketFactory(trusted);
socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
return socketFactory;
} catch (Exception e) {
throw new AssertionError(e);
}
}

/**
* Performs an HTTP Post request to the specified url with the
* specified parameters.
*
* @param url The web address to post the request to
* @param postParameters The parameters to send via the request
* @return The result of the request
* @throws Exception
*/
public static String executeHttpPost(String url, ArrayList<NameValuePair> postParameters) throws Exception {
BufferedReader in = null;
try {
HttpClient client = getHttpClient();
HttpPost request = new HttpPost(url);
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(postParameters);
request.setEntity(formEntity);
HttpResponse response = client.execute(request);
in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));

StringBuffer sb = new StringBuffer("");
String line = "";
String NL = System.getProperty("line.separator");
while ((line = in.readLine()) != null) {
sb.append(line + NL);
}
in.close();

String result = sb.toString();
return result;
}
finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

/**
* Performs an HTTP GET request to the specified url.
*
* @param url The web address to post the request to
* @return The result of the request
* @throws Exception
*/
public static String executeHttpGet(String url) throws Exception {
BufferedReader in = null;
try {
HttpClient client = getHttpClient();
HttpGet request = new HttpGet();
request.setURI(new URI(url));
HttpResponse response = client.execute(request);
in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));

StringBuffer sb = new StringBuffer("");
String line = "";
String NL = System.getProperty("line.separator");
while ((line = in.readLine()) != null) {
sb.append(line + NL);
}
in.close();

String result = sb.toString();
return result;
}
finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

Full implementation can be found in this Reference Link

How do I tell the TLS version in Android Volley

You may modify the version of TLS used in Volley by creating a custom HTTPStack and setting the stack in the Volley.newRequestQueue(context, httpStack) method in Volley.java. Although, you only need to do this for Android versions 16-19. Before v16, TLS 1.2 isn't supported and after v19, TLS 1.2 is enabled by default. So, you should focus on manually setting TLS to 1.2 for Android versions 16-19.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
&& Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
try {
ProviderInstaller.installIfNeeded(getContext());
} catch (GooglePlayServicesRepairableException e) {
// Indicates that Google Play services is out of date, disabled, etc.
// Prompt the user to install/update/enable Google Play services.
GooglePlayServicesUtil.showErrorNotification(e.getConnectionStatusCode(), getContext());
// Notify the SyncManager that a soft error occurred.
syncResult.stats.numIOExceptions++;
return;
} catch (GooglePlayServicesNotAvailableException e) {
// Indicates a non-recoverable error; the ProviderInstaller is not able
// to install an up-to-date Provider.
// Notify the SyncManager that a hard error occurred.
syncResult.stats.numAuthExceptions++;
return;
}

HttpStack stack = null;
try {
stack = new HurlStack(null, new TLSSocketFactory());
} catch (KeyManagementException e) {
e.printStackTrace();
Log.d("Your Wrapper Class", "Could not create new stack for TLS v1.2");
stack = new HurlStack();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
Log.d("Your Wrapper Class", "Could not create new stack for TLS v1.2");
stack = new HurlStack();
}
requestQueue = Volley.newRequestQueue(context, stack);
} else {
requestQueue = Volley.newRequestQueue(context);
}

And then use a TLSSocketFactory class which extends SSLSocketFactory like the one Florian Krauthan created here, where the v1.2 TLS protocol is enabled: https://gist.github.com/fkrauthan/ac8624466a4dee4fd02f#file-tlssocketfactory-java

Android Volley SSL error

SOLVED : what looked like an SSL error was in fact a timeout of the connection.

jsonRequest.setRetryPolicy(new DefaultRetryPolicy(10000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));

setting the request at 10 seconds and MAX_RETIES did the trick as seen in :

Volley SSL Handshake Exception although has removed SSL3 protocol



Related Topics



Leave a reply



Submit