170

I'm trying to make HTTPS connections, using HttpClient lib, but the problem is that, since the certificate isn't signed by a recognized Certificate Authority (CA) like Verisign,GlobalSIgn, etc., listed on the set of Android Trusted Certificates, I keep getting javax.net.ssl.SSLException: Not trusted server certificate.

I've seen solutions where you simply accept all certificates, but what if I want to ask the user?

I want to get a dialog similar to that of the browser, letting the user decide to continue or not. Preferably I'd like to use the same certificatestore as the browser. Any ideas?

ivanleoncz
  • 9,070
  • 7
  • 57
  • 49
Morten
  • 4,507
  • 7
  • 29
  • 31
  • This Accepted solution worked for me- https://stackoverflow.com/questions/2642777/trusting-all-certificates-using-httpclient-over-https?noredirect=1&lq=1 – Venkatesh Feb 03 '20 at 07:12

13 Answers13

174

The first thing you need to do is to set the level of verification. Such levels is not so much:

  • ALLOW_ALL_HOSTNAME_VERIFIER
  • BROWSER_COMPATIBLE_HOSTNAME_VERIFIER
  • STRICT_HOSTNAME_VERIFIER

Although the method setHostnameVerifier() is obsolete for new library apache, but for version in Android SDK is normal. And so we take ALLOW_ALL_HOSTNAME_VERIFIER and set it in the method factory SSLSocketFactory.setHostnameVerifier().

Next, You need set our factory for the protocol to https. To do this, simply call the SchemeRegistry.register() method.

Then you need to create a DefaultHttpClient with SingleClientConnManager. Also in the code below you can see that on default will also use our flag (ALLOW_ALL_HOSTNAME_VERIFIER) by the method HttpsURLConnection.setDefaultHostnameVerifier()

Below code works for me:

HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;

DefaultHttpClient client = new DefaultHttpClient();

SchemeRegistry registry = new SchemeRegistry();
SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
registry.register(new Scheme("https", socketFactory, 443));
SingleClientConnManager mgr = new SingleClientConnManager(client.getParams(), registry);
DefaultHttpClient httpClient = new DefaultHttpClient(mgr, client.getParams());

// Set verifier     
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);

// Example send http request
final String url = "https://encrypted.google.com/";
HttpPost httpPost = new HttpPost(url);
HttpResponse response = httpClient.execute(httpPost);
rekire
  • 47,260
  • 30
  • 167
  • 264
