7

My server provides a Self Signed certificate when calling its HTTPS API. I have the certificate file in the asset folder and referenced its path in pubspec.yaml I have tried passing the certificate to SecurityContext and then using that context to create an HttpClient. But the way I'm passing the certificate to SecurityContext is not working. Here is the code:

Future<ByteData> getFileData(String path) async {
    return await rootBundle.load(path);
}

void initializeHttpClient() async {
    try {
         Future<ByteData> data = getFileData('assets/raw/certificate.crt');
         await data.then((value) {
             var context = SecurityContext.defaultContext;
             context.useCertificateChainBytes(value.buffer.asInt8List());
             client = HttpClient(context: context);
         });

    } on Exception catch (exception) {
         print(exception.toString());
    }
}

The SecurityContext has two methods:
1) useCertificateChain() this accepts a file path. But when I give the path of the file in my asset folder ('assets/raw/certificate.crt'). It says file not found.
2) useCertificateChainBytes() the above code is using this method. But this also gives me error like (unexpected end of file).

Solution as of now

I am bypassing it using client.badCertificateCallback = (X509Certificate cert, String host, int port)=> true;.

but I'd like to make it work with certificate

Richard Heap
  • 48,344
  • 9
  • 130
  • 112
Siddharth jain
  • 447
  • 1
  • 5
  • 13

1 Answers1

17

It's not clear from your question what the role of the self-signed certificate is. Based on your work around, I assume that it's a server side certificate that you have installed in the HTTPS server. (It's not a client side certificate that you would like to pass to the server.)

So, what you need to do is to get the Dart HttpClient to trust that certificate, which will be passed to it by the server as part of the TLS handshake. (By setting the callback you have made the client trust any certificate, not just your server's.)

To set the trusted certificate use setTrustedCertificatesBytes in place of useCertificateChainBytes (which you would use if your certificate was a client side one).

You cannot access assets directly as Files as they are bundled by the build. You are doing the right thing by loading them and using the ...Bytes methods. You could improve the readability of your code like this (removing the then). Also, note the subtle change to Uint8List

ByteData data = await rootBundle.load('assets/raw/certificate.crt');
SecurityContext context = SecurityContext.defaultContext;
context.setTrustedCertificatesBytes(data.buffer.asUint8List());
client = HttpClient(context: context);
Android Noob
  • 621
  • 8
  • 18
Richard Heap
  • 48,344
  • 9
  • 130
  • 112
  • Are you sure this code is working? Because as far as I know it will throw error of file not found exception. @Richard Heap – Hardy Dec 27 '19 at 06:14
  • I am still using it. If the file you provided is at that path and is of proper format it will work – Siddharth jain Dec 27 '19 at 10:12
  • 1
    yes it is working fine with Flutter native or dart native which is working in dart VM (dart.io) . but it is not working on Flutter web(dart.html) . we do not have classes that support SecurityContext and SecureSocket class for dart web . does any one have solution for same – SANU Feb 27 '20 at 11:15
  • @SANU It would be a huge security hole if a JavaScript program could control which certificates the browser trusted. For testing or internal use you could install the cert - see for example https://peacocksoftware.com/blog/make-chrome-auto-accept-your-self-signed-certificate. In production you'd need to use a properly signed cert. – Richard Heap Feb 27 '20 at 13:58