I'm using the Apache HttpClient library to setup a https connection. Unfortunately Android gives me a "Not trusted server certificate" error. If I browse to the site with the phone's browser it validates the certificate correctly, which leads me to believe that I need to make the HttpClient 'aware' of the root certificates on the phone. This is my HttpClient setup code:
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout( params, 20000 );
HttpConnectionParams.setSoTimeout( params, 20000 );
HttpProtocolParams.setVersion( params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset( params, HTTP.DEFAULT_CONTENT_CHARSET);
HttpProtocolParams.setUseExpectContinue( params, false);
SchemeRegistry schReg = new SchemeRegistry();
schReg.register( new Scheme( "http", PlainSocketFactory.getSocketFactory(), 80 ) );
schReg.register( new Scheme( "https", SSLSocketFactory.getSocketFactory(), 443 ) );
ClientConnectionManager conMgr = new ThreadSafeClientConnManager( params, schReg );
DefaultHttpClient defaultHttpClient = new DefaultHttpClient( conMgr, params );
return ( defaultHttpClient );
As you can see I do nothing special with the SSLSocketFactory. How can I make the HttpClient library validate my site without adding a custom certificate in the keystore?. Should I create a custom SSLSocketFactory and load the cacerts.bks from the Android phone? In that case I might get problems with different passwords for the keystore on different phones?
Please let me know if you need further information. This SSL stuff is pretty difficult for me.
I believe your certificate doesn't contain all intermediate certificates necessary to validate path to a system trusted root certificate. It can be reported as incomplete certificate chain by some SSL config validation tools.
A certificate can contain a special Authority Information Access extension (RFC-3280) with URL to issuer's certificate. Most browsers can use the AIA extension to download missing intermediate certificate to complete the certificate chain. But some clients (mobile browsers, OpenSSL) don't support this extension, so they report such certificate as untrusted.
You can solve the incomplete certificate chain issue manually by concatenating all certificates from the certificate to the trusted root certificate (exclusive, in this order), to prevent such issues. Note, the trusted root certificate should not be there, as it is already included in the system’s root certificate store.
You should be able to fetch intermediate certificates from the issuer and concat them together by yourself. I have written a script to automate the procedure, it loops over the AIA extension to produce output of correctly chained certificates. https://github.com/zakjan/cert-chain-resolver
Following up on zakjan's answer, I had a problem when I tried to use jquery to do an AJAX request on my newly secure server, in an android webview. It worked in the browser, but not in my app.
I used this site: https://certificatechain.io/
I pasted in the text of my signed .crt file I got back from Comodo (positiveSSL), and it gave me back a concatination of everything I needed. I saved it as my domain + "chain.crt" (see below)
Then, in my apache configs, I entered something like this for that particular virtual host:
SSLEngine On
SSLCertificateFile /etc/ssl/localcerts/example_com.crt
SSLCertificateKeyFile /etc/ssl/localcerts/example.com.key
SSLCACertificateFile /etc/ssl/localcerts/example.com.chain.crt
After that, my Android app's webview did not have a problem using ajax to POST to my server. I tried it on 2 real-world devices, one running 2.3.4, one running 4.something. And on the emulator running 2.3. All worked.
I hope this helps.
i have same problem before..
you can use this answer, it works for me nicely..
Trusting all certificates using HttpClient over HTTPS
Related
Using com.squareup.retrofit2:retrofit:2.0.1 with com.squareup.okhttp3:okhttp:3.2.0 on an AVD with Android 6.0.
I'm trying to implement public key pinning using a self signed certificate that is signed by a Root CA. That Root CA is in the system CA trust store.
Using the example provided by okhttp wiki with some small changes:
OkHttpClient client = new OkHttpClient.Builder().certificatePinner(
new CertificatePinner.Builder()
.add(pinningUrl, "sha256/invalidPIN")
.build()).build();
Request request = new Request.Builder()
.url(pinningUrl)
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
for (Certificate certificate : response.handshake().peerCertificates()) {
System.out.println(CertificatePinner.pin(certificate));
}
What happens is that response.isSuccessful returns true, no exception is thrown, although the pin isn't correct. The only thing that is done correctly is the validation of the certificate with the Root CAs in systems CA trust store.
What I've found to be working, is adding this line before the for loop. But that isn't the right approach because the request is already sent, the pinning should work before TLS negotiation is finished. Also this line isn't mentioned in any sample code I've found.
client.certificatePinner().check(pinningUrl, response.handshake().peerCertificates());
throws
javax.net.ssl.SSLPeerUnverifiedException: Certificate pinning failure!
Is there a bug in the sample code provided by okhttp or am I doing something wrong?
You’re configuring it incorrectly. Replace pinningUrl with the hostname of the pinning URL. For example, you want example.com instead of http://example.com/. If you’d like to send a PR to make hostname validation more strict, it would be quite welcome.
Recently I acquired a new domain name to use with an existing Android application. I also bought SSL certificate from a trusted CA (Comodo). When I browse to the new domain with a web browser, everything works as expected - no errors about SSL certificate. Same with HttpUrlConnection, but for some reason Apaches HttpClient generates an SSLException:
javax.net.ssl.SSLException: hostname in certificate didn't match: my.new.domain != my.old.domain OR my.old.domain
What's more interesting, some of the devices that I tried with worked fine for about a week after the change, and stopped working after that. Other devices stopped working right away.
If I use the old domain name in client code, everything works correctly.
I'm using version 4.3.3 from here of the HttpClient for Android. I realize that converting to HttpUrlConnection would indeed solve my problem, but I'm interested in WHY this is happening - from where does the HttpClient pick up the old domain name? Is it some misconfiguration on the server, or does Apaches HttpClient have some sort of internal dns cache? Testing with a fresh emulator instance raised the same exception, so the problem is not related to caching.
My own investigations got stuck - all I could find was instructions for disabling hostname verification completely, or instructions for self-signed certificates.
That's probably because Apache HttpClient does not support SNI (server name indication), where you can have multiple certificates behind the same IP address. This means, that it does not send the target hostname inside the SSL handshake and thus the server has only the target IP address to decide which certificate it should use and just uses the default certificate for the IP - which is probably the wrong one.
I'm trying to make a connection to an LDAP server in my Android app, and am using the UnboundID SDK. Recently, a change was made from unsecured to secured LDAP, and I have to change the app accordingly. I have been given the SSL certificates file to validate against. I've already used the file to make a keystore as described here. I've got this keystore file in the assets folder of my app, and am pulling from that. The code below does not currently work, and throws the exception:
LDAPException(resultCode=01 (connect error), errorMessage=('An error occurred while attempting to connect to server place.myserver.com:636: javax.net.ssl.SSLHandShakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found
// code from above link
AssetManager assetManager = getApplicationContext().getAssets();
InputStream keyStoreInputStream = assetManager.open("yourapp.store");
KeyStore trustStore = KeyStore.getInstance("BKS");
trustStore.load(keyStoreInputStream, "myPassword".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(trustStore);
// my code
SSLUtil sslUtil = new SSLUtil(tmf.getTrustManagers());
LDAPConnection connection = new LDAPConnection(sslUtil.createSSLSocketFactory());
connection.connect("place.myserver.com", 636);
However, the code segment:
SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
LDAPConnection connection = new LDAPConnection(sslUtil.createSSLSocketFactory());
connection.connect("place.myserver.com", 636);
does work (although I was informed by the higher-ups that this would be insecure).
I'm not quite sure as to what exactly I'm doing wrong here, so any help would be appreciated. Also, if there is a better way of accomplishing this than what I'm attempting to do above, feel free to let me know :) I would like to stick with the UnboundID library though, since the rest of the code is already written using that as well, and everything works if I use the TrustAllTrustManager.
It's true that the trust all trust manager isn't secure. It's convenient for testing purposes, but it will allow a bad guy to set up his own server with a certificate he generates for himself and use it to impersonate the real server, or to operate as a man in the middle, intercepting and potentially alerting any communication between the client and the real server. With a more strict trust manager in place, the client should reject the bogus certificate that the fake server will present.
Unfortunately, though, it looks like the trust manager you're trying to use in this case doesn't like the certificate that your server is presenting to it. Because the trust all trust manager allows you to establish the connection, that means that your server does have a certificate and is capable of performing SSL communication, but there's something about that certificate that your trust manager doesn't like. It's almost certainly not an issue with the LDAP SDK, since the same problem should arise with any other LDAP API if you're using the same trust store.
If you look at the result, it has a message of "Trust anchor for certification path not found". This implies that neither the certificate the server is using nor those of any of its issuers was found in the trust store. You'll need to import the server certificate (or the certificate of one of its issuers) into the trust store that you're using. It sounds like you've tried to do that, but since it's not working then something must not be quite right with the way it was done. I'd recommend working wit the directory server administrator to ensure that you're trying to import the right certificate based on the server configuration.
I'm trying to hit an https url with my app. I've followed this tutorial. The store I've created has the correct CA. However, when I try to make the connection, I get the following error:
ERROR/IOException(1843): webPost: javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
I've done some reading about this error and people have decided to allow their apps to accept all certificates. I don't think that's really acceptable. What should I check next to see what the issue is?
You can configure your device to ignore SSL certificates:
http://www.virtualzone.de/2011-02-27/how-to-use-apache-httpclient-with-httpsssl-on-android/(dead link)
UPDATE: Much better answer is visible here:
Trusting all certificates using HttpClient over HTTPS
Or do things specific to your HttpClient version:
HttpGet with HTTPS : SSLPeerUnverifiedException
I am attempting to connect to a local HTTPS server using the apache DefaultHttpClient on a Android device.
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://192.168.1.121:4113/services");
... header and content filling in ...
HttpResponse response = httpclient.execute(httppost);
I am getting an error of "javax.net.ssl SSLException: Not trusted server certificate" when the .execute runs. I want to simply allow any certificate to work, regardless of if it is or is not in the android key chain.
I have spent about 40 hours researching and trying to figure out a workaround for this issue. I have seen many examples of how to do this but none so far have worked in Android; they seem to only work for JAVA. Does anyone know how to configure, or override the certificate validation used by the Apache HttpClient in Android so that it will just approve all certificates for a DefaultHttpClient connection?
I thank you for your kind response
If anyone is still trying to figure this out I ended up going with the solution here:
HTTPS GET (SSL) with Android and self-signed server certificate
Scroll down to the solution by SimonJ. It is a simple straight forward solution to this problem.
Look at this tutorial http://blog.antoine.li/index.php/2010/10/android-trusting-ssl-certificates/
The tutorial is based on Apache's HttpClient and explains how to use the SSLSocketFactory to trust the defined certificates in your own keystore (also explained how you can create it with the BouncyCastle provider).
I've tested it and it works great. In my opinion this is the secure way.