Nikolay Moskvin
  • 1,038
  • 1
  • 16
  • 27
  • 6
    I can't make this code work unfortunately, I still get the "Not trusted server certificate". Are there any extra permissions that I have to set in order to make it work? – Juriy Feb 24 '11 at 21:59
  • No, standard permission – Nikolay Moskvin Feb 28 '11 at 03:49
  • This is not working for me either. I still get the "not trusted" error. Could you show the rest of the code on how to post after all of that. This way we can make sure that we are posting correctly? Thanks – prolink007 Apr 11 '11 at 23:52
  • I updated source code above and added simple example. This code works for you? – Nikolay Moskvin Apr 12 '11 at 17:14
  • Hi code worked well for https://encrypted.google.com/ but did not worked for https://api.instagram.com/v1/media/search in android 2.2 – Yekmer Simsek Apr 14 '11 at 10:45
  • 1
    Doesn't this code just accept all certificates? I need a popup to accept it. – Morten Apr 20 '11 at 08:36
  • Works for the url you used in the code but not working for my https url. :( – Omar Rehman Jun 08 '11 at 11:55
  • From what I understand, this only solves the problem if the certificate chain is correct and in a specific order. There was once a bug in Android related to certificate chain ordering. Perhaps any failures that this solution doesn't resolve are related to that (as I suspect my issue was). – Steve Pomeroy Jun 10 '11 at 05:05
  • Doesn't work for me either, with my https url (Nexus One, Android 2.3.6). – Mathias Conradt Nov 01 '11 at 09:08
  • @Octavian Damiean What concrete you don't understand? – Nikolay Moskvin Dec 09 '11 at 18:22
  • It is not really that I'd not understand something but that I want everyone to understand it without asking. After all we are here to share knowledge, not just code. – Octavian Helm Dec 09 '11 at 22:33
  • I know this is a long ago post. But I am stuck on this issue for days. What do I do with that response variable? How do I use it? – Melvin Lai Mar 01 '12 at 10:12
  • You can create hostnameVerifier as the above code and you can transfer variable in socket factory. Is It clear? – Nikolay Moskvin Mar 02 '12 at 03:08
  • This solution doesn't work on my android 2.2 with untrusted server cert. But answer below helped me. – Roger Alien Mar 02 '12 at 05:58
  • I don't see how the `HttpsURLConnection.setDefaultHostnameVerifier()` ties in to the `DefaultHttpClient httpClient`. – Someone Somewhere Mar 14 '12 at 20:24
  • 3
    I'm using `org.apache.http.conn.ssl.SSLSocketFactory` why do I want to use `javax.net.ssl.HttpsURLConnection` ?? – Someone Somewhere Mar 14 '12 at 20:30
  • @Someone Somewhere I do not remember why, maybe you're right that you using org.apache.http.conn.ssl.SSLSocketFactory. If I will have a little the time I will to do some experiment with this – Nikolay Moskvin Mar 22 '12 at 05:13
  • 10
    Can you explain how this code is any better than disabling certificate verification entirely? I'm not familiar with android's ssl API, but at a glance this seems completely insecure against active attackers. – CodesInChaos Dec 20 '12 at 11:40
  • 3
    I would suggest using ThreadSafeClientConnManager instead of SingleClientConnManager – Farm May 24 '13 at 23:49
  • @Morten:: do you get any clue how to work around to show pop up while bypassing SSL check again? If you have solution then plz pass on to me – amity Sep 03 '13 at 10:27
  • @amity The only solution that I've heard about is to implement your own certificate store and store accepted certificates in the store. However that was a bit over the top for me, so we never implemented that. However see saxos answer below, that shows a lot of promise. – Morten Sep 22 '13 at 07:50
  • I was looking for this solution for days. Thanks a lot :) – KuKeC Apr 14 '16 at 12:51
  • I have a similar doubt. Could you please help https://stackoverflow.com/questions/57389622/client-certificate-based-multifactor-authentication-in-android# – StezPet Aug 13 '19 at 14:28
130

The following main steps are required to achieve a secured connection from Certification Authorities which are not considered as trusted by the android platform.

