Trusting all certificates using HttpClient over HTTPS
Asked Answered
S

22

433

Recently posted a question regarding the HttpClient over Https (found here). I've made some headway, but I've run into new issues. As with my last problem, I can't seem to find an example anywhere that works for me. Basically, I want my client to accept any certificate (because I'm only ever pointing to one server) but I keep getting a javax.net.ssl.SSLException: Not trusted server certificate exception.

So this is what I have:


    public void connect() throws A_WHOLE_BUNCH_OF_EXCEPTIONS {

        HttpPost post = new HttpPost(new URI(PROD_URL));
        post.setEntity(new StringEntity(BODY));

        KeyStore trusted = KeyStore.getInstance("BKS");
        trusted.load(null, "".toCharArray());
        SSLSocketFactory sslf = new SSLSocketFactory(trusted);
        sslf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        SchemeRegistry schemeRegistry = new SchemeRegistry();
        schemeRegistry.register(new Scheme ("https", sslf, 443));
        SingleClientConnManager cm = new SingleClientConnManager(post.getParams(),
                schemeRegistry);

        HttpClient client = new DefaultHttpClient(cm, post.getParams());
        HttpResponse result = client.execute(post);
    }

And here's the error I'm getting:

    W/System.err(  901): javax.net.ssl.SSLException: Not trusted server certificate 
    W/System.err(  901):    at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:360) 
    W/System.err(  901):    at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:92) 
    W/System.err(  901):    at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:321) 
    W/System.err(  901):    at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:129) 
    W/System.err(  901):    at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164) 
    W/System.err(  901):    at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119) 
    W/System.err(  901):    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:348) 
    W/System.err(  901):    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555) 
    W/System.err(  901):    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487) 
    W/System.err(  901):    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465) 
    W/System.err(  901):    at me.harrisonlee.test.ssl.MainActivity.connect(MainActivity.java:129) 
    W/System.err(  901):    at me.harrisonlee.test.ssl.MainActivity.access$0(MainActivity.java:77) 
    W/System.err(  901):    at me.harrisonlee.test.ssl.MainActivity$2.run(MainActivity.java:49) 
    W/System.err(  901): Caused by: java.security.cert.CertificateException: java.security.InvalidAlgorithmParameterException: the trust anchors set is empty 
    W/System.err(  901):    at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:157) 
    W/System.err(  901):    at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:355) 
    W/System.err(  901):    ... 12 more 
    W/System.err(  901): Caused by: java.security.InvalidAlgorithmParameterException: the trust anchors set is empty 
    W/System.err(  901):    at java.security.cert.PKIXParameters.checkTrustAnchors(PKIXParameters.java:645) 
    W/System.err(  901):    at java.security.cert.PKIXParameters.<init>(PKIXParameters.java:89) 
    W/System.err(  901):    at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.<init>(TrustManagerImpl.java:89) 
    W/System.err(  901):    at org.apache.harmony.xnet.provider.jsse.TrustManagerFactoryImpl.engineGetTrustManagers(TrustManagerFactoryImpl.java:134) 
    W/System.err(  901):    at javax.net.ssl.TrustManagerFactory.getTrustManagers(TrustManagerFactory.java:226)W/System.err(  901):     at org.apache.http.conn.ssl.SSLSocketFactory.createTrustManagers(SSLSocketFactory.java:263) 
    W/System.err(  901):    at org.apache.http.conn.ssl.SSLSocketFactory.<init>(SSLSocketFactory.java:190) 
    W/System.err(  901):    at org.apache.http.conn.ssl.SSLSocketFactory.<init>(SSLSocketFactory.java:216) 
    W/System.err(  901):    at me.harrisonlee.test.ssl.MainActivity.connect(MainActivity.java:107) 
    W/System.err(  901):    ... 2 more
Siana answered 15/4, 2010 at 4:41 Comment(4)
I needed to do this for internal use. I really hope you are not letting users outside your company use your app since you have opened it up to man in the middle attack and they would then be vulnerable to someone hijacking their session. nonetheless, I need to do this temporary for some testing until I get the real certificate in hand.....hopefully you are doing it for the same temporary kind of reason or the app is only used internally.Gurtner
I tried these solutions on 4.3 apache http client, but they are mostly deprecated. Here is not deprecated solution: https://mcmap.net/q/35849/-java-httpclient-error-for-no-ssl-certificate-found-using-certificate-as-string-within-codeValtin
Java 1.6 doesn't have SNI support which is also problematic in these scenarios - if you don't construct the request properly, you could get a certificate that doesn't match the request. See issues.apache.org/jira/browse/HTTPCLIENT-1119Alcove
This question is cited in The Most Dangerous Code in the World paper as an example of fallacious reasoning. (research paper: cs.utexas.edu/~shmat/shmat_ccs12.pdf)Palinode
R
441

Note: Do not implement this in production code you are ever going to use on a network you do not entirely trust. Especially anything going over the public internet.

Your question is just what I want to know. After I did some searches, the conclusion is as follows.

In HttpClient way, you should create a custom class from org.apache.http.conn.ssl.SSLSocketFactory, not the one org.apache.http.conn.ssl.SSLSocketFactory itself. Some clues can be found in this post Custom SSL handling stopped working on Android 2.2 FroYo.

An example is like ...

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.conn.ssl.SSLSocketFactory;
public class MySSLSocketFactory extends SSLSocketFactory {
    SSLContext sslContext = SSLContext.getInstance("TLS");

    public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
        super(truststore);

        TrustManager tm = new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };

        sslContext.init(null, new TrustManager[] { tm }, null);
    }

    @Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
        return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    }

    @Override
    public Socket createSocket() throws IOException {
        return sslContext.getSocketFactory().createSocket();
    }
}

and use this class while creating instance of HttpClient.

public HttpClient getNewHttpClient() {
    try {
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null, null);

        MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
        sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        registry.register(new Scheme("https", sf, 443));

        ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

        return new DefaultHttpClient(ccm, params);
    } catch (Exception e) {
        return new DefaultHttpClient();
    }
}

BTW, the link below is for someone who is looking for HttpURLConnection solution. Https Connection Android

I have tested the above two kinds of solutions on froyo, and they all work like a charm in my cases. Finally, using HttpURLConnection may face the redirect problems, but this is beyond the topic.

Note: Before you decide to trust all certificates, you probably should know the site full well and won't be harmful of it to end-user.

