diff options
| -rw-r--r-- | app/assets/urls/bitmask demo.url | 3 | ||||
| -rw-r--r-- | app/assets/urls/calyx.url | 4 | ||||
| -rw-r--r-- | app/assets/urls/riseup.url | 3 | ||||
| -rw-r--r-- | app/build.gradle | 1 | ||||
| -rw-r--r-- | app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java | 28 | ||||
| -rw-r--r-- | app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java | 68 | ||||
| -rw-r--r-- | app/src/main/java/se/leap/bitmaskclient/Provider.java | 14 | ||||
| -rw-r--r-- | app/src/main/java/se/leap/bitmaskclient/ProviderManager.java | 52 | ||||
| -rw-r--r-- | app/src/main/res/values-es/strings.xml | 1 | ||||
| -rw-r--r-- | app/src/main/res/values/strings.xml | 1 | ||||
| -rw-r--r-- | app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java | 23 | ||||
| -rw-r--r-- | app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java | 43 | 
12 files changed, 167 insertions, 74 deletions
| diff --git a/app/assets/urls/bitmask demo.url b/app/assets/urls/bitmask demo.url index 1a412055..81bdba52 100644 --- a/app/assets/urls/bitmask demo.url +++ b/app/assets/urls/bitmask demo.url @@ -1,3 +1,4 @@  {
 -	"main_url" : "https://demo.bitmask.net/"
 +	"main_url" : "https://demo.bitmask.net/",
 +	"ca_cert_fingerprint" : "c3f9f39af6d42d1f201195dec918ac7603597049"
  }
 diff --git a/app/assets/urls/calyx.url b/app/assets/urls/calyx.url index 8de04fe9..9ae902f7 100644 --- a/app/assets/urls/calyx.url +++ b/app/assets/urls/calyx.url @@ -1,3 +1,5 @@  { -	"main_url" : "https://calyx.net/" +	"main_url" : "https://calyx.net/", +	"ca_cert_fingerprint" : "98086aee17b5800acd0bdefe852e7c1ae72bd248" +  } diff --git a/app/assets/urls/riseup.url b/app/assets/urls/riseup.url index 4548b433..e250e93f 100644 --- a/app/assets/urls/riseup.url +++ b/app/assets/urls/riseup.url @@ -1,3 +1,4 @@  { -	"main_url" : "https://riseup.net/" +	"main_url" : "https://riseup.net/", +	"ca_cert_fingerprint" : "c429750763322fcc20b6402d7b508ba9e754b176"  } diff --git a/app/build.gradle b/app/build.gradle index 4441cafa..c4286d29 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -45,6 +45,7 @@ dependencies {    compile 'com.github.pedrovgs:renderers:1.3+'    compile 'com.intellij:annotations:12.0'    compile 'com.google.code.gson:gson:2+' +  compile 'org.thoughtcrime.ssl.pinning:AndroidPinning:1.0.0'  }  def processFileInplace(file, Closure processText) { diff --git a/app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java b/app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java index 59d77d83..8910891a 100644 --- a/app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java +++ b/app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java @@ -126,7 +126,7 @@ public class ConfigurationWizard extends Activity          if (fragment_manager.findFragmentByTag(ProviderDetailFragment.TAG) == null && setting_up_provider) {              if (selected_provider != null) -                onItemSelectedUi(selected_provider); +                onItemSelectedUi();              if (progress > 0)                  mProgressBar.setProgress(progress);          } @@ -231,20 +231,18 @@ public class ConfigurationWizard extends Activity      void onItemSelected(int position) {          //TODO Code 2 pane view          selected_provider = adapter.getItem(position); -        onItemSelectedLogic(selected_provider); -        onItemSelectedUi(selected_provider); +        onItemSelectedLogic(); +        onItemSelectedUi();      } -    private void onItemSelectedLogic(Provider selected_provider) { -        boolean danger_on = true; -        if (preferences.contains(ProviderItem.DANGER_ON)) -            danger_on = preferences.getBoolean(ProviderItem.DANGER_ON, false); -        setUpProvider(selected_provider.mainUrl(), danger_on); +    private void onItemSelectedLogic() { +        boolean danger_on = preferences.getBoolean(ProviderItem.DANGER_ON, false); +        setUpProvider(danger_on);      } -    private void onItemSelectedUi(Provider provider) { +    private void onItemSelectedUi() {          startProgressBar(); -        adapter.hideAllBut(adapter.indexOf(provider)); +        adapter.hideAllBut(adapter.indexOf(selected_provider));      }      @Override @@ -383,21 +381,21 @@ public class ConfigurationWizard extends Activity      private void autoSelectProvider(Provider provider, boolean danger_on) {          preferences.edit().putBoolean(ProviderItem.DANGER_ON, danger_on).apply();          selected_provider = provider; -        onItemSelectedLogic(selected_provider); -        onItemSelectedUi(selected_provider); +        onItemSelectedLogic(); +        onItemSelectedUi();      }      /**       * Asks ProviderAPI to download a new provider.json file       * -     * @param provider_main_url       * @param danger_on         tells if HTTPS client should bypass certificate errors       */ -    public void setUpProvider(URL provider_main_url, boolean danger_on) { +    public void setUpProvider(boolean danger_on) {          Intent provider_API_command = new Intent(this, ProviderAPI.class);          Bundle parameters = new Bundle(); -        parameters.putString(Provider.MAIN_URL, provider_main_url.toString()); +        parameters.putString(Provider.MAIN_URL, selected_provider.mainUrl().toString());          parameters.putBoolean(ProviderItem.DANGER_ON, danger_on); +        parameters.putString(Provider.CA_CERT_FINGERPRINT, selected_provider.certificatePin());          provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);          provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); diff --git a/app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java b/app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java index 9b5601a9..6c57fca2 100644 --- a/app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java +++ b/app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java @@ -22,6 +22,10 @@ import android.content.res.*;  import android.os.*;  import android.util.*; +import org.apache.http.client.*; +import org.json.*; +import org.thoughtcrime.ssl.pinning.util.*; +  import java.io.*;  import java.math.*;  import java.net.*; @@ -32,10 +36,7 @@ import java.util.*;  import javax.net.ssl.*; -import org.apache.http.client.*; -import org.json.*; - -import se.leap.bitmaskclient.ProviderListContent.ProviderItem; +import se.leap.bitmaskclient.ProviderListContent.*;  import se.leap.bitmaskclient.eip.*;  /** @@ -88,6 +89,7 @@ public class ProviderAPI extends IntentService {      private static boolean go_ahead = true;      private static SharedPreferences preferences;      private static String provider_api_url; +    private static String provider_ca_cert_fingerprint;      private Resources resources;      public static void stop() { @@ -102,6 +104,7 @@ public class ProviderAPI extends IntentService {      public void onCreate() {          super.onCreate(); +          preferences = getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE);          resources = getResources();          CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER)); @@ -124,7 +127,7 @@ public class ProviderAPI extends IntentService {          final ResultReceiver receiver = command.getParcelableExtra(RECEIVER_KEY);          String action = command.getAction();          Bundle parameters = command.getBundleExtra(PARAMETERS); -        if (provider_api_url == null) { +        if (provider_api_url == null && preferences.contains(Provider.KEY)) {              try {                  JSONObject provider_json = new JSONObject(preferences.getString(Provider.KEY, "no provider"));                  provider_api_url = provider_json.getString(Provider.API_URL) + "/" + provider_json.getString(Provider.API_VERSION); @@ -136,12 +139,10 @@ public class ProviderAPI extends IntentService {          if (action.equalsIgnoreCase(SET_UP_PROVIDER)) {              Bundle result = setUpProvider(parameters); -            if (go_ahead) { -                if (result.getBoolean(RESULT_KEY)) { -                    receiver.send(PROVIDER_OK, result); -                } else { -                    receiver.send(PROVIDER_NOK, result); -                } +            if (result.getBoolean(RESULT_KEY)) { +                receiver.send(PROVIDER_OK, result); +            } else { +                receiver.send(PROVIDER_NOK, result);              }          } else if (action.equalsIgnoreCase(SIGN_UP)) {              UserSessionStatus.updateStatus(UserSessionStatus.SessionStatus.SIGNING_UP, resources); @@ -511,15 +512,20 @@ public class ProviderAPI extends IntentService {          int progress = 0;          Bundle current_download = new Bundle(); -        if (task != null && task.containsKey(ProviderItem.DANGER_ON) && task.containsKey(Provider.MAIN_URL)) { -            last_danger_on = task.getBoolean(ProviderItem.DANGER_ON); -            last_provider_main_url = task.getString(Provider.MAIN_URL); +        if (task != null) { +            last_danger_on = task.containsKey(ProviderItem.DANGER_ON) && task.getBoolean(ProviderItem.DANGER_ON); +            last_provider_main_url = task.containsKey(Provider.MAIN_URL) ? +                    task.getString(Provider.MAIN_URL) : +                    ""; +            provider_ca_cert_fingerprint = task.containsKey(Provider.CA_CERT_FINGERPRINT) ? +                    task.getString(Provider.CA_CERT_FINGERPRINT) : +                    "";              CA_CERT_DOWNLOADED = PROVIDER_JSON_DOWNLOADED = EIP_SERVICE_JSON_DOWNLOADED = false;              go_ahead = true;          }          if (!PROVIDER_JSON_DOWNLOADED) -            current_download = getAndSetProviderJson(last_provider_main_url, last_danger_on); +            current_download = getAndSetProviderJson(last_provider_main_url, last_danger_on, provider_ca_cert_fingerprint);          if (PROVIDER_JSON_DOWNLOADED || (current_download.containsKey(RESULT_KEY) && current_download.getBoolean(RESULT_KEY))) {              broadcastProgress(progress++);              PROVIDER_JSON_DOWNLOADED = true; @@ -608,11 +614,15 @@ public class ProviderAPI extends IntentService {          return hexData.toString();      } -    private Bundle getAndSetProviderJson(String provider_main_url, boolean danger_on) { +    private Bundle getAndSetProviderJson(String provider_main_url, boolean danger_on, String provider_ca_cert_fingerprint) {          Bundle result = new Bundle();          if (go_ahead) { -            String provider_dot_json_string = downloadWithCommercialCA(provider_main_url + "/provider.json", danger_on); +            String provider_dot_json_string; +            if(provider_ca_cert_fingerprint.isEmpty()) +                provider_dot_json_string = downloadWithCommercialCA(provider_main_url + "/provider.json", danger_on); +            else +                provider_dot_json_string = downloadWithCommercialCA(provider_main_url + "/provider.json", danger_on, provider_ca_cert_fingerprint);              try {                  JSONObject provider_json = new JSONObject(provider_dot_json_string); @@ -678,6 +688,29 @@ public class ProviderAPI extends IntentService {          return error_message;      } +    private String downloadWithCommercialCA(String url_string, boolean danger_on, String ca_cert_fingerprint) { +        String result = ""; + +        int seconds_of_timeout = 2; +        String[] pins = new String[] {ca_cert_fingerprint}; +        try { +            URL url = new URL(url_string); +            HttpsURLConnection connection = PinningHelper.getPinnedHttpsURLConnection(Dashboard.getContext(), pins, url); +            connection.setConnectTimeout(seconds_of_timeout * 1000); +            if (!LeapSRPSession.getToken().isEmpty()) +                connection.addRequestProperty(LeapSRPSession.AUTHORIZATION_HEADER, "Token token = " + LeapSRPSession.getToken()); +            result = new Scanner(connection.getInputStream()).useDelimiter("\\A").next(); +        } catch (IOException e) { +            if(e instanceof SSLHandshakeException) { +                result = danger_on ? downloadWithoutCA(url_string) : +                        formatErrorMessage(R.string.error_security_pinnedcertificate); +            } else +                result = formatErrorMessage(R.string.error_io_exception_user_message); +        } + +        return result; +    } +      /**       * Tries to download the contents of the provided url using commercially validated CA certificate from chosen provider.       * <p/> @@ -696,6 +729,7 @@ public class ProviderAPI extends IntentService {          try {              provider_url = new URL(string_url);              URLConnection url_connection = provider_url.openConnection(); +              url_connection.setConnectTimeout(seconds_of_timeout * 1000);              if (!LeapSRPSession.getToken().isEmpty())                  url_connection.addRequestProperty(LeapSRPSession.AUTHORIZATION_HEADER, "Token token = " + LeapSRPSession.getToken()); diff --git a/app/src/main/java/se/leap/bitmaskclient/Provider.java b/app/src/main/java/se/leap/bitmaskclient/Provider.java index ee06a586..54bfcc19 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Provider.java +++ b/app/src/main/java/se/leap/bitmaskclient/Provider.java @@ -32,6 +32,7 @@ public final class Provider implements Parcelable {      private JSONObject definition; // Represents our Provider's provider.json      private URL main_url; +    private String certificate_pin;      final public static String              API_URL = "api_uri", @@ -62,8 +63,9 @@ public final class Provider implements Parcelable {          this.main_url = main_url;      } -    public Provider(File provider_file) { - +    public Provider(URL main_url, String certificate_pin) { +        this.main_url = main_url; +        this.certificate_pin = certificate_pin;      }      public static final Parcelable.Creator<Provider> CREATOR @@ -81,11 +83,9 @@ public final class Provider implements Parcelable {          try {              main_url = new URL(in.readString());              String definition_string = in.readString(); -            if (definition_string != null) +            if (!definition_string.isEmpty())                  definition = new JSONObject((definition_string)); -        } catch (MalformedURLException e) { -            e.printStackTrace(); -        } catch (JSONException e) { +        } catch (MalformedURLException | JSONException e) {              e.printStackTrace();          }      } @@ -106,6 +106,8 @@ public final class Provider implements Parcelable {          return main_url;      } +    protected String certificatePin() { return certificate_pin; } +      protected String getName() {          // Should we pass the locale in, or query the system here?          String lang = Locale.getDefault().getLanguage(); diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java b/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java index 40fe8b5a..220a71c8 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java @@ -49,11 +49,14 @@ public class ProviderManager implements AdapteeCollection<Provider> {          Set<Provider> providers = new HashSet<Provider>();          try {              for (String file : relative_file_paths) { -                String main_url = extractMainUrlFromInputStream(assets_manager.open(directory + "/" + file)); -                providers.add(new Provider(new URL(main_url))); +                InputStream provider_file = assets_manager.open(directory + "/" + file); +                String main_url = extractMainUrlFromInputStream(provider_file); +                String certificate_pin = extractCertificatePinFromInputStream(provider_file); +                if(certificate_pin.isEmpty()) +                    providers.add(new Provider(new URL(main_url))); +                else +                    providers.add(new Provider(new URL(main_url), certificate_pin));              } -        } catch (MalformedURLException e) { -            e.printStackTrace();          } catch (IOException e) {              e.printStackTrace();          } @@ -75,30 +78,43 @@ public class ProviderManager implements AdapteeCollection<Provider> {                  String main_url = extractMainUrlFromInputStream(new FileInputStream(external_files_dir.getAbsolutePath() + "/" + file));                  providers.add(new Provider(new URL(main_url)));              } -        } catch (MalformedURLException e) { -            e.printStackTrace(); -        } catch (FileNotFoundException e) { +        } catch (MalformedURLException | FileNotFoundException e) {              e.printStackTrace();          }          return providers;      } -    private String extractMainUrlFromInputStream(InputStream input_stream_file_contents) { +    private String extractMainUrlFromInputStream(InputStream input_stream) {          String main_url = ""; -        byte[] bytes = new byte[0]; + +        JSONObject file_contents = inputStreamToJson(input_stream); +        if(file_contents != null) +            main_url = file_contents.optString(Provider.MAIN_URL); +        return main_url; +    } + +    private String extractCertificatePinFromInputStream(InputStream input_stream) { +        String certificate_pin = ""; + +        JSONObject file_contents = inputStreamToJson(input_stream); +        if(file_contents != null) +            certificate_pin = file_contents.optString(Provider.CA_CERT_FINGERPRINT); + +        return certificate_pin; +    } + +    private JSONObject inputStreamToJson(InputStream input_stream) { +        JSONObject json = null;          try { -            bytes = new byte[input_stream_file_contents.available()]; -            if (input_stream_file_contents.read(bytes) > 0) { -                JSONObject file_contents = new JSONObject(new String(bytes)); -                main_url = file_contents.getString(Provider.MAIN_URL); -            } -        } catch (IOException e) { -            e.printStackTrace(); -        } catch (JSONException e) { +            byte[] bytes = new byte[input_stream.available()]; +            if (input_stream.read(bytes) > 0) +                json = new JSONObject(new String(bytes)); +            input_stream.reset(); +        } catch (IOException | JSONException e) {              e.printStackTrace();          } -        return main_url; +        return json;      }      public Set<Provider> providers() { diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 7ab5150e..82ca44e9 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -48,6 +48,7 @@      <string name="setup_error_close_button">Salir</string>      <string name="setup_error_text">Sucedió un error configurando Bitmask con tu proveedor elegido.\n\nPuedes volver a intentarlo, o elegir otro proveedor.</string>      <string name="server_unreachable_message">No se ha detectado red para hablar con el servidor, inténtalo de nuevo.</string> +    <string name="error.security.pinnedcertificate">Error de seguridad, actualiza la aplicación o elige otro proveedor.</string>      <string name="malformed_url">No parece que sea un proveedor de Bitmask.</string>      <string name="certificate_error">No es un proveedor de Bitmak de confianza.</string>      <string name="service_is_down_error">El servicio está caído.</string> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ac6191a9..bcfd3a2c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -47,6 +47,7 @@      <string name="setup_error_close_button">Exit</string>      <string name="setup_error_text">There was an error configuring Bitmask with your chosen provider.\n\nYou may choose to reconfigure, or exit and configure a provider upon next launch.</string>      <string name="server_unreachable_message">Server is unreachable, please try again.</string> +    <string name="error.security.pinnedcertificate">Security error, update the app or choose another provider.</string>      <string name="malformed_url">It doesn\'t seem to be a Bitmask provider.</string>      <string name="certificate_error">This is not a trusted Bitmask provider.</string>      <string name="service_is_down_error">Service is down.</string> diff --git a/app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java b/app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java index 19ba1ba8..68ff9e47 100644 --- a/app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java +++ b/app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java @@ -126,7 +126,7 @@ public class ConfigurationWizard extends Activity          if (fragment_manager.findFragmentByTag(ProviderDetailFragment.TAG) == null && setting_up_provider) {              if (selected_provider != null) -                onItemSelectedUi(selected_provider); +                onItemSelectedUi();              if (progress > 0)                  mProgressBar.setProgress(progress);          } @@ -229,17 +229,17 @@ public class ConfigurationWizard extends Activity      void onItemSelected(int position) {          //TODO Code 2 pane view          selected_provider = adapter.getItem(position); -        onItemSelectedUi(selected_provider); -        onItemSelectedLogic(selected_provider); +        onItemSelectedUi(); +        onItemSelectedLogic();      } -    private void onItemSelectedLogic(Provider selected_provider) { -        setUpProvider(selected_provider.mainUrl()); +    private void onItemSelectedLogic() { +        setUpProvider();      } -    private void onItemSelectedUi(Provider provider) { +    private void onItemSelectedUi() {          startProgressBar(); -        adapter.hideAllBut(adapter.indexOf(provider)); +        adapter.hideAllBut(adapter.indexOf(selected_provider));      }      @Override @@ -379,8 +379,8 @@ public class ConfigurationWizard extends Activity      private void autoSelectProvider(Provider provider) {          selected_provider = provider; -        onItemSelectedUi(selected_provider); -        onItemSelectedLogic(selected_provider); +        onItemSelectedUi(); +        onItemSelectedLogic();      }      /** @@ -389,10 +389,11 @@ public class ConfigurationWizard extends Activity       * @param provider_name       * @param provider_main_url       */ -    public void setUpProvider(URL provider_main_url) { +    public void setUpProvider() {          Intent provider_API_command = new Intent(this, ProviderAPI.class);          Bundle parameters = new Bundle(); -        parameters.putString(Provider.MAIN_URL, provider_main_url.toString()); +        parameters.putString(Provider.MAIN_URL, selected_provider.mainUrl().toString()); +        parameters.putString(Provider.CA_CERT_FINGERPRINT, selected_provider.certificatePin());          provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);          provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); diff --git a/app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java b/app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java index 890262ce..a96556bc 100644 --- a/app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java +++ b/app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java @@ -34,6 +34,7 @@ import javax.net.ssl.*;  import org.apache.http.client.*;  import org.json.*; +import org.thoughtcrime.ssl.pinning.util.*;  import se.leap.bitmaskclient.ProviderListContent.ProviderItem;  import se.leap.bitmaskclient.eip.*; @@ -87,6 +88,7 @@ public class ProviderAPI extends IntentService {      private static boolean go_ahead = true;      private static SharedPreferences preferences;      private static String provider_api_url; +    private static String provider_ca_cert_fingerprint;      private Resources resources;      public static void stop() { @@ -504,13 +506,19 @@ public class ProviderAPI extends IntentService {          Bundle current_download = new Bundle();          if (task != null && task.containsKey(Provider.MAIN_URL)) { -            last_provider_main_url = task.getString(Provider.MAIN_URL); +            last_provider_main_url = task.containsKey(Provider.MAIN_URL) ? +                    task.getString(Provider.MAIN_URL) : +                    ""; +            provider_ca_cert_fingerprint = task.containsKey(Provider.CA_CERT_FINGERPRINT) ? +                    task.getString(Provider.CA_CERT_FINGERPRINT) : +                    ""; +              CA_CERT_DOWNLOADED = PROVIDER_JSON_DOWNLOADED = EIP_SERVICE_JSON_DOWNLOADED = false;              go_ahead = true;          }          if (!PROVIDER_JSON_DOWNLOADED) -            current_download = getAndSetProviderJson(last_provider_main_url); +            current_download = getAndSetProviderJson(last_provider_main_url, provider_ca_cert_fingerprint);          if (PROVIDER_JSON_DOWNLOADED || (current_download.containsKey(RESULT_KEY) && current_download.getBoolean(RESULT_KEY))) {              broadcastProgress(progress++);              PROVIDER_JSON_DOWNLOADED = true; @@ -602,11 +610,16 @@ public class ProviderAPI extends IntentService {          return hexData.toString();      } -    private Bundle getAndSetProviderJson(String provider_main_url) { +    private Bundle getAndSetProviderJson(String provider_main_url, String provider_ca_cert_fingerprint) {          Bundle result = new Bundle();          if (go_ahead) { -            String provider_dot_json_string = downloadWithCommercialCA(provider_main_url + "/provider.json"); +            String provider_dot_json_string; + +            if(provider_ca_cert_fingerprint.isEmpty()) +                provider_dot_json_string = downloadWithCommercialCA(provider_main_url + "/provider.json"); +            else +                provider_dot_json_string = downloadWithCommercialCA(provider_main_url + "/provider.json", provider_ca_cert_fingerprint);              try {                  JSONObject provider_json = new JSONObject(provider_dot_json_string); @@ -672,6 +685,28 @@ public class ProviderAPI extends IntentService {          return error_message;      } +    private String downloadWithCommercialCA(String url_string, String ca_cert_fingerprint) { +        String result = ""; + +        int seconds_of_timeout = 2; +        String[] pins = new String[] {ca_cert_fingerprint}; +        try { +            URL url = new URL(url_string); +            HttpsURLConnection connection = PinningHelper.getPinnedHttpsURLConnection(Dashboard.getContext(), pins, url); +            connection.setConnectTimeout(seconds_of_timeout * 1000); +            if (!LeapSRPSession.getToken().isEmpty()) +                connection.addRequestProperty(LeapSRPSession.AUTHORIZATION_HEADER, "Token token = " + LeapSRPSession.getToken()); +            result = new Scanner(connection.getInputStream()).useDelimiter("\\A").next(); +        } catch (IOException e) { +            if(e instanceof SSLHandshakeException) +                result = formatErrorMessage(R.string.error_security_pinnedcertificate); +            else +                result = formatErrorMessage(R.string.error_io_exception_user_message); +        } + +        return result; +    } +      /**       * Tries to download the contents of the provided url using commercially validated CA certificate from chosen provider.       * | 
