Recently for one of my Android apps I wanted to use a self-signed certificate on the server-side. However, if you try to connect to such a server with default settings, the connection is going to be refused. This is because it has been signed by you (your server domain), and ‘you’ is not trusted by any system.
Read the excellent article – Android security – Implementation of Self-signed SSL certificate for your App. This describes all the concepts and the pros and cons of pinning. However, it does not describe how to do this in Volley. The internet is littered with many articles on this, and most of them are outdated and can’t be used now.
So, here is an updated guide. I have tested this on my app which uses Android API 22 and Volley code downloaded on Jan 2015.
First use the official guide to create a singleton class to get the request queue. Now below is the modified code which takes care of the SSL pinning.
public class MySingleton {
private static char[] KEYSTORE_PASSWORD = "YourKeyStorePass".toCharArray();
...
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
// getApplicationContext() is key, it keeps you from leaking the
// Activity or BroadcastReceiver if someone passes one in.
mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext(), new HurlStack(null, newSslSocketFactory()));
}
return mRequestQueue;
}
...
private SSLSocketFactory newSslSocketFactory() {
try {
// Get an instance of the Bouncy Castle KeyStore format
KeyStore trusted = KeyStore.getInstance("BKS");
// Get the raw resource, which contains the keystore with
// your trusted certificates (root and any intermediate certs)
InputStream in = mCtx.getApplicationContext().getResources().openRawResource(R.raw.codeprojectssl);
try {
// Initialize the keystore with the provided trusted certificates
// Provide the password of the keystore
trusted.load(in, KEYSTORE_PASSWORD);
} finally {
in.close();
}
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(trusted);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
SSLSocketFactory sf = context.getSocketFactory();
return sf;
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
Please note, that the above solution will work for Android API 9 and above. To support the below versions you need to pass an instance of HttpClientStack
instead of HurlStack
. So, replacing the line
mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext(), new HurlStack(null, newSslSocketFactory()));
with
mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext(), new HttpClientStack(new MyHttpClient()));
might work. Where MyHttpClient
is the one defined in the CodeProject article. I haven’t tested this part though.
Hi,
Thank you for the great tutorial.
I’ve tried to follow your code, but I get the error
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
The error is returned to my Android application at the onErrorResponse of Volley.
I still don’t know why.
This error means that according to your app’s trust store the SSL certificate is untrusted since it is signed by you. If you followed the Implementation steps mentioned in the article linked in this post (http://www.codeproject.com/Articles/826045/Android-security-Implementation-of-Self-signed-SSL), then this error should not come. The aim of this post is that your app uses the trust store provided by you, which accepts your self-signed certificate as valid one.
[…] 详见这篇文章,这里不在重复. […]
Hi i’m getting the following error:
java.io.IOException: Hostname ‘54.201.163.133’ was not verified Cause: java.io.IOException: Hostname ‘54.201.163.133’ was not verified
Rading says that i need to use a host name verifier but in the examples people use SSLSocketFactory from apache which has something like:
final SSLSocketFactory sf = CustomSSLSocketFactory.getSSLSocketFactory(context);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
How can i accomplish this with your code?
It is because you are using an IP address instead of a host name. If the IP you provided do not have a host name assigned then you need to override this check too. See: http://stackoverflow.com/questions/14619781/java-io-ioexception-hostname-was-not-verified
And goole is your friend, you could have simply googled this out.
[…] I’m trying to use a self-signed certificate on Node.Js server (using express) and Volley on android. Using : http://blog.applegrew.com/2015/04/using-pinned-self-signed-ssl-certificate-with-android-volley/ […]
How can I know MYKEYSTORE PASSWOED??
[…] I’m trying to use a self-signed certificate on Node.Js server (using express) and Volley on android. Using : http://blog.applegrew.com/2015/04/using-pinned-self-signed-ssl-certificate-with-android-volley/ […]
[…] I’m trying to use a self-signed certificate on Node.Js server (using express) and Volley on android. Using : http://blog.applegrew.com/2015/04/using-pinned-self-signed-ssl-certificate-with-android-volley/ […]