Indeed, the risk you take should be considered carefully, including the effect of hacker's mock site mentioned in the following comments that I deeply appreciated. In some situation, although it might be hard to take care of all certificates, you'd better know the implicit drawbacks to trust all of them.

Rosabelle answered 29/1, 2011 at 13:57 Comment(19)
this answer should probably note that trusting all certificates is horribly insecure and nullifies the entire purpose of ssl...Skyeskyhigh
well its still encrypted right? it just could be coming from anyone or being intercepted by anyone...Mccarty
@sweeney - Except that it's not guaranteed that you are talking to the server you think you are. If someone has mucked up a DNS server you could be communicating an encryption key with a hacker's server.Mingmingche
@sweeney In other words you are now liable to man-in-the-middle attacks. You should also note that that code doesn't meet the specification: check the Javadoc. getAcceptedIssuers() isn't allowed to return null.Islander
Yes, I have tested it on a Gingerbread. This is what I was looking for for a month!! Thanks @Daniel!!Allodial
-1 Because it is a terrible idea to accept all certificates. It is too bad that there are so many blogs and tutorials that happily guide Java developers along the path of doing the wrong thing.Papen
+1 Because I needed a quick solution for debugging purposes only. I would not use this in production due to the security concerns others have mentioned, but this was exactly what I needed for testing. Thank you!Anking
Could someone point me to a tutorial where I can accept 1 trusted certificate without using some CLI?Buttercup
There is precisely zero point and some very real and extremely large risks in writing code that is only going to be used 'for testing'. I have a nasty suspicion, indeed it is practically certain, that a lot of these implementations find their way into production, and have yielded radically insecure systems as a result. If you don't code these security trapdoors into your system in the first place, that cannot happen.Islander
+1 for actually answering the question -1 because most classes in the client example are deprecatedBates
I ran into issues with server CA cert misconfigured and needed a quick workaround to the SSL error "No peer certificate" and this worked like a charm.Villus
@EJP: "a lot of these implementations find their way into production" Indeed. There is a research paper "The Most Dangerous Code in the World: Validating SSL Certificates in Non-Browser Software": cs.utexas.edu/~shmat/shmat_ccs12.pdf From the paper: We demonstrated that even applications that rely on standard SSL libraries [...] often perform SSL certificate validation incorrectly or not at all. These vulnerabilities are pervasive in critical softwareGarik
This implementation doesn't work for API level 22 because org.apache.http.conn.ssl.SSLSocketFactory is deprecated. Do you have an updated solution?Cimbura
@inga, the whole org.apache.http interfaces are deprecated. If you worry about that, you may try HttpURLConnection solution which I mentioned above for workaround.Rosabelle
Hello! I tried the code above for debug purpouses and got the javac error "The constructor DefaultHttpClient(ClientConnectionManager, HttpParams) is undefined" at <code>return new DefaultHttpClient(ccm, params);</code>. This is probably caused by jar version mismatch. So I tried different versions. (httpclient-4.5.3.jar, 4.1.2, 4.3-beta1 ...) No one of them worked :-( Any suggestion appreciated. Thank you for your time!Zel
Perfect code previously I was getting the same issue in Android 5.0 but not in above one but this code working for all OS version.Amphiaster
now it says "javax.net.ssl.SSLHandshakeException: Handshake failed" :(Cautious
Some of that stuff doesn't seem needed see https://mcmap.net/q/35822/-trusting-all-certificates-using-httpclient-over-httpsAmygdalate
My problem started when the "DST Root CA X3" expired. Now I have two options: teach every client, with a aged mobile model, how to install a certificate on their mobile, or this answer.Limiting
P
533

You basically have four potential solutions to fix a "Not Trusted" exception on Android using httpclient:

  1. Trust all certificates. Don't do this, unless you really know what you're doing.
  2. Create a custom SSLSocketFactory that trusts only your certificate. This works as long as you know exactly which servers you're going to connect to, but as soon as you need to connect to a new server with a different SSL certificate, you'll need to update your app.
  3. Create a keystore file that contains Android's "master list" of certificates, then add your own. If any of those certs expire down the road, you are responsible for updating them in your app. I can't think of a reason to do this.
  4. Create a custom SSLSocketFactory that uses the built-in certificate KeyStore, but falls back on an alternate KeyStore for anything that fails to verify with the default.

This answer uses solution #4, which seems to me to be the most robust.

The solution is to use an SSLSocketFactory that can accept multiple KeyStores, allowing you to supply your own KeyStore with your own certificates. This allows you to load additional top-level certificates such as Thawte that might be missing on some Android devices. It also allows you to load your own self-signed certificates as well. It will use the built-in default device certificates first, and fall back on your additional certificates only as necessary.

First, you'll want to determine which cert you are missing in your KeyStore. Run the following command:

openssl s_client -connect www.yourserver.com:443

And you'll see output like the following:

Certificate chain
 0 s:/O=www.yourserver.com/OU=Go to 
   https://www.thawte.com/repository/index.html/OU=Thawte SSL123 
   certificate/OU=Domain Validated/CN=www.yourserver.com
   i:/C=US/O=Thawte, Inc./OU=Domain Validated SSL/CN=Thawte DV SSL CA
 1 s:/C=US/O=Thawte, Inc./OU=Domain Validated SSL/CN=Thawte DV SSL CA
   i:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 
   2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA

As you can see, our root certificate is from Thawte. Go to your provider's website and find the corresponding certificate. For us, it was here, and you can see that the one we needed was the one Copyright 2006.

If you're using a self-signed certificate, you didn't need to do the previous step since you already have your signing certificate.

Then, create a keystore file containing the missing signing certificate. Crazybob has details how to do this on Android, but the idea is to do the following:

If you don't have it already, download the bouncy castle provider library from: http://www.bouncycastle.org/latest_releases.html. This will go on your classpath below.

Run a command to extract the certificate from the server and create a pem file. In this case, mycert.pem.

echo | openssl s_client -connect ${MY_SERVER}:443 2>&1 | \
 sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > mycert.pem

Then run the following commands to create the keystore.

export CLASSPATH=/path/to/bouncycastle/bcprov-jdk15on-155.jar
CERTSTORE=res/raw/mystore.bks
if [ -a $CERTSTORE ]; then
    rm $CERTSTORE || exit 1
fi
keytool \
      -import \
      -v \
      -trustcacerts \
      -alias 0 \
      -file <(openssl x509 -in mycert.pem) \
      -keystore $CERTSTORE \
      -storetype BKS \
      -provider org.bouncycastle.jce.provider.BouncyCastleProvider \
      -providerpath /path/to/bouncycastle/bcprov-jdk15on-155.jar \
      -storepass some-password

You'll notice that the above script places the result in res/raw/mystore.bks. Now you have a file that you'll load into your Android app that provides the missing certificate(s).

To do this, register your SSLSocketFactory for the SSL scheme:

final SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schemeRegistry.register(new Scheme("https", createAdditionalCertsSSLSocketFactory(), 443));

// and then however you create your connection manager, I use ThreadSafeClientConnManager
final HttpParams params = new BasicHttpParams();
...
final ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(params,schemeRegistry);

To create your SSLSocketFactory:

protected org.apache.http.conn.ssl.SSLSocketFactory createAdditionalCertsSSLSocketFactory() {
    try {
        final KeyStore ks = KeyStore.getInstance("BKS");

        // the bks file we generated above
        final InputStream in = context.getResources().openRawResource( R.raw.mystore);  
        try {
            // don't forget to put the password used above in strings.xml/mystore_password
            ks.load(in, context.getString( R.string.mystore_password ).toCharArray());
        } finally {
            in.close();
        }

        return new AdditionalKeyStoresSSLSocketFactory(ks);

    } catch( Exception e ) {
        throw new RuntimeException(e);
    }
}

And finally, the AdditionalKeyStoresSSLSocketFactory code, which accepts your new KeyStore and checks if the built-in KeyStore fails to validate an SSL certificate:

/**
 * Allows you to trust certificates from additional KeyStores in addition to
 * the default KeyStore
 */
public class AdditionalKeyStoresSSLSocketFactory extends SSLSocketFactory {
    protected SSLContext sslContext = SSLContext.getInstance("TLS");

    public AdditionalKeyStoresSSLSocketFactory(KeyStore keyStore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
        super(null, null, null, null, null, null);
        sslContext.init(null, new TrustManager[]{new AdditionalKeyStoresTrustManager(keyStore)}, null);
    }

    @Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
        return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    }

    @Override
    public Socket createSocket() throws IOException {
        return sslContext.getSocketFactory().createSocket();
    }



    /**
     * Based on http://download.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#X509TrustManager
     */
    public static class AdditionalKeyStoresTrustManager implements X509TrustManager {

        protected ArrayList<X509TrustManager> x509TrustManagers = new ArrayList<X509TrustManager>();


        protected AdditionalKeyStoresTrustManager(KeyStore... additionalkeyStores) {
            final ArrayList<TrustManagerFactory> factories = new ArrayList<TrustManagerFactory>();

            try {
                // The default Trustmanager with default keystore
                final TrustManagerFactory original = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
                original.init((KeyStore) null);
                factories.add(original);

                for( KeyStore keyStore : additionalkeyStores ) {
                    final TrustManagerFactory additionalCerts = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
                    additionalCerts.init(keyStore);
                    factories.add(additionalCerts);
                }

            } catch (Exception e) {
                throw new RuntimeException(e);
            }



            /*
             * Iterate over the returned trustmanagers, and hold on
             * to any that are X509TrustManagers
             */
            for (TrustManagerFactory tmf : factories)
                for( TrustManager tm : tmf.getTrustManagers() )
                    if (tm instanceof X509TrustManager)
                        x509TrustManagers.add( (X509TrustManager)tm );


            if( x509TrustManagers.size()==0 )
                throw new RuntimeException("Couldn't find any X509TrustManagers");

        }

        /*
         * Delegate to the default trust manager.
         */
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            final X509TrustManager defaultX509TrustManager = x509TrustManagers.get(0);
            defaultX509TrustManager.checkClientTrusted(chain, authType);
        }

        /*
         * Loop over the trustmanagers until we find one that accepts our server
         */
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            for( X509TrustManager tm : x509TrustManagers ) {
                try {
                    tm.checkServerTrusted(chain,authType);
                    return;
                } catch( CertificateException e ) {
                    // ignore
                }
            }
            throw new CertificateException();
        }

        public X509Certificate[] getAcceptedIssuers() {
            final ArrayList<X509Certificate> list = new ArrayList<X509Certificate>();
            for( X509TrustManager tm : x509TrustManagers )
                list.addAll(Arrays.asList(tm.getAcceptedIssuers()));
            return list.toArray(new X509Certificate[list.size()]);
        }
    }

}
Pigweed answered 16/6, 2011 at 21:29 Comment(21)
Hey @emmby, this seems to be the perfect answer for my problem, but I still get no SSL connection. Can you please take a look at it? http://stackoverflow.com/questions/7822381/need-help-understanding-certificate-chainsRegeniaregensburg
Thanks for the great writeup @emmby! I'm sometimes getting a really long delay and then a javax.net.ssl.SSLException: Read error:. Any idea? How can I set a timeout if the solution is same as https://mcmap.net/q/35850/-android-2-3-4-ssl-problem?Ari
Edwin, use HttpConnectionParams.setConnectionTimeout(params, TIMEOUT_CONNECTION); and HttpConnectionParams.setSoTimeout(params, TIMEOUT_SOCKET); in my class the two values are Constants.Beady
I only need to add .PFX Certificate File KeyStore using Android 2.3.3, Which piece of code from above solution will help me? Kindly help me i am working on this from 2 weeks and unable to understand anything.Behah
@emmby, could you tell where should I put this code export CLASSPATH=bcprov-jdk16-145.jar CERTSTORE=res/raw/mystore.bks if [ -a $CERTSTORE ]; then rm $CERTSTORE || exit 1 fi keytool \ -import \ -v \ -trustcacerts \ -alias 0 \ -file <(openssl x509 -in mycert.pem) \ -keystore $CERTSTORE \ -storetype BKS \ -provider org.bouncycastle.jce.provider.BouncyCastleProvider \ -providerpath /usr/share/java/bcprov.jar \ -storepass some-passwordReligieuse
I implemented this, and it works great when I use the server certificate that I selfsigned. However, I wanted more flexibility and use a selfsigned rootCA, so that I can issue new certificates if needed (multiple domains etc). I created a new keystore with only that rootCA, but now on android 2.1 it complains with Not trusted server certificate. On higher versions (2.3/4) it verifies correct. Also in my browser it verifies ok. Any ideas?Cistercian
Well, it turns out it was due to using a snapshot with the emulator, making it's current date set to months earlier then the issues date of the cert. ... :)Cistercian
Is there any way to remove the password from the certificate store? The password isn't providing any protection anyway, the password ends up embedded in the binary.Marcasite
Please consider updating to point out that a badly configured server (missing the intermediate certificates) will also report "Not Trusted"? I sooo nearly went to the effort of creating a trust manager (and I bet many others have), when all I actually needed to do was fix the server configuration (see #6825726).Selfannihilation
I used this as the basis for an app's mutual authentication by adding the presentation of a client certificate. This worked a treat in Android 4.x, but in 2.3.x I am getting a strange error: javax.net.ssl.SSLHandshakeException: org.bouncycastle.jce.exception.ExtCertPathValidatorException: No valid policy tree found when one expected.. It seems to be related to the AdditionalKeyStoresSSLSocketFactory's AdditionalKeyStoresTrustManager. Worst bit is that googling the error returns next to nothing useful. Anyone have any ideas?Maurinemaurise
Hi @Devgeeks, i am facing the same problem. May this developer.android.com/training/articles/… help you. If you already found this, please ignore this.Leyes
Hey @emmby. I am using your solution in my app and using self signed certificate of my server but getting a CertificateException() in checkServerTrusted() method. I tried commenting that throw exception, and it works. if it does not validate my server cert then can i handle it in other way, Can you please guide what is the best solution in this case?Leyes
@Ankit, I am seeing the same behavior you suggest here after trying this solution. Can I please bother you for further information assuming you've moved past this by now? Thanks.Kirman
@Kirman Yeah sure mate, tell me how may i help you?Leyes
@Leyes Well, what was your problem? How did you get past the CertificateException that's thrown at the end of that method? Thanks.Kirman
@Kirman in my case actual problem was from server side, they had not signed valid or proper certificate which i had to keep in my resources / assets, when they signed and gave me correct one, i used above solution and it worked for me. So in some cases self-signed certificates has some problem.Leyes
This should be marked as the right answer. One of the most thorough and well-written answers I've ever seen on SO. DopeOniskey
It seems that this ignores hostnames that don't match the certificate and accepts them even though it shouldn't.User
I am still getting Handshake Exception and Certificate ExceptionAssemblage
the idea is right. but I've got this error when I follow along this solution. error is: keytool error: java.lang.ClassNotFoundException: org.bouncycastle.jce.provider.BouncyCastleProvider . I solved it by using portecle jar to generate keystore.Ellerey
I think this answer is outdated. SSLSocketFactory is marked as deprecated in the apache package.Include
R
441

Note: Do not implement this in production code you are ever going to use on a network you do not entirely trust. Especially anything going over the public internet.

Your question is just what I want to know. After I did some searches, the conclusion is as follows.

In HttpClient way, you should create a custom class from org.apache.http.conn.ssl.SSLSocketFactory, not the one org.apache.http.conn.ssl.SSLSocketFactory itself. Some clues can be found in this post Custom SSL handling stopped working on Android 2.2 FroYo.

An example is like ...

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.conn.ssl.SSLSocketFactory;
public class MySSLSocketFactory extends SSLSocketFactory {
    SSLContext sslContext = SSLContext.getInstance("TLS");

    public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
        super(truststore);

        TrustManager tm = new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };

        sslContext.init(null, new TrustManager[] { tm }, null);
    }

    @Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
        return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    }

    @Override
    public Socket createSocket() throws IOException {
        return sslContext.getSocketFactory().createSocket();
    }
}