As requested by many users, I've mirrored the most important parts from my blog article here:

  1. Grab all required certificates (root and any intermediate CA’s)
  2. Create a keystore with keytool and the BouncyCastle provider and import the certs
  3. Load the keystore in your android app and use it for the secured connections (I recommend to use the Apache HttpClient instead of the standard java.net.ssl.HttpsURLConnection (easier to understand, more performant)

Grab the certs

You have to obtain all certificates that build a chain from the endpoint certificate the whole way up to the Root CA. This means, any (if present) Intermediate CA certs and also the Root CA cert. You don’t need to obtain the endpoint certificate.

Create the keystore

Download the BouncyCastle Provider and store it to a known location. Also ensure that you can invoke the keytool command (usually located under the bin folder of your JRE installation).

Now import the obtained certs (don’t import the endpoint cert) into a BouncyCastle formatted keystore.

I didn’t test it, but I think the order of importing the certificates is important. This means, import the lowermost Intermediate CA certificate first and then all the way up to the Root CA certificate.

With the following command a new keystore (if not already present) with the password mysecret will be created and the Intermediate CA certificate will be imported. I also defined the BouncyCastle provider, where it can be found on my file system and the keystore format. Execute this command for each certificate in the chain.

keytool -importcert -v -trustcacerts -file "path_to_cert/interm_ca.cer" -alias IntermediateCA -keystore "res/raw/mykeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret

Verify if the certificates were imported correctly into the keystore:

keytool -list -keystore "res/raw/mykeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret

Should output the whole chain:

RootCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 24:77:D9:A8:91:D1:3B:FA:88:2D:C2:FF:F8:CD:33:93
IntermediateCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 98:0F:C3:F8:39:F7:D8:05:07:02:0D:E3:14:5B:29:43

Now you can copy the keystore as a raw resource in your android app under res/raw/

Use the keystore in your app

First of all we have to create a custom Apache HttpClient that uses our keystore for HTTPS connections:

import org.apache.http.*

public class MyHttpClient extends DefaultHttpClient {

    final Context context;

    public MyHttpClient(Context context) {
        this.context = context;
    }

    @Override
    protected ClientConnectionManager createClientConnectionManager() {
        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        // Register for port 443 our SSLSocketFactory with our keystore
        // to the ConnectionManager
        registry.register(new Scheme("https", newSslSocketFactory(), 443));
        return new SingleClientConnManager(getParams(), registry);
    }

    private SSLSocketFactory newSslSocketFactory() {
        try {
            // Get an instance of the Bouncy Castle KeyStore format
            KeyStore trusted = KeyStore.getInstance("BKS");
            // Get the raw resource, which contains the keystore with
            // your trusted certificates (root and any intermediate certs)
            InputStream in = context.getResources().openRawResource(R.raw.mykeystore);
            try {
                // Initialize the keystore with the provided trusted certificates
                // Also provide the password of the keystore
                trusted.load(in, "mysecret".toCharArray());
            } finally {
                in.close();
            }
            // Pass the keystore to the SSLSocketFactory. The factory is responsible
            // for the verification of the server certificate.
            SSLSocketFactory sf = new SSLSocketFactory(trusted);
            // Hostname verification from certificate
            // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
            sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
            return sf;
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    }
}

We have created our custom HttpClient, now we can use it for secure connections. For example when we make a GET call to a REST resource:

// Instantiate the custom HttpClient
DefaultHttpClient client = new MyHttpClient(getApplicationContext());
HttpGet get = new HttpGet("https://www.mydomain.ch/rest/contacts/23");
// Execute the GET call and obtain the response
HttpResponse getResponse = client.execute(get);
HttpEntity responseEntity = getResponse.getEntity();

That's it ;)

Josh Correia
  • 3,807
  • 3
  • 33
  • 50
saxos
  • 2,467
  • 1
  • 20
  • 21
  • 9
    This is only useful for getting certificates before shipping your application. Doesn't really help users accepts their own certs. for your application – Fuzzy Jan 30 '12 at 08:59
  • Hi all can some one tell me the validation process for keystore with truststore for the above implementation??? thanks in advance.. – Quick learner Mar 02 '12 at 07:29
  • This worked fine..but now I am facing an issue when I rekey the certificate on the server. It seems odd that everytime i update the certificate on my server, the client side store should also be updated. There has to be a better way :| – bpn Oct 29 '12 at 08:25
  • Gr8 answer, I would suggest using ThreadSafeClientConnManager instead of SingleClientConnManager – Farm May 24 '13 at 23:47
  • I have added `/res/raw/mykeystore.bks`, though unable to resolve reference to it. how to solve this ? – Uniruddh Feb 12 '14 at 13:46
  • Can anyone tell me how to convert ".crt" file to ".bks" file.?? –  May 09 '18 at 09:05
  • outdated answer, code doesn't work: "SSLSocketFactory is abstract; cannot be instantiated" – Alberto M Jun 08 '18 at 08:26
  • @uniruddh Restarting Android Studio fixed the reference for me. – Josh Correia Jul 02 '20 at 22:56
21

If you have a custom/self-signed certificate on server that is not there on device, you can use the below class to load it and use it on client side in Android:

Place the certificate *.crt file in /res/raw so that it is available from R.raw.*

Use below class to obtain an HTTPClient or HttpsURLConnection which will have a socket factory using that certificate :

package com.example.customssl;

import android.content.Context;
import org.apache.http.client.HttpClient;
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.AllowAllHostnameVerifier;
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 javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;

public class CustomCAHttpsProvider {

    /**
     * Creates a {@link org.apache.http.client.HttpClient} which is configured to work with a custom authority
     * certificate.
     *
     * @param context       Application Context
     * @param certRawResId  R.raw.id of certificate file (*.crt). Should be stored in /res/raw.
     * @param allowAllHosts If true then client will not check server against host names of certificate.
     * @return Http Client.
     * @throws Exception If there is an error initializing the client.
     */
    public static HttpClient getHttpClient(Context context, int certRawResId, boolean allowAllHosts) throws Exception {


        // build key store with ca certificate
        KeyStore keyStore = buildKeyStore(context, certRawResId);

        // init ssl socket factory with key store
        SSLSocketFactory sslSocketFactory = new SSLSocketFactory(keyStore);

        // skip hostname security check if specified
        if (allowAllHosts) {
            sslSocketFactory.setHostnameVerifier(new AllowAllHostnameVerifier());
        }

        // basic http params for client
        HttpParams params = new BasicHttpParams();

        // normal scheme registry with our ssl socket factory for "https"
        SchemeRegistry schemeRegistry = new SchemeRegistry();
        schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        schemeRegistry.register(new Scheme("https", sslSocketFactory, 443));

        // create connection manager
        ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);

        // create http client
        return new DefaultHttpClient(cm, params);
    }

    /**
     * Creates a {@link javax.net.ssl.HttpsURLConnection} which is configured to work with a custom authority
     * certificate.
     *
     * @param urlString     remote url string.
     * @param context       Application Context
     * @param certRawResId  R.raw.id of certificate file (*.crt). Should be stored in /res/raw.
     * @param allowAllHosts If true then client will not check server against host names of certificate.
     * @return Http url connection.
     * @throws Exception If there is an error initializing the connection.
     */
    public static HttpsURLConnection getHttpsUrlConnection(String urlString, Context context, int certRawResId,
                                                           boolean allowAllHosts) throws Exception {

        // build key store with ca certificate
        KeyStore keyStore = buildKeyStore(context, certRawResId);

        // 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);

        // Create a connection from url
        URL url = new URL(urlString);
        HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
        urlConnection.setSSLSocketFactory(sslContext.getSocketFactory());

        // skip hostname security check if specified
        if (allowAllHosts) {
            urlConnection.setHostnameVerifier(new AllowAllHostnameVerifier());
        }

        return urlConnection;
    }

    private static KeyStore buildKeyStore(Context context, int certRawResId) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
        // init a default key store
        String keyStoreType = KeyStore.getDefaultType();
        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);

        // read and add certificate authority
        Certificate cert = readCert(context, certRawResId);
        keyStore.setCertificateEntry("ca", cert);

        return keyStore;
    }

    private static Certificate readCert(Context context, int certResourceId) throws CertificateException, IOException {

        // read certificate resource
        InputStream caInput = context.getResources().openRawResource(certResourceId);

        Certificate ca;
        try {
            // generate a certificate
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            ca = cf.generateCertificate(caInput);
        } finally {
            caInput.close();
        }

        return ca;
    }

}

