Flutter applications often need to store data locally, and Hive is a popular, lightweight NoSQL database solution for this purpose. However, storing sensitive information in plain text can pose significant security risks. Encrypting local storage with Hive is a crucial step to protect user data from unauthorized access.
What is Hive?
Hive is a fast and lightweight NoSQL database written in pure Dart. It’s well-suited for Flutter applications needing a local data store. Hive supports encryption, making it a good choice for applications handling sensitive data.
Why Encrypt Local Storage?
- Data Protection: Prevents unauthorized access to sensitive information.
- Compliance: Helps meet data protection regulations.
- Security: Mitigates the risk of data breaches, even if the device is compromised.
How to Encrypt Local Storage with Hive in Flutter
To implement encryption with Hive, you’ll need the Hive and Hive Flutter packages along with the encrypt
package to handle the encryption process. This approach provides a secure way to store and retrieve sensitive data in your Flutter app.
Step 1: Add Dependencies
Include the necessary dependencies in your pubspec.yaml
file:
dependencies:
flutter:
sdk: flutter
hive: ^2.2.3
hive_flutter: ^1.1.2
encrypt: ^5.0.1
dev_dependencies:
flutter_test:
sdk: flutter
hive_generator: ^2.0.1
build_runner: ^2.4.8
Run flutter pub get
to install the dependencies.
Step 2: Initialize Hive
In your main.dart
file, initialize Hive:
import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Hive.initFlutter();
// Register adapters if needed (e.g., Hive.registerAdapter(MyTypeAdapter()))
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Encrypted Hive Demo',
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Encrypted Hive Demo')),
body: Center(
child: ElevatedButton(
child: Text('Store Secret'),
onPressed: () async {
await storeSecret(context);
},
),
),
);
}
Future storeSecret(BuildContext context) async {
final box = await Hive.openBox('myBox');
box.put('secret', 'My Super Secret Data');
print('Secret stored');
await Future.delayed(Duration(seconds: 2));
final secret = box.get('secret');
print('Secret fetched: $secret');
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Stored Secret: $secret')));
}
}
Step 3: Generate Encryption Key
Hive requires an encryption key to secure the data. It’s best to generate a secure key and store it securely. This example generates a key, which should be managed securely in a real-world application.
import 'package:encrypt/encrypt.dart' as enc;
import 'package:hive/hive.dart';
Future generateKey() async {
final key = enc.Key.fromSecureRandom(32);
final encryptedKey = key.base64; // Store this securely (e.g., KeyChain)
print('Generated Encryption Key: $encryptedKey'); // Ensure key generation is logged
return HiveAesCipher(key);
}
Step 4: Open Encrypted Box
Open your Hive box with encryption:
import 'dart:io';
import 'package:encrypt/encrypt.dart' as enc;
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:path_provider/path_provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
Directory appDocDir = await getApplicationDocumentsDirectory();
Hive.init(appDocDir.path);
HiveCipher? hiveCipher = await generateKey(); // Generate or retrieve stored encryption key
// Delete box if exists
try {
await Hive.deleteBoxFromDisk('encryptedBox');
} catch (e) {
print("Error deleting the box before: $e");
}
final encryptedBox = await Hive.openBox('encryptedBox', encryptionCipher: hiveCipher);
encryptedBox.put('message', 'My Super Secret'); // Correct usage with encryptedBox
print('Stored to encypted Box');
runApp(MyApp(encryptedBox));
}
class MyApp extends StatelessWidget {
final Box encryptedBox;
MyApp(this.encryptedBox);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Encrypt Hive Data Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () async {
// Usage
try {
final storedMessage = encryptedBox.get('message'); // Correct usage with encryptedBox
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('The Stored Secret is : ${storedMessage}')));
print("retrieved The Encripted secret");
} catch (e) {
print('box failed because = $e');
}
},
child: Text('Print encrypted string!'))
],
),
),
),
);
}
}
Future generateKey() async {
final key = enc.Key.fromSecureRandom(32);
final encryptedKey = key.base64; // Store this securely (e.g., KeyChain)
print('Generated Encryption Key: $encryptedKey'); // Ensure key generation is logged
return HiveAesCipher(key);
}
Step 5: Securely Storing the Key
Storing the encryption key securely is vital. Using the flutter_secure_storage
package or platform-specific keystore solutions like Android’s KeyStore or iOS’s Keychain is highly recommended.
For using the KeyChain to strore secure date in the Flutter:
flutter pub add flutter_secure_storage
import 'package:encrypt/encrypt.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class SecureKeyStorage {
static const _key = 'hive_encryption_key';
final _storage = FlutterSecureStorage();
Future getKey() async {
try {
final storedKey = await _storage.read(key: _key);
if (storedKey == null) {
return null;
}
return Key.fromBase64(storedKey);
} catch (e) {
print('Error reading key: $e');
return null;
}
}
Future saveKey(Key key) async {
try {
await _storage.write(key: _key, value: key.base64);
print('Key saved successfully');
} catch (e) {
print('Error saving key: $e');
}
}
Future deleteKey() async {
try {
await _storage.delete(key: _key);
print('Key deleted successfully');
} catch (e) {
print('Error deleting key: $e');
}
}
}
Future generateKeySecure() async {
final secureKeyStorage = SecureKeyStorage();
Key? key = await secureKeyStorage.getKey();
if (key == null) {
key = Key.fromSecureRandom(32);
await secureKeyStorage.saveKey(key);
}
print('Encryption Key (Securely Retrieved/Generated): ${key.base64}');
return HiveAesCipher(key);
}
Complete Example: Integrating Encryption with Hive
Here’s a comprehensive example demonstrating how to integrate Hive encryption within your Flutter application:
import 'dart:io';
import 'package:encrypt/encrypt.dart';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Get the application documents directory
Directory appDocDir = await getApplicationDocumentsDirectory();
Hive.init(appDocDir.path);
// Securely retrieve or generate the encryption key
HiveCipher? hiveCipher = await SecureKeyManager.getHiveCipher();
try {
// Ensure the box is deleted before reopening if you encounter initialization issues during development.
// NEVER SHIP THIS TO PRODUCTION! This is purely for debugging and development.
await Hive.deleteBoxFromDisk('secureBox');
print('Deleted the old Hive box.');
} catch (e) {
print('Error deleting Hive box: $e');
}
// Open the box with the encryption cipher.
final secureBox = await Hive.openBox('secureBox', encryptionCipher: hiveCipher);
print('Opened secureBox successfully.');
secureBox.put('message', 'My Super Secret');
// Start the Flutter app
runApp(MyApp(secureBox));
}
class MyApp extends StatelessWidget {
final Box secureBox;
MyApp(this.secureBox);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Hive Encryption with Secure Storage'),
),
body: Center(
child: ElevatedButton(
onPressed: () async {
try {
// Retrieve data from the secure box
final storedMessage = secureBox.get('message');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('The Stored Secret is: $storedMessage'),
),
);
print("Retrieved the encrypted secret successfully");
} catch (e) {
print('Error retrieving encrypted value: $e');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to retrieve encrypted value. Check logs.'),
backgroundColor: Colors.red,
),
);
}
},
child: Text('Get encrypted string!'),
),
),
),
);
}
}
class SecureKeyManager {
static const _keyName = 'hive_box_key';
static final _secureStorage = FlutterSecureStorage();
/// Retrieve the HiveCipher
static Future getHiveCipher() async {
final key = await _getExistingKeyOrCreateNew();
if (key == null) return null; // Handle appropriately in a real app
return HiveAesCipher(key);
}
/// Attempt to get an existing key, or create a new one if it does not exist
static Future _getExistingKeyOrCreateNew() async {
// Check if the encryption key exists in secure storage
String? base64Key = await _secureStorage.read(key: _keyName);
if (base64Key == null) {
// Key does not exist, let's create a new one
final key = Key.fromSecureRandom(32);
base64Key = key.base64;
// Store the key securely
await _secureStorage.write(key: _keyName, value: base64Key);
print('Generated and stored a new encryption key');
} else {
print('Retrieved existing encryption key');
}
// Return the Key object
return Key.fromBase64(base64Key);
}
}
Conclusion
Encrypting local storage in Flutter with Hive enhances the security of your application by protecting sensitive data. Implementing Hive encryption ensures that even if a device is compromised, the data remains unreadable without the correct encryption key. Follow the steps outlined above, always manage encryption keys securely, and consider using secure storage options for the best data protection practices in your Flutter applications.