I have looked into various posts about how to retrieve something via HTTPS
on Android, from a server that uses a self-signed certificate. However, none of them seem to work - they all fail to remove the
javax.net.ssl.SSLException: Not trusted server certificate message.
It is not an option to modify the server to have a trusted certificate, and it is also not an option to make the server certificate match the server's IP address.
Note, that the server will not have a DNS name, it will only have an IP-address. The GET request looks something like this:
https://username:password@anyIPAddress/blabla/index.php?param=1¶m2=3
I am fully aware that this solution is prone to man-in-the-middle attacks etc.
So, the solution must ignore the lack of trust in the certificate, and ignore the hostname mismatch.
Does anybody know the code, that does this, using Java for Android?
There are plenty of attempts to explain this on stackoverflow.com, and plenty of code snippets, but they don't seem to work, and nobody has provided one block of code that solves this, as far as I can see. It would be interesting to know if somebody really solved this, or if Android simply blocks certificates that are not trusted.
Go to Settings / Security / Credential storage and select “Install from device storage”. The . crt file will be detected and you will be prompted to enter a certificate name. After importing the certificate, you will find it in Settings / Security / Credential storage / Trusted credentials / User.
When using the SSL for non-production applications or other experiments you can use a self-signed SSL certificate. Though the certificate implements full encryption, visitors to your site will see a browser warning indicating that the certificate should not be trusted.
As you correctly point out, there are two issues: a) the certificate isn't trusted, and b) the name on the certificate doesn't match the hostname.
WARNING: for anybody else arriving at this answer, this is a dirty, horrible hack and you must not use it for anything that matters. SSL/TLS without authentication is worse than no encryption at all - reading and modifying your "encrypted" data is trivial for an attacker and you wouldn't even know it was happening.
Still with me? I feared so...
a) is solved by creating a custom SSLContext whose TrustManager accepts anything:
SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(null, new TrustManager[] { new X509TrustManager() { public void checkClientTrusted(X509Certificate[] chain, String authType) {} public void checkServerTrusted(X509Certificate[] chain, String authType) {} public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[]{}; } } }, null); HttpsURLConnection.setDefaultSSLSocketFactory(ctx.getSocketFactory());
and b) by creating a HostnameVerifier which allows the connection to proceed even though the cert doesn't match the hostname:
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } });
Both must happen right at the beginning of your code, before you start messing around with HttpsURLConnections and so on. This works both in Android and the regular JRE. Enjoy.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With