and use this class while creating instance of HttpClient.

public HttpClient getNewHttpClient() {
    try {
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null, null);

        MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
        sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        registry.register(new Scheme("https", sf, 443));

        ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

        return new DefaultHttpClient(ccm, params);
    } catch (Exception e) {
        return new DefaultHttpClient();
    }
}

BTW, the link below is for someone who is looking for HttpURLConnection solution. Https Connection Android

I have tested the above two kinds of solutions on froyo, and they all work like a charm in my cases. Finally, using HttpURLConnection may face the redirect problems, but this is beyond the topic.

Note: Before you decide to trust all certificates, you probably should know the site full well and won't be harmful of it to end-user.

Indeed, the risk you take should be considered carefully, including the effect of hacker's mock site mentioned in the following comments that I deeply appreciated. In some situation, although it might be hard to take care of all certificates, you'd better know the implicit drawbacks to trust all of them.

Rosabelle answered 29/1, 2011 at 13:57 Comment(19)
this answer should probably note that trusting all certificates is horribly insecure and nullifies the entire purpose of ssl...Skyeskyhigh
well its still encrypted right? it just could be coming from anyone or being intercepted by anyone...Mccarty
@sweeney - Except that it's not guaranteed that you are talking to the server you think you are. If someone has mucked up a DNS server you could be communicating an encryption key with a hacker's server.Mingmingche
@sweeney In other words you are now liable to man-in-the-middle attacks. You should also note that that code doesn't meet the specification: check the Javadoc. getAcceptedIssuers() isn't allowed to return null.Islander
Yes, I have tested it on a Gingerbread. This is what I was looking for for a month!! Thanks @Daniel!!Allodial
-1 Because it is a terrible idea to accept all certificates. It is too bad that there are so many blogs and tutorials that happily guide Java developers along the path of doing the wrong thing.Papen
+1 Because I needed a quick solution for debugging purposes only. I would not use this in production due to the security concerns others have mentioned, but this was exactly what I needed for testing. Thank you!Anking
Could someone point me to a tutorial where I can accept 1 trusted certificate without using some CLI?Buttercup
There is precisely zero point and some very real and extremely large risks in writing code that is only going to be used 'for testing'. I have a nasty suspicion, indeed it is practically certain, that a lot of these implementations find their way into production, and have yielded radically insecure systems as a result. If you don't code these security trapdoors into your system in the first place, that cannot happen.Islander
+1 for actually answering the question -1 because most classes in the client example are deprecatedBates
I ran into issues with server CA cert misconfigured and needed a quick workaround to the SSL error "No peer certificate" and this worked like a charm.Villus
@EJP: "a lot of these implementations find their way into production" Indeed. There is a research paper "The Most Dangerous Code in the World: Validating SSL Certificates in Non-Browser Software": cs.utexas.edu/~shmat/shmat_ccs12.pdf From the paper: We demonstrated that even applications that rely on standard SSL libraries [...] often perform SSL certificate validation incorrectly or not at all. These vulnerabilities are pervasive in critical softwareGarik
This implementation doesn't work for API level 22 because org.apache.http.conn.ssl.SSLSocketFactory is deprecated. Do you have an updated solution?Cimbura
@inga, the whole org.apache.http interfaces are deprecated. If you worry about that, you may try HttpURLConnection solution which I mentioned above for workaround.Rosabelle
Hello! I tried the code above for debug purpouses and got the javac error "The constructor DefaultHttpClient(ClientConnectionManager, HttpParams) is undefined" at <code>return new DefaultHttpClient(ccm, params);</code>. This is probably caused by jar version mismatch. So I tried different versions. (httpclient-4.5.3.jar, 4.1.2, 4.3-beta1 ...) No one of them worked :-( Any suggestion appreciated. Thank you for your time!Zel
Perfect code previously I was getting the same issue in Android 5.0 but not in above one but this code working for all OS version.Amphiaster
now it says "javax.net.ssl.SSLHandshakeException: Handshake failed" :(Cautious
Some of that stuff doesn't seem needed see https://mcmap.net/q/35822/-trusting-all-certificates-using-httpclient-over-httpsAmygdalate
My problem started when the "DST Root CA X3" expired. Now I have two options: teach every client, with a aged mobile model, how to install a certificate on their mobile, or this answer.Limiting
M
101

Add this code before the HttpsURLConnection and it will be done. I got it.

private void trustEveryone() { 
    try { 
            HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){ 
                    public boolean verify(String hostname, SSLSession session) { 
                            return true; 
                    }}); 
            SSLContext context = SSLContext.getInstance("TLS"); 
            context.init(null, new X509TrustManager[]{new X509TrustManager(){ 
                    public void checkClientTrusted(X509Certificate[] chain, 
                                    String authType) throws CertificateException {} 
                    public void checkServerTrusted(X509Certificate[] chain, 
                                    String authType) throws CertificateException {} 
                    public X509Certificate[] getAcceptedIssuers() { 
                            return new X509Certificate[0]; 
                    }}}, new SecureRandom()); 
            HttpsURLConnection.setDefaultSSLSocketFactory( 
                            context.getSocketFactory()); 
    } catch (Exception e) { // should never happen 
            e.printStackTrace(); 
    } 
}
Margy answered 14/3, 2011 at 10:0 Comment(4)
This is the ideal Q&D solution. Short and "just works".Mullens
Perfect answer for testing purposes!!! And yes it is a bad idea to use in production, but come on ... that should be clear to everyone looking at the question title. It still answers it best/shortest/with the same (in)security level!Rich
After adding this is app grant permission on playstore to upload ?Randall
Confused. It says Add this code before the HttpsURLConnection but the OP isn't using HttpsURLConnection...Piker
A
37

