Flutter-ready Encryption Utility
Secure my Secrets
In my previous articles we’ve journeyed from the vanilla Flutter starter App, through separating the state, making it persistent via local-storage, and then via Cloud Storage with Google Firestore. The eventual next step in that progression will be adding authenticated database access. However in this article we’ll take a slight digression to create an assistant utility for later use relating to the process of authentication.
Have you ever needed to use an API key for a 3rd party network data service, or some static/preset credentials, in a Flutter app? Especially in a situation where the end-user is not required to first personally sign-in to the App before said API service (or backend-login) is called on? This generally raises two challenges:
- how or where to store those sensitive values (at compile time), to avoid unwanted disclosure?
- how to access or fetch those values (at runtime), to enable accessing information from a remote source?
A standard way to protect sensitive data is with encryption. For Flutter, a handy encryption library is Encrypt. 2-way encryption is supported, so we can pre-encrypt the values (e.g. an API key or static username/password) prior to compilation — meaning we don’t have those sensitive strings exposed in plain-text in the source code or in the subsequent app-bundle that is installed on end-user devices. At runtime we can access the encrypted values and decrypt them just before making that API call or submitting a sign-in operation. For added obscurity and manageability, we can store the encrypted values outside of the code — either locally in an asset-file or remotely in a database — fetching them at runtime. In that last case it means that we, the developer, can update the values as necessary without requiring an app-update and all the release-related overhead.
For the task of the pre-encryption we can create a handy 2-way encryption command-line utility in Dart using the very same Encrypt library as we can use within our Flutter app code.
The utility code and usage instructions are in the GitHub repo.
We’ll go over the code, show some simple usage examples, and how we can use the resulting encrypted strings in our Flutter App.
But really, how?
We’ll create a simple encrypt-and-decrypt command-line utility in Dart. This will not use the same Dart engine that is packaged with Flutter. Instead follow these directions to install the standalone Dart SDK.
Next, download the simple_cryption repo. As per the README.md, you need to run
pub get once to pull down the dart packages mentioned in the dependencies of the
pubspec.yaml — namely the Encrypt library and the Args package.
The Dart code is all in the
simple_cryption.dart file. It mostly consists of using the Args package to identify the flags and options, and whatever remains of the command-line will be treated as strings to be encrypted or decrypted. By default, if neither
-d are specified, then the strings will be encrypted (as if
-e was specified).
The usage options can be seen by running the utility without extra args:
Usage: dart simple_cryption.dart [ -e | -d ] [ -k <key> ] <string> ...
-e, --encrypt encrypt strings
(defaults to on)
-d, --decrypt decrypt strings
-k, --key use a non-default key (max 32 chars)
Here’s an example, using the super-sensitive(!?) string “fun with flutter”:
$ dart simple_cryption.dart -e "fun with flutter"
encrypt: fun with flutter --> XfmMLkgvdd66xG/PIPNZkgFIYDKtjvGY+ynjd/hb5TA=$ dart simple_cryption.dart -d XfmMLkgvdd66xG/PIPNZkgFIYDKtjvGY+ynjd/hb5TA=
decrypt: XfmMLkgvdd66xG/PIPNZkgFIYDKtjvGY+ynjd/hb5TA= --> fun with flutter
Or, using a non-default key:
$ dart simple_cryption.dart -e -k "encryption is the key" "fun with flutter"
encrypt: fun with flutter --> fUCCsVDLeBm2Jh/g0tIFfZ2929Avz8SP0c6sqAkFrFE=$ dart simple_cryption.dart -d -k "encryption is the key" fUCCsVDLeBm2Jh/g0tIFfZ2929Avz8SP0c6sqAkFrFE=
decrypt: fUCCsVDLeBm2Jh/g0tIFfZ2929Avz8SP0c6sqAkFrFE= --> fun with flutter
So, we can encrypt text. And we can take an encrypted string and extract the original plain-text — so long as we know and use the same key for both operations.
To use a resulting encrypted value in our flutter app, we can extract the relevant code from
final sourceStrings = <list of encrypted strings>;
final rawKey = <the same 32-char-string used for encryption>;
final key = Key.fromUtf8(rawKey);
final iv = IV.fromLength(16);
final encrypter = Encrypter(AES(key));
final decrypted = encrypter.decrypt64(source, iv: iv);
<use-or-assign the decrypted strings here>;
rawKey can have their values loaded from asset files (with source-control exclusion (e.g.
.gitignore)), or a remote database, keeping them completely outside of your source-code.
Note that in a situation as described in the opening paragraph, encryption alone does not completely secure your sensitive access-strings. For more details see the article about storing secrets in Flutter.
In my next article we’ll see how to utilise this as part of protecting and managing static credentials for the next iteration of the Stateless Persistent-Data Flutter App (see the links below for the evolution so far).