AES Encryption
Encrypting Data with AES in Flutter¶
AES (Advanced Encryption Standard) is a symmetric encryption algorithm widely used for securing data at rest. In Flutter, you can implement AES encryption using the pointycastle library, which provides cryptographic primitives and supports AES in various modes (e.g., CBC, GCM). This section demonstrates how to encrypt and decrypt data using AES in Flutter.
Prerequisites¶
Add the following dependencies to your pubspec.yaml:
Key Concepts¶
- Symmetric Encryption: Same key is used for encryption and decryption.
- AES Key Sizes: 128-bit, 192-bit, or 256-bit (commonly 256-bit for strong security).
- CBC Mode: Requires an Initialization Vector (IV) for each encryption operation.
- Padding: AES requires padding (e.g., PKCS7) to handle data that isn't a multiple of the block size.
Example: AES Encryption and Decryption¶
1. Generate a Secure Key¶
Use a cryptographically secure random key for production. For simplicity, this example uses a static key (not recommended for real apps):
import 'dart:typed_data';
import 'dart:math';
import 'package:pointycastle/export.dart';
Future<Uint8List> getSecureKey() async {
// Simulate secure key retrieval from Android Keystore or secure storage
final key = Uint8List(32);
final random = Random.secure();
random.nextBytes(key);
return key;
}
2. Encrypt Data¶
import 'dart:convert';
import 'dart:typed_data';
import 'dart:math';
import 'package:pointycastle/export.dart';
Future<String> encryptData(String plainText) async {
final key = await getSecureKey();
final iv = Uint8List(16);
final random = Random.secure();
random.nextBytes(iv);
final cipher = AESBlockCipher(
BlockCipherFeedbackModeWithPadding(
AES().createCipher(),
PKCS7Padding(),
),
);
cipher.init(true, KeyParameter(key));
final input = Uint8List.fromList(plainText.codeUnits);
final output = Uint8List(input.length);
int offset = 0;
while (offset < input.length) {
final length = cipher.processBlock(input, offset, output, offset);
offset += length;
}
final encrypted = Uint8List(16 + output.length);
encrypted.setRange(0, 16, iv);
encrypted.setRange(16, 16 + output.length, output);
return base64.encode(encrypted);
}
3. Decrypt Data¶
Future<String> decryptData(String encryptedBase64) async {
final encrypted = base64.decode(encryptedBase64);
final iv = Uint8List.fromList(encrypted.sublist(0, 16));
final ciphertext = Uint8List.fromList(encrypted.sublist(16));
final key = await getSecureKey();
final cipher = AESBlockCipher(
BlockCipherFeedbackModeWithPadding(
AES().createCipher(),
PKCS7Padding(),
),
);
cipher.init(false, KeyParameter(key));
final output = Uint8List(ciphertext.length);
int offset = 0;
while (offset < ciphertext.length) {
final length = cipher.processBlock(ciphertext, offset, output, offset);
offset += length;
}
// Remove PKCS7 padding
final paddingLength = output[output.length - 1];
final plaintext = Uint8List.fromList(output.sublist(0, output.length - paddingLength));
return String.fromCharCodes(plaintext);
}
Security Considerations¶
- Key Management: Never hardcode keys in your app. Use secure storage (e.g., Android Keystore, iOS Keychain) or derive keys from passwords using PBKDF2.
- IV Handling: Always generate a random IV for each encryption operation and store it alongside the ciphertext.
- Padding Oracle Attacks: Use secure padding schemes like PKCS7 and avoid predictable IVs.
- Algorithm Choice: AES is secure, but ensure you use proper modes (e.g., CBC with IV) and avoid ECB mode.
Diagram: AES Encryption Workflow¶
[Plain Text] → [Padding] → [AES Encryption (CBC)] → [Ciphertext + IV]
[Encrypted Data] → [AES Decryption (CBC)] → [Padding Removal] → [Plain Text]
Key takeaways¶
- Use
pointycastlefor AES encryption in Flutter. - Generate secure keys and random IVs for each encryption operation.
- Store keys in secure storage, not hardcoded in the app.
- Always use padding schemes like PKCS7 and avoid ECB mode.