How to use TLS/SSL in Flutter with Dio

Mohamed Malkia
3 min readAug 9, 2023

--

Story

So some time ago a client of mine wanted me to make his app in Flutter and it had a payment section in it, so he tried to use a local bank API, now the bank did create documentation for us to use (even tho the documentation isn’t the best I’ve seen).

After reading the documentation the bank provided, the way to connect was clear although they use XML instead of JSON, the way this API works is there is only one endpoint, and depending on the body sent the server will know what kind of operation it needs to do but after a week of looking and testing it was challenging to find how to connect to the server because it requires you to send some certificates that the bank generated.

Tutorial

Now let’s move to the technical part of this blog we only need 2 packages for this to work

  dio: ^5.3.0
dio_http2_adapter: ^2.3.1

we need Dio as our HTTP package and dio_http2_adapter so we can have access to the adapters.

and also you need to add your certificates in your assets.

Now in my case, the server requires me to use all these 3 certificates (in your case might have the same files or fewer or different names) and don’t forget to import them in the pubspec.yaml.

The first thing is in the main.dart we need to add this piece of code in the main function:

import 'dart:io';

class MyHttpOverrides extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext? context) {
return super.createHttpClient(context)..badCertificateCallback = (X509Certificate cert, String host, int port) => true;
}
}

void main() async {
// your other code here.
HttpOverrides.global = MyHttpOverrides();
runApp(const MyApp());
}

After this, you can go to your function where you are going to make the request and firstly we are going to create an instance of Dio

final dio = Dio(BaseOptions(baseUrl: bankBaseUrl));

then we are going to load the certificates from the assets like this

ByteData clientCertificate = await rootBundle.load("assets/certificates/cert.pem");
ByteData privateKey = await rootBundle.load("assets/certificates/Key.pem");
ByteData rootCACertificate = await rootBundle.load("assets/certificates/ca.pem");

now we start injecting these certificates into Dio using the adapters, as you can see in the code below we need to use something called SecurityContext and then create an HttpClient with that context and return it

dio.httpClientAdapter = IOHttpClientAdapter(
createHttpClient: () {
final SecurityContext scontext = SecurityContext();

scontext.setTrustedCertificatesBytes(rootCACertificate.buffer.asUint8List());
scontext.usePrivateKeyBytes(privateKey.buffer.asUint8List());
scontext.useCertificateChainBytes(clientCertificate.buffer.asUint8List());

HttpClient client = HttpClient(context: scontext);

return client;
},
);

and Finally, you make your request like any other request.

final response = await dio.post(
"/Exec", // your API endpoint.
data: doc.toXmlString(), // your own data here
options: Options(
headers: {
HttpHeaders.contentTypeHeader: "text/xml",
},
),
);

Conclusion

Now as you can see how simple this is but when I needed it there weren’t sources, and everyone say something I went through GitHub issues and Stackoverflow and had to try all of them and find the solution

Full Code

import 'dart:io';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:flutter/services.dart';

class MyHttpOverrides extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext? context) {
return super.createHttpClient(context)..badCertificateCallback = (X509Certificate cert, String host, int port) => true;
}
}

void main() async {
// your other code here.
HttpOverrides.global = MyHttpOverrides();
runApp(const MyApp());
}

void requestAPI() async {
final dio = Dio(BaseOptions(baseUrl: bankBaseUrl));

ByteData clientCertificate = await rootBundle.load("assets/certificates/cert.pem");
ByteData privateKey = await rootBundle.load("assets/certificates/Key.pem");
ByteData rootCACertificate = await rootBundle.load("assets/certificates/ca.pem");

dio.httpClientAdapter = IOHttpClientAdapter(
createHttpClient: () {
final SecurityContext scontext = SecurityContext();

scontext.setTrustedCertificatesBytes(rootCACertificate.buffer.asUint8List());
scontext.usePrivateKeyBytes(privateKey.buffer.asUint8List());
scontext.useCertificateChainBytes(clientCertificate.buffer.asUint8List());

HttpClient client = HttpClient(context: scontext);

return client;
},
);

final response = await dio.post(
"/Exec", // your API endpoint.
data: doc.toXmlString(), // your own data here
options: Options(
headers: {
HttpHeaders.contentTypeHeader: "text/xml",
},
),
);
}

Hope you find this useful.

--

--