I was attempting to switch from a self-signed certificate to a well known CA (by Let's Encrypted). The original code below works well to connect from the Android app to the server by first passing a JSON e.g. {"name": "xxx"}, while the server echoes the corresponding data.
try {
URL url = new URL(intent.getStringExtra(URL));
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(buildSslSocketFactory());
conn.setConnectTimeout(15000);
conn.setRequestMethod("POST");
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setChunkedStreamingMode(0);
conn.setRequestProperty("Accept", "application/json");
conn.setRequestProperty("Content-Type", "application/json");
conn.connect();
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
Log.d(TAG,data +"sending to " + actStr(action) );
writer.write(data);
writer.close();
int respC = conn.getResponseCode();
if (respC < 200 || respC >= 400 ) { // OK is 200-399
Log.e(TAG, "sending to " + actStr(action) + ", got response code " + respC + "=" + conn.getResponseMessage());
conn.disconnect();
return;
}
reader = new BufferedReader( new InputStreamReader( conn.getInputStream()));
makeStr = new StringBuilder();
data = "";
while ((data = reader.readLine()) != null) {
makeStr.append(data);
Log.d(TAG, "read"+data);
}
conn.disconnect();
} catch (Exception e) {
Log.d(TAG, "Failed sending to " + actStr(action) + ", cause: " + e.getCause() + " message: " + e.getMessage());
} finally {
if (reader != null) {
try {
makeStr.append("");
reader.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
It comes together with a standard SslSocketFactory:
private static SSLSocketFactory buildSslSocketFactory() {
// Add support for self-signed (local) SSL certificates
// Based on http://developer.android.com/training/articles/security-ssl.html#UnknownCa
try {
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// From https://www.washington.edu/itconnect/security/ca/load-der.crt
InputStream is = App.getAppResources().getAssets().open(ManageActivity.certificate);
InputStream caInput = new BufferedInputStream(is);
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
//System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
Log.d(TAG,"ca=" +((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
return sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
So, as to switch to site with trusted certificate, I keep the above code the same, but changing the certificate, which is a crt with the combination of intermediate certificate + site certificate according to nKn's answer here: Android: how to do HttpPost with a certificate (I have actually tried on all the combinations among the root, intermediate and site certificates, and test on individual cert separately. But none of them worked). Unlike questions from the others, I don't get any errors saying I have an untrusted certificate or sth.
From the server site, it seems to receive a post request (I got a message from the error log), but there is nothing from the input, and therefore the app doesn't get any response from the server too. However, compared to self-signed certificate setting, codes from the server site are completely the same, plus when I pass the same JSON from the terminal with curl, it responses what I want, so I doubt if it is the certificate issue.
Does anyone know how to solve it, or I actually do not have to include the certificate or something?