This is a bad idea. Trusting any certificate is only (very) slightly better than using no SSL at all. When you say "I want my client to accept any certificate (because I'm only ever pointing to one server)" you are assuming this means that somehow pointing to "one server" is safe, which it's not on a public network.

You are completely open to a man-in-the-middle attack by trusting any certificate. Anyone can proxy your connection by establishing a separate SSL connection with you and with the end server. The MITM then has access to your entire request and response. Unless you didn't really need SSL in the first place (your message has nothing sensitive, and doesn't do authentication) you shouldn't trust all certificates blindly.

You should consider adding the public cert to a jks using keytool, and using that to build your socket factory, such as this:

    KeyStore ks = KeyStore.getInstance("JKS");

    // get user password and file input stream
    char[] password = ("mykspassword")).toCharArray();
    ClassLoader cl = this.getClass().getClassLoader();
    InputStream stream = cl.getResourceAsStream("myjks.jks");
    ks.load(stream, password);
    stream.close();

    SSLContext sc = SSLContext.getInstance("TLS");
    KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
    TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");

    kmf.init(ks, password);
    tmf.init(ks);

    sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(),null);

    return sc.getSocketFactory();

This has one caveat to watch out for. The certificate will expire eventually, and the code will stop working at that time. You can easily determine when this will happen by looking at the cert.

