Duplication: There are a few similar questions (How Can I Access an SSL Connection Through Android?, "Trust anchor for certification path not found" in Android SSL Socket client, Trust anchor for certification path not found using SSL) with no applicable answers to my issue. I try to give a specific context (e.g. paho and specific test sites) and problem.
Main Issue: I'm using Android Paho, as client, and everything's fine. Now I want to add SSL connection. The broker is the certified entity, and sends its CA-issued certificate during handshake. (Please note that I'm a security newbie, I just read a few tutorials on the subject.)
Most Java and Paho examples I can find deal with having a local certificate (self-issued, or less known CA, etc.), while I need instead to manage CA-issued certificates sent to my app by the broker server during handshake.
From Android documentation (https://developer.android.com/training/articles/security-ssl.html), the idea seems to be that you just have to use SSLSocketFactory.getDefault()
, since root CA certifications for well-known CAs are included with the system. So, simply:
MqttConnectOptions options;
// ...
options.setSocketFactory(SSLSocketFactory.getDefault());
But I tested on both:
- ssl://iot.eclipse.org:8883
- ssl://test.mosquitto.org:8883
and I always got the dreaded javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
The possible reasons given by the aforementioned android documentation, and Stack Overflow answers, presumably don't apply to such well-known and used test sites. I presume that both sites have updated certificates, issued by well-known Certification Authorities, with no lesser known CAs in the chain. (While I cannot find clear confirmation, usage examples around the net do seem to imply it.)
Any pointers? I really have no idea where to go from here (and I presume I'm missing something obvious that it's not clearly stated.)
Secondary Issue: Furthermore, the Android documentation warns that SSLSocket
doesn't do any Hostname Verification: https://developer.android.com/training/articles/security-ssl.html#WarningsSslSocket
And hence it suggests:
SocketFactory sf = SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket) sf.createSocket("gmail.com", 443);
HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
SSLSession s = socket.getSession();
// Verify that the certicate hostname is for mail.google.com
// This is due to lack of SNI support in the current SSLSocket.
if (!hv.verify("mail.google.com", s)) {
throw new SSLHandshakeException("Expected mail.google.com, "
"found " + s.getPeerPrincipal());
}
Please note that you need a reference to the actual socket to pass to verify()
(no overloaded alternative), you cannot just configure the SSLSocketFactory
.
Paho doesn't seem to expose access to the socket it's using, not to set a custom verifier. You can only set the SSLSocketFactory
.
If I understand correctly, there seem to be a Paho bug reported about it: https://bugs.eclipse.org/bugs/show_bug.cgi?id=425195
Is it actually the same issue?
Finally, if so, is there any way to work around this and actually have Android Paho over SSL with Hostname Verification?
EDIT, Partial answer / findings: I record here my partial provisional findings.
My assumption about iot.eclipse.org:8883
and test.mosquitto.org:8883
seems to be wrong: I found doc for both that provide certificates to explicitly be used by the clients (I presume self-signed, it's not specified). Some examples omitting this misled me.
- ssl://iot.eclipse.org:8883 : iot.eclipse.org.crt as indicated by http://iot.eclipse.org/getting-started
- ssl://test.mosquitto.org:8883 : mosquitto.org.crt as indicated by http://test.mosquitto.org/
Loading the certificates in a custom TrustManager
, everything work. Mystery solved on this point. Our own broker with CA certificates isn't ready yet to be tested.