Native Encrypted Storage
A Flutter plugin for secure data storage with hardware-backed encryption using Android Keystore. All encryption happens in native code for maximum security.
Note: Currently supports Android only. iOS support is coming in a future update.
π Features
- β AES-256-GCM Encryption - Military-grade encryption
- β Hardware-Backed Security - Keys stored in Android Keystore
- β Native Implementation - All encryption happens in native code (Kotlin)
- β Simple API - Easy to use, similar to SharedPreferences
- β No External Dependencies - Uses only Android native APIs
- β Production Ready - Clean code without debug logs
- π iOS Support - Coming in a future update
π Why Use This Plugin?
The Problem with JWT and Other Tokens
JWT tokens and API keys are often stored in plain text or with Base64 encoding, which is NOT encryption:
// β INSECURE - Anyone with file access can read this
final prefs = await SharedPreferences.getInstance();
await prefs.setString('token', jwtToken);
Even though JWT is signed, the payload is readable by anyone:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiam9obiJ9.signature
β This is just Base64 - easily decoded!
The Solution
// β
SECURE - Encrypted with AES-256, key stored in hardware
await storage.write(key: 'token', value: jwtToken);
The token is now encrypted and impossible to read without the encryption key from Keystore/Keychain.
π¦ Installation
Add this to your pubspec.yaml:
dependencies:
native_encrypted_storage: ^0.1.0
Then run:
flutter pub get
π― Usage
Basic Example
import 'package:native_encrypted_storage/native_encrypted_storage.dart';
final storage = NativeEncryptedStorage();
// Save encrypted data
await storage.write(
key: 'user_token',
value: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
);
// Read and decrypt data
final token = await storage.read(key: 'user_token');
print(token); // Original value, automatically decrypted
// Delete data
await storage.delete(key: 'user_token');
// Delete all data
await storage.deleteAll();
// Check if key exists
final exists = await storage.containsKey(key: 'user_token');
// Get all keys
final keys = await storage.getAllKeys();
Real-World Example: Authentication
class AuthService {
final _storage = NativeEncryptedStorage();
// Save user token after login
Future<void> saveToken(String token) async {
await _storage.write(key: 'auth_token', value: token);
}
// Get token for API requests
Future<String?> getToken() async {
return await _storage.read(key: 'auth_token');
}
// Save user credentials (if needed)
Future<void> saveCredentials({
required String email,
required String password,
}) async {
await _storage.write(key: 'user_email', value: email);
await _storage.write(key: 'user_password', value: password);
}
// Logout - clear all data
Future<void> logout() async {
await _storage.deleteAll();
}
}
π How It Works
Architecture
βββββββββββββββββββββββββββββββββββββββββββ
β Your Flutter App β
β storage.write(key, value) β
βββββββββββββββββββ¬ββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββ
β Native Code (Kotlin) β
β 1. Get encryption key from Keystore β
β 2. Encrypt value with AES-256-GCM β
β 3. Save encrypted data β
ββββββββββββ¬βββββββββββββββ¬ββββββββββββββββ
β β
ββββββββββββ βββββββββββββββ
β Keystore β βSharedPrefs β
β π β β key β π¦ β
ββββββββββββ βββββββββββββββ
Hardware-backed Encrypted data
What Gets Encrypted?
| Item | Encrypted? | Location | Purpose |
|---|---|---|---|
| Encryption Key | β Hardware-protected | Android Keystore | Encrypts/decrypts data |
| Key (identifier) | β Plain text | SharedPreferences | Used to find data |
| Value (your data) | β AES-256-GCM | SharedPreferences | Your sensitive data |
Security Details
- Algorithm: AES-256-GCM (Galois/Counter Mode)
- Key Size: 256 bits
- IV: Random 12 bytes per encryption
- Authentication: 128-bit authentication tag
- Key Storage: Android Keystore (hardware-backed) / iOS Keychain
π‘οΈ Security Guarantees
β What This Plugin Protects Against
- File Access: Even if someone gets your app's data files, they cannot decrypt the data
- Backup Extraction: Encrypted data in backups is useless without the hardware key
- Rooted/Jailbroken Devices: Keys are hardware-protected and cannot be extracted
- Memory Dumps: Data is encrypted at rest, only decrypted when needed
β οΈ What This Plugin Does NOT Protect Against
- Runtime Access: If malware is running in your app's process, it can read decrypted data
- Screen Recording: Decrypted data shown on screen can be captured
- Debugger Attachment: A debugger can read decrypted values in memory
π± Platform Support
| Platform | Status | Minimum Version | Encryption Backend |
|---|---|---|---|
| Android | β Supported | API 23 (6.0) | Android Keystore |
| iOS | π Coming Soon | - | - |
π§ API Reference
Methods
write({required String key, required String value})
Encrypts and saves a value.
await storage.write(key: 'api_key', value: 'secret123');
read({required String key})
Reads and decrypts a value. Returns null if key doesn't exist.
final value = await storage.read(key: 'api_key');
delete({required String key})
Deletes a key-value pair.
await storage.delete(key: 'api_key');
deleteAll()
Deletes all stored data.
await storage.deleteAll();
containsKey({required String key})
Checks if a key exists.
final exists = await storage.containsKey(key: 'api_key');
getAllKeys()
Returns all stored keys.
final keys = await storage.getAllKeys();
π Comparison with Other Solutions
| Feature | Native Encrypted Storage | flutter_secure_storage | SharedPreferences | Hive (encrypted) |
|---|---|---|---|---|
| Hardware-backed | β Yes | β Yes | β No | β No |
| Encryption | β AES-256-GCM | β AES-CBC | β None | β AES-256 |
| Native Code | β Yes | β Yes | β Yes | β Dart only |
| Key Storage | β Keystore/Keychain | β Keystore/Keychain | β Plain text | β In app |
| Performance | β‘ Fast | β‘ Fast | β‘ Very Fast | β‘ Very Fast |
| Setup | β Zero config | β οΈ Requires config | β Zero config | β οΈ Manual key |
π‘ Best Practices
β DO
- Use for sensitive data (tokens, passwords, API keys)
- Use unique, descriptive keys
- Handle exceptions properly
- Clear data on logout
try {
await storage.write(key: 'token', value: token);
} catch (e) {
print('Failed to save token: $e');
}
β DON'T
- Store large files (use encrypted file storage instead)
- Store non-sensitive data (use SharedPreferences for better performance)
- Use the same key for different data types
- Forget to handle null returns from
read()
π Troubleshooting
Android: "Key not found" after app reinstall
This is expected behavior. The encryption key is tied to the app installation. When you uninstall and reinstall, the old encrypted data cannot be decrypted.
Solution: Clear data on first launch after reinstall.
Android: Encryption fails
Check that your minimum SDK version is met:
- Android: minSdkVersion 23 (Android 6.0)
iOS Support
iOS support is coming in a future update. Stay tuned!
π License
MIT License - see LICENSE file for details.
π€ Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
π§ Support
For issues and feature requests, please use the GitHub issue tracker.
π Acknowledgments
- Android Keystore documentation
- iOS Keychain Services documentation
- Flutter plugin development guide
Made with β€οΈ for secure Flutter apps