SSL Pinning Failed in Android
Your SSL Pinning implementation looks mostly correct, but if it’s suddenly failing, there are a few key areas to check and fix.

✅ 1. Certificate Mismatch (Most Common Issue)
- If your server recently renewed or changed its SSL certificate, the old pinned certificate in
res/raw/certificate.pem
no longer matches. - Fix: Get the latest certificate from your server and replace the
certificate.pem
file.
How to Get the Latest Certificate?
Run this command in a terminal (replace yourdomain.com
):
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com | openssl x509 -outform PEM > certificate.pem
Then, update your res/raw/certificate.pem
file.
2. Incorrect Certificate File Format
- Ensure your certificate file is in PEM format and stored in
res/raw/certificate.pem
. - Fix: Convert DER to PEM format using:
openssl x509 -inform DER -in certificate.der -out certificate.pem
3. Check PinningHurlStack
Implementation
🔹 Replace Your PinningHurlStack.java
With This Version:
import android.content.Context;
import com.android.volley.toolbox.HurlStack;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
public class PinningHurlStack extends HurlStack { private final Context context;
public PinningHurlStack(Context context) {
this.context = context;
} @Override
protected HttpURLConnection createConnection(URL url) throws java.io.IOException
{
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
if (connection instanceof javax.net.ssl.HttpsURLConnection)
{
((javax.net.ssl.HttpsURLConnection)
connection).setSSLSocketFactory(getPinnedSSLSocketFactory());
}
return connection;
} private SSLSocketFactory getPinnedSSLSocketFactory() {
try {
// Load certificate from res/raw/certificate.pem
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = context.getResources().openRawResource(R.raw.certificate);
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
} finally {
caInput.close();
} // Create a KeyStore and add the certificate
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the KeyStore
TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
// Create an SSLContext that uses the TrustManager
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
return sslContext.getSocketFactory();
} catch (Exception e) {
throw new RuntimeException("SSL Pinning Failed: " + e.getMessage(), e);
}
}
}
✔ Fixes Issues:
- Ensures proper certificate loading.
- Uses better error handling (
throw new RuntimeException
helps debugging). - Properly closes input streams to prevent leaks.
4. Fix VolleySingleton
🔹 Update VolleySingleton.java
to Ensure Secure Requests
import android.content.Context;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;
public class VolleySingleton {
private static VolleySingleton instance;
private RequestQueue requestQueue;
private final Context context; private VolleySingleton(Context context) {
this.context = context.getApplicationContext();
requestQueue = Volley.newRequestQueue(context, new PinningHurlStack(context));
} public static synchronized VolleySingleton getInstance(Context context) {
if (instance == null) {
instance = new VolleySingleton(context);
}
return instance;
} public RequestQueue getRequestQueue() {
return requestQueue;
}
}
✔ Ensures all requests go through PinningHurlStack
.
5. Improve NetworkRequest
Handling
🔹 Fix NetworkRequest.java
import android.content.Context;
import android.widget.Toast;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
public class NetworkRequest {
public void makePinnedRequest(Context context, NetworkCallbackActivity callback)
{
StringRequest stringRequest = new StringRequest(Request.Method.GET,
URL_SSL_PINNING_CERTIFICATE,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
System.out.println("Response: " + response);
callback.onSuccess();
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
System.err.println("SSL Pinning Error: " +
(error.getMessage() != null ? error.getMessage() : "Unknown error"));
error.printStackTrace();
callback.onFailure("SSL Pinning Failed!");
Toast.makeText(context, "SSL Pinning Failed!",
Toast.LENGTH_SHORT).show();
}
}); // Add to secure request queue
VolleySingleton.getInstance(context).getRequestQueue().add(stringRequest);
}
}
✔ Ensures error messages are properly handled and logged.
Final Checklist
✅ 1. Is the Certificate Updated?
- Run:
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com | openssl x509 -outform PEM > certificate.pem
- Replace old
res/raw/certificate.pem
file.
✅ 2. Is the Certificate in PEM Format?
- Run:
openssl x509 -inform DER -in certificate.der -out certificate.pem
✅ 3. Are You Using the Correct TrustManager & SSL Context?
- Implement the updated
PinningHurlStack.java
.
✅ 4. Are You Using the Correct Singleton Pattern?
- Ensure
VolleySingleton.java
properly initializes requests with SSL Pinning.
✅ 5. Debugging?
- If SSL Pinning still fails, log the stack trace using:
error.printStackTrace();
Check for javax.net.ssl.SSLHandshakeException
—this confirms a certificate mismatch.
Summary
If SSL Pinning suddenly fails, it’s 99% a certificate issue.
- First, update the pinned certificate (Step 1).
- Second, confirm PEM format (Step 2).
- Third, use correct SSL initialization (Steps 3 & 4).
After these fixes, SSL Pinning will work securely again!