Skip to content

Capacitor Plugin for HTTP requests with self signed SSL certificates

Aymeric edited this page Oct 30, 2021 · 2 revisions

I'm using CapacitorJS for easy development with Android. I needed a way to do an HTTPS request to a box that uses self-signed SSL certificate. To accomplish it, I created my own capacitor plugin.

First, we need okhttp. To install it, open your build.gradle file in Android Studio, and in the dependencies section we add the below:

dependencies {
  […other implementation…]
  implementation 'com.squareup.okhttp3:okhttp:4.9.0'
  implementation 'com.squareup.okhttp3:okhttp-tls:4.9.2'
  implementation 'com.google.code.gson:gson:2.8.8'
}

Then, in the project structure, below java/path.to.your.project/ we create a file called HttpClientPlugin.java with the below content:

package path.to.your.project;

import com.getcapacitor.JSObject;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;

import java.io.IOException;
import java.security.cert.X509Certificate;

import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.tls.Certificates;
import okhttp3.tls.HandshakeCertificates;

// source: https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/CustomTrust.java
@CapacitorPlugin(name = "HttpClient")
public class HttpClientPlugin extends Plugin {
    final X509Certificate freeboxCertificateAuthority = Certificates.decodeCertificatePem(""
            + "-----BEGIN CERTIFICATE-----\n"
            + "MIIFmjCCA4KgAwIBAgIJAKLyz15lYOrYMA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNV\n"
            + "BAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxDjAMBgNVBAcMBVBhcmlzMRAwDgYDVQQK\n"
            + "DAdGcmVlYm94MRgwFgYDVQQDDA9GcmVlYm94IFJvb3QgQ0EwHhcNMTUwNzMwMTUw\n"
            + "OTIwWhcNMzUwNzI1MTUwOTIwWjBaMQswCQYDVQQGEwJGUjEPMA0GA1UECAwGRnJh\n"
            + "bmNlMQ4wDAYDVQQHDAVQYXJpczEQMA4GA1UECgwHRnJlZWJveDEYMBYGA1UEAwwP\n"
            + "RnJlZWJveCBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA\n"
            + "xqYIvq8538SH6BJ99jDlOPoyDBrlwKEp879oYplicTC2/p0X66R/ft0en1uSQadC\n"
            + "sL/JTyfgyJAgI1Dq2Y5EYVT/7G6GBtVH6Bxa713mM+I/v0JlTGFalgMqamMuIRDQ\n"
            + "tdyvqEIs8DcfGB/1l2A8UhKOFbHQsMcigxOe9ZodMhtVNn0mUyG+9Zgu1e/YMhsS\n"
            + "iG4Kqap6TGtk80yruS1mMWVSgLOq9F5BGD4rlNlWLo0C3R10mFCpqvsFU+g4kYoA\n"
            + "dTxaIpi1pgng3CGLE0FXgwstJz8RBaZObYEslEYKDzmer5zrU1pVHiwkjsgwbnuy\n"
            + "WtM1Xry3Jxc7N/i1rxFmN/4l/Tcb1F7x4yVZmrzbQVptKSmyTEvPvpzqzdxVWuYi\n"
            + "qIFSe/njl8dX9v5hjbMo4CeLuXIRE4nSq2A7GBm4j9Zb6/l2WIBpnCKtwUVlroKw\n"
            + "NBgB6zHg5WI9nWGuy3ozpP4zyxqXhaTgrQcDDIG/SQS1GOXKGdkCcSa+VkJ0jTf5\n"
            + "od7PxBn9/TuN0yYdgQK3YDjD9F9+CLp8QZK1bnPdVGywPfL1iztngF9J6JohTyL/\n"
            + "VMvpWfS/X6R4Y3p8/eSio4BNuPvm9r0xp6IMpW92V8SYL0N6TQQxzZYgkLV7TbQI\n"
            + "Hw6v64yMbbF0YS9VjS0sFpZcFERVQiodRu7nYNC1jy8CAwEAAaNjMGEwHQYDVR0O\n"
            + "BBYEFD2erMkECujilR0BuER09FdsYIebMB8GA1UdIwQYMBaAFD2erMkECujilR0B\n"
            + "uER09FdsYIebMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqG\n"
            + "SIb3DQEBCwUAA4ICAQAZ2Nx8mWIWckNY8X2t/ymmCbcKxGw8Hn3BfTDcUWQ7GLRf\n"
            + "MGzTqxGSLBQ5tENaclbtTpNrqPv2k6LY0VjfrKoTSS8JfXkm6+FUtyXpsGK8MrLL\n"
            + "hZ/YdADTfbbWOjjD0VaPUoglvo2N4n7rOuRxVYIij11fL/wl3OUZ7GHLgL3qXSz0\n"
            + "+RGW+1oZo8HQ7pb6RwLfv42Gf+2gyNBckM7VVh9R19UkLCsHFqhFBbUmqwJgNA2/\n"
            + "3twgV6Y26qlyHXXODUfV3arLCwFoNB+IIrde1E/JoOry9oKvF8DZTo/Qm6o2KsdZ\n"
            + "dxs/YcIUsCvKX8WCKtH6la/kFCUcXIb8f1u+Y4pjj3PBmKI/1+Rs9GqB0kt1otyx\n"
            + "Q6bqxqBSgsrkuhCfRxwjbfBgmXjIZ/a4muY5uMI0gbl9zbMFEJHDojhH6TUB5qd0\n"
            + "JJlI61gldaT5Ci1aLbvVcJtdeGhElf7pOE9JrXINpP3NOJJaUSueAvxyj/WWoo0v\n"
            + "4KO7njox8F6jCHALNDLdTsX0FTGmUZ/s/QfJry3VNwyjCyWDy1ra4KWoqt6U7SzM\n"
            + "d5jENIZChM8TnDXJzqc+mu00cI3icn9bV9flYCXLTIsprB21wVSMh0XeBGylKxeB\n"
            + "S27oDfFq04XSox7JM9HdTt2hLK96x1T7FpFrBTnALzb7vHv9MhXqAT90fPR/8A==\n"
            +  "-----END CERTIFICATE-----");

