Flutter – Encryption of stored data

Well , this is the first post I’m writing about Flutter , I came across with this environment about a month ago , and been working with quite intensively
Usually, the documentation when it comes to Flutter and Dart is very extensive and you would find the answer to every question quite easily, but when it comes to things I struggled with and built my own solution, I will add it to this blog.

This time the scenario is very simple and basic.

I want to save a file with data from my application and while doing it , I want to encrypt it, when reading the data, I obviously want to decrypt the data I read from the device.

First thing first: the Storage:
I found the Path_Provider package much more efficient than the well known and widely documented Shared-Preferences package.

Add the package to the pubspec.yaml of your project

path_provider: ^1.6.10

and import it to a new class

import 'package:path_provider/path_provider.dart';

In Path Provider there are actually 3 steps:

  • Setting the Save/Load Path
  • Setting the File Name (separated to allow easy usage with multiple files)
  • Initiating Write / Read function

obviously, all the functions will be asynchronous.

The function of the path will look something like:

Path Provider allows auto selection of different paths, for both Android and IOS, I prefer using the ExternalStorageDirectory just because it is accessible during development.

Future<String> get _localPath async {
    final directory = await getExternalStorageDirectory();
    return directory.path;
  }

The function of the File will be like:

Future<File> get _localFile async {
    final path = await _localPath;
    return File('$path/someFileName.txt');
  }

simply add the name of the file to the folder in the _localPath and return them together.

The Read and Write functions will simply do what they are meant to

Future<String> readData() async {
    try {
      final file = await _localAllProjectsFile;
      String data = await file.readAsString();
      return data;
    } catch (e) {
      return e;
    }
  }

The Write function will obviously receive a String to be saved

Future<File> writeData(String data) async {
    final file = await _localFile;
    return file.writeAsString(data);
  }

So far So good , everything is quite simple and straight-forward.

Next thing we would like to do is to integrate the Encryption/decryption before we save the file, and after we read the file, to make sure it is unreadable on the storage but still accessible in the application, once loaded.

I have found the “encrypt” package that does exactly that , and as usualy with great documentation, but nothing suggested any help about the process where i want it to be.

The Idea of the package is simple: 

  • Generate a random key
  • Generate an Initialization Vector (iv)
  • run the encryption/description with your favorite algorithm ( i prefer AES)

to do this , all you need is to initialize the variables:

   final key = Key.fromLength(32);
    final iv = IV.fromLength(16);
    final encrypter = Encrypter(AES(key));

Once initialized, all we need to do is to run the process itself and make the Read/Write action as mentioned above:

for Writing:

   final encrypted = encrypter.encrypt(data, iv: iv);
   return file.writeAsString(encrypted.base16);

and for Reading:

  String readData = await file.readAsString();
  final decrypted = encrypter.decrypt16(readData, iv: iv);

And that’s actually it, here is the full example of code for both processes described here.

import 'dart:io';
import 'package:encrypt/encrypt.dart';
import 'package:path_provider/path_provider.dart';

class HandleStorage {
  Future<String> get _localPath async {
    final directory = await getExternalStorageDirectory();
    return directory.path;
  }

  Future<File> get _localFile async {
    final path = await _localPath;
    return File('$path/fineName.txt');
  }

  Future<String> readData() async {
    try {
      final file = await _localFile;
      final key = Key.fromLength(32);
      final iv = IV.fromLength(16);
      final encrypter = Encrypter(AES(key));
      String data = await file.readAsString();
      final decrypted = encrypter.decrypt16(data, iv: iv);
      return decrypted;
    } catch (e) {
      return e;
    }
  }

  Future<File> writeFile(String data) async {
    final file = await _localFile;
    final key = Key.fromLength(32);
    final iv = IV.fromLength(16);
    final encrypter = Encrypter(AES(key));
    final encrypted = encrypter.encrypt(data, iv: iv);
    return file.writeAsString(encrypted.base16);
  }
}

Hope this helps, any comments will be appreciated.

Guy

Leave a Comment