Key points:

  1. Certificate objects are generated from .crt files.
  2. A default KeyStore is created.
  3. keyStore.setCertificateEntry("ca", cert) is adding certificate to key store under alias "ca". You modify the code to add more certificates (intermediate CA etc).
  4. Main objective is to generate a SSLSocketFactory which can then be used by HTTPClient or HttpsURLConnection.
  5. SSLSocketFactory can be configured further, for example to skip host name verification etc.

More information at : http://developer.android.com/training/articles/security-ssl.html

S.D.
  • 29,290
  • 3
  • 79
  • 130
16

I was frustrated trying to connect my Android App to my RESTful service using https. Also I was a bit annoyed about all the answers that suggested to disable certificate checking altogether. If you do so, whats the point of https?

After googled about the topic for a while, I finally found this solution where external jars are not needed, just Android APIs. Thanks to Andrew Smith, who posted it on July, 2014

 /**
 * Set up a connection to myservice.domain using HTTPS. An entire function
 * is needed to do this because myservice.domain has a self-signed certificate.
 * 
 * The caller of the function would do something like:
 * HttpsURLConnection urlConnection = setUpHttpsConnection("https://littlesvr.ca");
 * InputStream in = urlConnection.getInputStream();
 * And read from that "in" as usual in Java
 * 
 * Based on code from:
 * https://developer.android.com/training/articles/security-ssl.html#SelfSigned
 */
public static HttpsURLConnection setUpHttpsConnection(String urlString)
{
    try
    {
        // Load CAs from an InputStream
        // (could be from a resource or ByteArrayInputStream or ...)
        CertificateFactory cf = CertificateFactory.getInstance("X.509");

        // My CRT file that I put in the assets folder
        // I got this file by following these steps:
        // * Go to https://littlesvr.ca using Firefox
        // * Click the padlock/More/Security/View Certificate/Details/Export
        // * Saved the file as littlesvr.crt (type X.509 Certificate (PEM))
        // The MainActivity.context is declared as:
        // public static Context context;
        // And initialized in MainActivity.onCreate() as:
        // MainActivity.context = getApplicationContext();
        InputStream caInput = new BufferedInputStream(MainActivity.context.getAssets().open("littlesvr.crt"));
        Certificate ca = cf.generateCertificate(caInput);
        System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());

        // 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 context = SSLContext.getInstance("TLS");
        context.init(null, tmf.getTrustManagers(), null);

        // Tell the URLConnection to use a SocketFactory from our SSLContext
        URL url = new URL(urlString);
        HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();
        urlConnection.setSSLSocketFactory(context.getSocketFactory());

        return urlConnection;
    }
    catch (Exception ex)
    {
        Log.e(TAG, "Failed to establish SSL connection to server: " + ex.toString());
        return null;
    }
}