Addict answered 30/3, 2011 at 22:53 Comment(5)
If you're not using client-certificate authentication, from the client side, you don't need a keymanager (use null in SSLContext.init). You should also use the default algorithms (KMF/TMF.getDefaultAlgorithm() ), instead of hard-coding SunX509` (more so because the default for TMF is actually PKIX on the Sun/Oracle JVM).Clearsighted
Exists a ready to use root certificates file? (like browsers do)Currycomb
Where did myjks.jks comes from?Chilton
@Chilton Generated using Java "keytool".Addict
How do you run "export " command from windows, I download bouncycastly jar file, do I need to install it to windows?Haeres
J
28

You can disable HttpURLConnection SSL checking for testing purposes this way since API 8:

    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    if (conn instanceof HttpsURLConnection) {
        HttpsURLConnection httpsConn = (HttpsURLConnection) conn;
        httpsConn.setSSLSocketFactory(SSLCertificateSocketFactory.getInsecure(0, null));
        httpsConn.setHostnameVerifier(new AllowAllHostnameVerifier());
    }
Janus answered 4/2, 2015 at 9:56 Comment(2)
org.apache.http.conn.ssl.AllowAllHostnameVerifier is deprecated.Mailbox
@Mailbox According to the javadoc, AllowAllHostnameVerifier is replaced by NoopHostnameVerifier"Guideline
A
11

The code above in https://mcmap.net/q/35822/-trusting-all-certificates-using-httpclient-over-https is correct, except it MUST also call the hostname verifier:

    @Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
    SSLSocket sslSocket = (SSLSocket)sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    getHostnameVerifier().verify(host, sslSocket);
    return sslSocket;
}

I signed up to stackoverflow expressly to add this fix. Heed my warning!

Arcadia answered 26/11, 2013 at 21:17 Comment(1)
Once you verify the certificate this way on the first connection, what do you do with subsequent connections? Do you leverage the knowledge you gained from the first connection? What if a fake certificate with the same name is used on connection attempt 3?Inlet
B
10

I'm adding a response for those that use the httpclient-4.5, and probably works for 4.4 as well.

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.fluent.ContentResponseHandler;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;



public class HttpClientUtils{

public static HttpClient getHttpClientWithoutSslValidation_UsingHttpClient_4_5_2() {
    try {
        SSLContextBuilder builder = new SSLContextBuilder();
        builder.loadTrustMaterial(null, new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                return true;
            }
        });
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(), new NoopHostnameVerifier());
        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); 
        return httpclient;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
}
Blinkers answered 30/5, 2016 at 17:0 Comment(5)
what is new NoopHostnameVerifier() class?Woodsia
@MushtakimAhmedAnsari From docs: "The NO_OP HostnameVerifier essentially turns hostname verification off. This implementation is a no-op, and never throws the SSLException."Blinkers
Thanks for the great answer.This one should get more up votes.Serenata
How do I use it? or are you suggesting that simply having the class will override ssl certificate verifications?Frolick
yes. that httpClient when used will not validate https certificatesBlinkers
Z
9

The API of HttpComponents has got changed. It works with the code below.

public static HttpClient getTestHttpClient() {
    try {
        SSLSocketFactory sf = new SSLSocketFactory(new TrustStrategy(){
            @Override
            public boolean isTrusted(X509Certificate[] chain,
                    String authType) throws CertificateException {
                return true;
            }
        }, new AllowAllHostnameVerifier());

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("https",8444, sf));
        ClientConnectionManager ccm = new ThreadSafeClientConnManager(registry);
        return new DefaultHttpClient(ccm);
    } catch (Exception e) {
        e.printStackTrace();
        return new DefaultHttpClient();
    }
}
Zinciferous answered 14/8, 2012 at 12:3 Comment(1)
Using a custom trust strategy is the right answer. Thanks.Prudential
F
6

Trusting all certificates was no real alternative for me, so I did the following to get HttpsURLConnection to trust a new certificate (see also http://nelenkov.blogspot.jp/2011/12/using-custom-certificate-trust-store-on.html).

  1. Get the certificate; I got this done by exporting the certificate in Firefox (click on the little lock icon, get certificate details, click export), then used portecle to export a truststore (BKS).

  2. Load the Truststore from /res/raw/geotrust_cert.bks with the following code:

        final KeyStore trustStore = KeyStore.getInstance("BKS");
        final InputStream in = context.getResources().openRawResource(
                R.raw.geotrust_cert);
        trustStore.load(in, null);
    
        final TrustManagerFactory tmf = TrustManagerFactory
                .getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(trustStore);
    
        final SSLContext sslCtx = SSLContext.getInstance("TLS");
        sslCtx.init(null, tmf.getTrustManagers(),
                new java.security.SecureRandom());
    
        HttpsURLConnection.setDefaultSSLSocketFactory(sslCtx
                .getSocketFactory());
    
Fuhrman answered 15/4, 2013 at 19:0 Comment(1)
I get this error. IOExceptionjavax.net.ssl.SSLPeerUnverifiedException: No peer certificate. This when doing the actual execute call on the HttpClient after the above setup is done.Informal
S
3

Here is a much simple version using 4.1.2 httpclient code. This can then be modified to any trust algorithm you see fit.

public static HttpClient getTestHttpClient() {
    try {
        SSLSocketFactory sf = new SSLSocketFactory(new TrustStrategy(){
            @Override
            public boolean isTrusted(X509Certificate[] chain,
                    String authType) throws CertificateException {
                return true;
            }
        });
        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("https", 443, sf));
        ClientConnectionManager ccm = new ThreadSafeClientConnManager(registry);
        return new DefaultHttpClient(ccm);
    } catch (Exception e) {
        return new DefaultHttpClient();
    }
}
Sibilate answered 15/11, 2011 at 20:35 Comment(0)
T
3

I'm looked response from "emmby" (answered Jun 16 '11 at 21:29), item #4: "Create a custom SSLSocketFactory that uses the built-in certificate KeyStore, but falls back on an alternate KeyStore for anything that fails to verify with the default."

This is a simplified implementation. Load the system keystore & merge with application keystore.

public HttpClient getNewHttpClient() {
    try {
        InputStream in = null;
        // Load default system keystore
        KeyStore trusted = KeyStore.getInstance(KeyStore.getDefaultType()); 
        try {
            in = new BufferedInputStream(new FileInputStream(System.getProperty("javax.net.ssl.trustStore"))); // Normally: "/system/etc/security/cacerts.bks"
            trusted.load(in, null); // no password is "changeit"
        } finally {
            if (in != null) {
                in.close();
                in = null;
            }
        }

        // Load application keystore & merge with system
        try {
            KeyStore appTrusted = KeyStore.getInstance("BKS"); 
            in = context.getResources().openRawResource(R.raw.mykeystore);
            appTrusted.load(in, null); // no password is "changeit"
            for (Enumeration<String> e = appTrusted.aliases(); e.hasMoreElements();) {
                final String alias = e.nextElement();
                final KeyStore.Entry entry = appTrusted.getEntry(alias, null);
                trusted.setEntry(System.currentTimeMillis() + ":" + alias, entry, null);
            }
        } finally {
            if (in != null) {
                in.close();
                in = null;
            }
        }

        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

        SSLSocketFactory sf = new SSLSocketFactory(trusted);
        sf.setHostnameVerifier(SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        registry.register(new Scheme("https", sf, 443));

        ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

        return new DefaultHttpClient(ccm, params);
    } catch (Exception e) {
        return new DefaultHttpClient();
    }
}

A simple mode to convert from JKS to BKS:

keytool -importkeystore -destkeystore cacerts.bks -deststoretype BKS -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-jdk16-141.jar -deststorepass changeit -srcstorepass changeit -srckeystore $JAVA_HOME/jre/lib/security/cacerts -srcstoretype JKS -noprompt

*Note: In Android 4.0 (ICS) the Trust Store has changed, more info: http://nelenkov.blogspot.com.es/2011/12/ics-trust-store-implementation.html

Telstar answered 12/2, 2012 at 3:59 Comment(0)
L
3

For those who would like to allow all certificates to work (for testing purposes) over OAuth, follow these steps:

1) Download the source code of the Android OAuth API here: https://github.com/kaeppler/signpost

2) Find the file "CommonsHttpOAuthProvider" class

3) Change it as below:

public class CommonsHttpOAuthProvider extends AbstractOAuthProvider {

private static final long serialVersionUID = 1L;

private transient HttpClient httpClient;

public CommonsHttpOAuthProvider(String requestTokenEndpointUrl, String accessTokenEndpointUrl,
        String authorizationWebsiteUrl) {
    super(requestTokenEndpointUrl, accessTokenEndpointUrl, authorizationWebsiteUrl);


    //this.httpClient = new DefaultHttpClient();//Version implemented and that throws the famous "javax.net.ssl.SSLException: Not trusted server certificate" if the certificate is not signed with a CA
    this.httpClient = MySSLSocketFactory.getNewHttpClient();//This will work with all certificates (for testing purposes only)
}

The "MySSLSocketFactory" above is based on the accepted answer. To make it even easier, here goes the complete class:

package com.netcomps.oauth_example;

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;

//https://mcmap.net/q/35822/-trusting-all-certificates-using-httpclient-over-https
public class MySSLSocketFactory extends SSLSocketFactory {

    SSLContext sslContext = SSLContext.getInstance("TLS");

public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {

    super(truststore);
    TrustManager tm = new X509TrustManager() {

        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    };

    sslContext.init(null, new TrustManager[] { tm }, null);
}

@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
    return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
}

@Override
public Socket createSocket() throws IOException {
    return sslContext.getSocketFactory().createSocket();
}



public static HttpClient getNewHttpClient() {

    try {
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null, null);

        SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
        sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        registry.register(new Scheme("https", sf, 443));

        ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

        return new DefaultHttpClient(ccm, params);

    } catch (Exception e) {
        return new DefaultHttpClient();
    }
}

}

Hope this helps someone.

Levelheaded answered 11/12, 2012 at 2:58 Comment(1)
The question was HttpClient and HTTPS; not OAuth for Android from a GitHub project.Inlet
G
3

I used this and It works for me on all OS.

/**
 * Disables the SSL certificate checking for new instances of {@link HttpsURLConnection} This has been created to
 * aid testing on a local box, not for use on production.
 */


private static void disableSSLCertificateChecking() {
    TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        @Override
        public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
            // Not implemented
        }

        @Override
        public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
            // Not implemented
        }
    } };

    try {
        SSLContext sc = SSLContext.getInstance("TLS");

        sc.init(null, trustAllCerts, new java.security.SecureRandom());

        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    } catch (KeyManagementException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
}
Galoot answered 28/7, 2016 at 12:36 Comment(1)
Hello @yegor256, I am using this code, but still getting SSL handshake problemSaltworks
A
2

Just adding -Dtrust_all_cert=true to VM arguments should do. This argument tells java to ignore certificate checks.

Athal answered 21/11, 2016 at 7:35 Comment(1)
Please don't add the same answer to multiple questions. Answer the best one and flag the rest as duplicates. See Is it acceptable to add a duplicate answer to several questions?Gyatt
U
2

There are a-lot alternatives for this use case. If you don't want to have any custom code in your code base such as custom TrustManager or custom SSLSocketFactory I would suggest to try GitHub - SSLContext Kickstart and the following code snippet:

<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>sslcontext-kickstart</artifactId>
    <version>7.0.2</version>
</dependency>

SSL configuration

SSLFactory sslFactory = SSLFactory.builder()
    .withUnsafeTrustMaterial()
    .withUnsafeHostnameVerifier()
    .build();

SSLSocketFactory sslSocketFactory = sslFactory.getSslSocketFactory();

HttpClient configuration

HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("https", sslSocketFactory, 443));

ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

HttpClient httpClient = new DefaultHttpClient(ccm, params);

HttpsUrlConnection

HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory); 

I also need to give some disclaimer, I am the maintainer of the library.

Unpaidfor answered 11/4, 2021 at 22:2 Comment(1)
This was simple and worked really well for me in contrast to many other answers. I was able to take the sslSocketFactory provided by this code and give it to a WebSocket library (nv-websocket-client) with .setSSLSocketFactory(). The only thing different for me was how to specify the depencency - my build.gradle file has dependencies{ implementation 'io.github.hakky54:sslcontext-kickstart:6.6.0' } instead of the XML provided in this answer. Thanks for your library!Minded
L
0

Any body still struggling with StartCom SSL Certificates on Android 2.1 visit https://www.startssl.com/certs/ and download the ca.pem, now in the answer provided by @emmby replace

`export CLASSPATH=bcprov-jdk16-145.jar
 CERTSTORE=res/raw/mystore.bks
      if [ -a $CERTSTORE ]; then
          rm $CERTSTORE || exit 1
      fi
 keytool \
  -import \
  -v \
  -trustcacerts \
  -alias 0 \
  -file <(openssl x509 -in mycert.pem) \
  -keystore $CERTSTORE \
  -storetype BKS \
  -provider org.bouncycastle.jce.provider.BouncyCastleProvider \
  -providerpath /usr/share/java/bcprov.jar \
  -storepass some-password`

with

 `export CLASSPATH=bcprov-jdk16-145.jar
 CERTSTORE=res/raw/mystore.bks
      if [ -a $CERTSTORE ]; then
          rm $CERTSTORE || exit 1
      fi
 keytool \
  -import \
  -v \
  -trustcacerts \
  -alias 0 \
  -file <(openssl x509 -in ca.pem) \
  -keystore $CERTSTORE \
  -storetype BKS \
  -provider org.bouncycastle.jce.provider.BouncyCastleProvider \
  -providerpath /usr/share/java/bcprov.jar \
  -storepass some-password`

Should work out of the box. I was struggling it for over a day even after a perfect answer by @emmby.. Hope this helps someone...

Loads answered 16/5, 2012 at 7:39 Comment(0)
D
0

use this class

public class WCFs
{
    //  https://192.168.30.8/myservice.svc?wsdl
private static final String NAMESPACE = "http://tempuri.org/";
private static final String URL = "192.168.30.8";
private static final String SERVICE = "/myservice.svc?wsdl";
private static String SOAP_ACTION = "http://tempuri.org/iWCFserviceMe/";


public static Thread myMethod(Runnable rp)
{
    String METHOD_NAME = "myMethod";

    SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);

    request.addProperty("Message", "Https WCF Running...");
    return _call(rp,METHOD_NAME, request);
}

protected static HandlerThread _call(final RunProcess rp,final String METHOD_NAME, SoapObject soapReq)
{
    final SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
    int TimeOut = 5*1000;

    envelope.dotNet = true;
    envelope.bodyOut = soapReq;
    envelope.setOutputSoapObject(soapReq);

    final HttpsTransportSE httpTransport_net = new HttpsTransportSE(URL, 443, SERVICE, TimeOut);

    try
    {
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() // use this section if crt file is handmake
        {
            @Override
            public boolean verify(String hostname, SSLSession session)
            {
                return true;
            }
        });

        KeyStore k = getFromRaw(R.raw.key, "PKCS12", "password");
        ((HttpsServiceConnectionSE) httpTransport_net.getServiceConnection()).setSSLSocketFactory(getSSLSocketFactory(k, "SSL"));


    }
    catch(Exception e){}

    HandlerThread thread = new HandlerThread("wcfTd"+ Generator.getRandomNumber())
    {
        @Override
        public void run()
        {
            Handler h = new Handler(Looper.getMainLooper());
            Object response = null;

            for(int i=0; i<4; i++)
            {
                response = send(envelope, httpTransport_net , METHOD_NAME, null);

                try
                {if(Thread.currentThread().isInterrupted()) return;}catch(Exception e){}

                if(response != null)
                    break;

                ThreadHelper.threadSleep(250);
            }

            if(response != null)
            {
                if(rp != null)
                {
                    rp.setArguments(response.toString());
                    h.post(rp);
                }
            }
            else
            {
                if(Thread.currentThread().isInterrupted())
                    return;

                if(rp != null)
                {
                    rp.setExceptionState(true);
                    h.post(rp);
                }
            }

            ThreadHelper.stopThread(this);
        }
    };

    thread.start();

    return thread;
}


private static Object send(SoapSerializationEnvelope envelope, HttpTransportSE androidHttpTransport, String METHOD_NAME, List<HeaderProperty> headerList)
{
    try
    {
        if(headerList != null)
            androidHttpTransport.call(SOAP_ACTION + METHOD_NAME, envelope, headerList);
        else
            androidHttpTransport.call(SOAP_ACTION + METHOD_NAME, envelope);

        Object res = envelope.getResponse();

        if(res instanceof SoapPrimitive)
            return (SoapPrimitive) envelope.getResponse();
        else if(res instanceof SoapObject)
            return ((SoapObject) envelope.getResponse());
    }
    catch(Exception e)
    {}

    return null;
}

public static KeyStore getFromRaw(@RawRes int id, String algorithm, String filePassword)
{
    try
    {
        InputStream inputStream = ResourceMaster.openRaw(id);
        KeyStore keystore = KeyStore.getInstance(algorithm);
        keystore.load(inputStream, filePassword.toCharArray());
        inputStream.close();

        return keystore;
    }
    catch(Exception e)
    {}

    return null;
}

public static SSLSocketFactory getSSLSocketFactory(KeyStore trustKey, String SSLAlgorithm)
{
    try
    {
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(trustKey);

        SSLContext context = SSLContext.getInstance(SSLAlgorithm);//"SSL" "TLS"
        context.init(null, tmf.getTrustManagers(), null);

        return context.getSocketFactory();
    }
    catch(Exception e){}

    return null;
}

}

Dunleavy answered 13/5, 2018 at 13:38 Comment(0)
S
0

enter image description here

A sspi failed in xamarin android.

I found this solution; put this code before you hit on an HTTPS link

const SslProtocols _Tls12 = (SslProtocols)0x00000C00;
const SecurityProtocolType Tls12 = (SecurityProtocolType)_Tls12;
ServicePointManager.SecurityProtocol = Tls12;
Sniffle answered 17/4, 2019 at 7:13 Comment(0)
K
-3

work with all https

httpClient = new DefaultHttpClient();

SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager() {
    public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { }

    public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { }

    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
};

ctx.init(null, new TrustManager[]{tm}, null);
SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, ssf));
Kist answered 25/4, 2013 at 1:27 Comment(1)
Just repeats the same fallacious insecure non-solution that has already been discussed and dismissed in this thread.Islander
H
-3

There a many answers above but I wasn't able to get any of them working correctly (with my limited time), so for anyone else in the same situation you can try the code below which worked perfectly for my java testing purposes:

    public static HttpClient wrapClient(HttpClient base) {
    try {
        SSLContext ctx = SSLContext.getInstance("TLS");
        X509TrustManager tm = new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { }

            public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { }

            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };
        ctx.init(null, new TrustManager[]{tm}, null);
        SSLSocketFactory ssf = new SSLSocketFactory(ctx);
        ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        ClientConnectionManager ccm = base.getConnectionManager();
        SchemeRegistry sr = ccm.getSchemeRegistry();
        sr.register(new Scheme("https", ssf, 443));
        return new DefaultHttpClient(ccm, base.getParams());
    } catch (Exception ex) {
        return null;
    }
}

and call like:

DefaultHttpClient baseClient = new DefaultHttpClient();
HttpClient httpClient = wrapClient(baseClient );

Reference: http://tech.chitgoks.com/2011/04/24/how-to-avoid-javax-net-ssl-sslpeerunverifiedexception-peer-not-authenticated-problem-using-apache-httpclient/

Hamlen answered 17/9, 2013 at 11:1 Comment(1)
To quote EJP: "Just repeats the same fallacious insecure non-solution that has already been discussed and dismissed in this thread".Inlet
H
-4

Simply use this -

public DefaultHttpClient wrapClient(HttpClient base) {
    try {
        SSLContext ctx = SSLContext.getInstance("TLS");
        X509TrustManager tm = new X509TrustManager() {
        public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { }

        public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { }

        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    };
    ctx.init(null, new TrustManager[]{tm}, null);
    SSLSocketFactory ssf = new SSLSocketFactory(ctx);
    ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
    ClientConnectionManager ccm = base.getConnectionManager();
    SchemeRegistry sr = ccm.getSchemeRegistry();
    sr.register(new Scheme("https", ssf, 443));
    return new DefaultHttpClient(ccm, base.getParams());
} catch (Exception ex) {
    return null;
}
}
Hogue answered 8/7, 2013 at 12:12 Comment(1)
To quote EJP: "Just repeats the same fallacious insecure non-solution that has already been discussed and dismissed in this thread".Inlet
G
-6

Daniel's answer was good except I had to change this code...

    SchemeRegistry registry = new SchemeRegistry();
    registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
    registry.register(new Scheme("https", sf, 443));

    ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

to this code...

    ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
    SchemeRegistry registry = ccm.getShemeRegistry()
    registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
    registry.register(new Scheme("https", sf, 443));

to get it to work.

Gurtner answered 5/10, 2011 at 18:11 Comment(1)
how should that work? you reference registry before you even created it!Regeniaregensburg

© 2022 - 2024 — McMap. All rights reserved.