    // Only the "Intermediate CA" certificate worked… It's defined below
    // To get it, open the website in Chrome
    // then click to the "lock" icon to see certificates details
    // then in "Certificates", go to "Certificate Path"
    // then export to Base64 .cer
    final X509Certificate freeboxIntermediateCertificateAuthority = Certificates.decodeCertificatePem(""
            + "-----BEGIN CERTIFICATE-----\n"
            + "MIICTjCCAdOgAwIBAgICEzcwCgYIKoZIzj0EAwIwYTELMAkGA1UEBhMCRlIxDzAN\n"
            + "BgNVBAgMBkZyYW5jZTEOMAwGA1UEBwwFUGFyaXMxEzARBgNVBAoMCkZyZWVib3gg\n"
            + "U0ExHDAaBgNVBAMME0ZyZWVib3ggRUNDIFJvb3QgQ0EwHhcNMTUwOTAxMTgwODI3\n"
            + "WhcNMjUwODI5MTgwODI3WjBZMQswCQYDVQQGEwJGUjEPMA0GA1UECAwGRnJhbmNl\n"
            + "MRMwEQYDVQQKDApGcmVlYm94IFNBMSQwIgYDVQQDDBtGcmVlYm94IEVDQyBJbnRl\n"
            + "cm1lZGlhdGUgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASZh/Apn56RulcNKDqV\n"
            + "gVqTDusvVQK9kIgJD39MzpnbsxMWv16RKs5JXGNb21z5QsmDnKcjZt9TE+BPh4l0\n"
            + "KDmMtAL5q+I/r0lFuJE7JohWN47rPWb7hOl2N9RDY+6HqQyjZjBkMB0GA1UdDgQW\n"
            + "BBT6m56/7eLixv5eBCT7XHDeHaItXTAfBgNVHSMEGDAWgBTIB3c2GlbV6EIh2ErE\n"
            + "MJvFxMz/QTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAKBggq\n"
            + "hkjOPQQDAgNpADBmAjEA50+k9U6+hCSgpX53vBiiaZosuqViMTMB03VDsclTfDh7\n"
            + "PzhplpUmHnXp6G4FQfHqAjEAyJQABAn6zMxFbqrIjEFl8hR+BfbMXwtI0vcrlSzo\n"
            + "SOza2d0EaD2163o50tkZm4g6\n"
            +  "-----END CERTIFICATE-----");

    private final OkHttpClient client;
    public HttpClientPlugin() {
        HandshakeCertificates certificates = new HandshakeCertificates.Builder()
                .addTrustedCertificate(freeboxCertificateAuthority)
                .addTrustedCertificate(freeboxIntermediateCertificateAuthority)
                .addPlatformTrustedCertificates() // to allow all the other regular certificates
                .build();

        client = new OkHttpClient.Builder()
                .sslSocketFactory(certificates.sslSocketFactory(), certificates.trustManager())
                .build();
    }

    @PluginMethod()
    public void get(PluginCall call) {
        String url = call.getString("url");

        JSObject ret = new JSObject();
        ret.put("body", "ERROR");
        ret.put("code", 0);
        Request request = new Request.Builder()
                .url(url)
                .build();

        try (Response response = client.newCall(request).execute()) {
            String body = response.body().string();
            //System.out.println("[FREEBOX] "+ body);
            int responseCode = response.code();
            ret.put("body", body);
            ret.put("code", responseCode);
        } catch(IOException err) {
            System.out.println("[FREEBOX] "+ err);
            ret.put("body", err);
        }

        call.resolve(ret);
    }

    public static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
    @PluginMethod()
    public void post(PluginCall call) {
        String url = call.getString("url");
        JSObject jsonData = call.getObject("data");

        RequestBody postBody = RequestBody.Companion.create(jsonData.toString(), JSON);

        JSObject ret = new JSObject();
        ret.put("body", "ERROR");
        ret.put("code", 0);
        Request request = new Request.Builder()
                .url(url)
                .post(postBody)
                .build();

        try (Response response = client.newCall(request).execute()) {
            String body = response.body().string();
            int responseCode = response.code();
            //System.out.println("[FREEBOX] Code "+ responseCode);
            ret.put("body", body);
            ret.put("code", responseCode);
        } catch(IOException err) {
            System.out.println("[FREEBOX] "+ err);
            ret.put("body", err);
        }

        call.resolve(ret);
    }
}

Register the plugin in MainActivity.java (see the documentation).

In our JavaScript web app, we create a file called HttpClient.js:

import { registerPlugin } from '@capacitor/core';

const HttpClient = registerPlugin('HttpClient');

export default HttpClient;

We can finally call it in our main JavaScript web app:

import HttpClient from './components/HttpClient.js'

[]
// GET Request
let { body, code } = await HttpClient.get({ url: 'https://web.site' });
body = JSON.parse(body);
// POST request
let { body, code } = await HttpClient.post({ url: 'https://web.site', data:{hello:'world'}});
body = JSON.parse(body);
Clone this wiki locally