It worked nice for my mockup App.

8

Google recommends the usage of Android Volley for HTTP/HTTPS connections, since that HttpClient is deprecated. So, you know the right choice :).

And also, NEVER NUKE SSL Certificates (NEVER!!!).

To nuke SSL Certificates, is totally against the purpose of SSL, which is promoting security. There's no sense of using SSL, if you're planning to bomb all SSL certificates that comes. A better solution would be creating a custom TrustManager on your App + using Android Volley for HTTP/HTTPS connections.

Here's a Gist which I created, with a basic LoginApp, performing HTTPS connections, using a Self-Signed Certificate on the server-side, accepted on the App.

Here's also another Gist that may help, for creating Self-Signed SSL Certificates for setting up on your Server and also using the certificate on your App. Very important: you must copy the .crt file which was generated by the script above, to the "raw" directory from your Android project.

ivanleoncz
  • 9,070
  • 7
  • 57
  • 49
  • Hello Ivan, I have never worked with SSL certificates. Would you care to elaborate a bit, how do I get the .crt file? – jlively Mar 09 '17 at 15:26
  • Hi Jively! I see. Yes, of course. But first, would you mind to take a look at the second Gist that I mentioned above? I putted two files on this Gist: one is the file used by the script and the other one, is the script itself, that uses "openssl" binary in order to read the file and then, building the file which contains the SSL Certificate (.crt). Let me know if you managed to understand the whole thing. Regards :). – ivanleoncz Mar 09 '17 at 15:44
  • Hmm yes I have looked at those 2 gists, but I can't really understand how do I use them? – jlively Apr 19 '17 at 09:36
6

The top answer didn´t work for me. After some investigation I found the required information on "Android Developer": https://developer.android.com/training/articles/security-ssl.html#SelfSigned

Creating an empty implementation of X509TrustManager did the trick:

private static class MyTrustManager implements 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;
    }

}

...

HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
try
{
    // Create an SSLContext that uses our TrustManager
    SSLContext context = SSLContext.getInstance("TLS");
    TrustManager[] tmlist = {new MyTrustManager()};
    context.init(null, tmlist, null);
    conn.setSSLSocketFactory(context.getSocketFactory());
}
catch (NoSuchAlgorithmException e)
{
    throw new IOException(e);
} catch (KeyManagementException e)
{
    throw new IOException(e);
}
conn.setRequestMethod("GET");
int rcode = conn.getResponseCode();

Please be aware that this empty implementation of TustManager is just an example and using it in a productive environment would cause a severe security threat!

Markus Lenger
  • 521
  • 5
  • 7
  • 1
    just fyi - idk if it was like this at the time, but [they seem to strongly discourage this approach now](https://developer.android.com/training/articles/security-ssl.html#UnknownCa) (see the note) – Saik Caskey Mar 30 '20 at 14:47
4

Simplest way for create SSL certificate

Open Firefox (I suppose it's also possible with Chrome, but it's easier for me with FF)

Visit your development site with a self-signed SSL certificate.

Click on the certificate (next to the site name)

Click on "More information"

Click on "View certificate"

Click on "Details"

Click on "Export..."

Choose "X.509 Certificate whith chain (PEM)", select the folder and name to save it and click "Save"

Go to command line, to the directory where you downloaded the pem file and execute "openssl x509 -inform PEM -outform DM -in .pem -out .crt"

Copy the .crt file to the root of the /sdcard folder inside your Android device Inside your Android device, Settings > Security > Install from storage.

It should detect the certificate and let you add it to the device Browse to your development site.

The first time it should ask you to confirm the security exception. That's all.

The certificate should work with any browser installed on your Android (Browser, Chrome, Opera, Dolphin...)

Remember that if you're serving your static files from a different domain (we all are page speed bitches) you also need to add the certificate for that domain.

Mani kandan
  • 359
  • 4
  • 12
4

Here's how you can add additional certificates to your KeyStore to avoid this problem: Trusting all certificates using HttpClient over HTTPS

It won't prompt the user like you ask, but it will make it less likely that the user will run into a "Not trusted server certificate" error.

Community
  • 1
  • 1
emmby
  • 99,783
  • 65
  • 191
  • 249
  • Just for test purposes, you cannot publish an app in the Play Store with this trick because it will be rejected – ariel Jun 15 '17 at 18:32
2

I wrote small library ssl-utils-android to trust particular certificate on Android.

You can simply load any certificate by giving the filename from assets directory.

Usage:

OkHttpClient client = new OkHttpClient();
SSLContext sslContext = SslUtils.getSslContextForCertificateFile(context, "BPClass2RootCA-sha2.cer");
client.setSslSocketFactory(sslContext.getSocketFactory());
klimat
  • 24,711
  • 7
  • 63
  • 70
2

None of these fixes worked for my develop platform targeting SDK 16, Release 4.1.2, so I found a workaround.

My app stores data on server using "http://www.example.com/page.php?data=somedata"

Recently page.php was moved to "https://www.secure-example.com/page.php" and I keep getting "javax.net.ssl.SSLException: Not trusted server certificate".

Instead of accepting all certificates for only a single page, starting with this guide I solved my problem writing my own page.php published on "http://www.example.com/page.php"

<?php

caronte ("https://www.secure-example.com/page.php");

function caronte($url) {
    // build curl request
    $ch = curl_init();
    foreach ($_POST as $a => $b) {
        $post[htmlentities($a)]=htmlentities($b);
    }
    curl_setopt($ch, CURLOPT_URL,$url);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS,http_build_query($post));

    // receive server response ...
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $server_output = curl_exec ($ch);
    curl_close ($ch);

    echo $server_output;
}

?>
Community
  • 1
  • 1
Rudy
  • 181
  • 2
  • 10
1

Jan 19th, 2020 Self Signed Certificate ISSUE FIX:

To play video , image , calling webservice for any self signed certificate or connecting to any unsecured url just call this method before performing any action , it will fix your issue regarding certificate issue :

KOTLIN CODE

  private fun disableSSLCertificateChecking() {
        val hostnameVerifier = object: HostnameVerifier {
            override fun verify(s:String, sslSession: SSLSession):Boolean {
                return true
            }
        }
        val trustAllCerts = arrayOf<TrustManager>(object: X509TrustManager {
            override fun getAcceptedIssuers(): Array<X509Certificate> {
                TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
            }

            //val acceptedIssuers:Array<X509Certificate> = null
            @Throws(CertificateException::class)
            override fun checkClientTrusted(arg0:Array<X509Certificate>, arg1:String) {// Not implemented
            }
            @Throws(CertificateException::class)
            override fun checkServerTrusted(arg0:Array<X509Certificate>, arg1:String) {// Not implemented
            }
        })
        try
        {
            val sc = SSLContext.getInstance("TLS")
            sc.init(null, trustAllCerts, java.security.SecureRandom())
            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory())
            HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier)
        }
        catch (e: KeyManagementException) {
            e.printStackTrace()
        }
        catch (e: NoSuchAlgorithmException) {
            e.printStackTrace()
        }
    }
Shobhakar Tiwari
  • 7,862
  • 4
  • 36
  • 71
0

Maybe this will helpful... it works on java clients using self-signed certificates (there is no check of the certificate). Be careful and use it only for development cases because that is no secure at all!!

How to ignore SSL certificate errors in Apache HttpClient 4.0

Hope it will works on Android just adding HttpClient library... good luck!!

Community
  • 1
  • 1
EmP
  • 11
  • 2
  • 1
    No it does not work on android since it relies on deprecated methods that are not present in the Android variant :-( – kellyfj Feb 25 '14 at 14:21
0

This is problem resulting from lack of SNI(Server Name Identification) support inA,ndroid 2.x. I was struggling with this problem for a week until I came across the following question, which not only gives a good background of the problem but also provides a working and effective solution devoid of any security holes.

'No peer certificate' error in Android 2.3 but NOT in 4

Community
  • 1
  • 1
alumat
  • 59
  • 9