AES Encryption - Decryption in Dart - Flutter
5,247
As @Yash Kadiya suggested, I went for Platform Specific code.
Posting it here, Happy coding!:
Flutter
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
void main() async {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Secure App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String encryptedData = '';
String decryptedData = '';
static const encryptionChannel = const MethodChannel('enc/dec');
Future<void> encryptData(String encrypted, String key) async {
try {
var result = await encryptionChannel.invokeMethod(
'encrypt',
{
'data': jsonString,
'key': key,
},
);
print('RETURNED FROM PLATFORM');
print(result);
setState(() {
encryptedData = result;
});
} on PlatformException catch (e) {
print('${e.message}');
}
}
Future<void> decryptData(String encrypted, String key) async {
try {
var result = await encryptionChannel.invokeMethod('decrypt', {
'data': encrypted,
'key': key,
});
print('RETURNED FROM PLATFORM');
print(result);
setState(() {
decryptedData = result;
});
} on PlatformException catch (e) {
print('${e.message}');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
MaterialButton(
child: Text('Encrypt Data'),
onPressed: () {
encryptData('data to be encrypted', '16 character long key');
},
),
MaterialButton(
child: Text('Decrypt Data'),
onPressed: () {
decryptData('data to be decrypted', '16 character long key');
// same key used to encrypt
},
),
Text(encryptedData),
Text(decryptedData),
],
),
),
);
}
}
Android
MainActivity.kt
package com.example.secureapp;
import android.util.Log
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterActivity() {
private val CHANNEL = "enc/dec";
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler{call, result ->
if(call.method.equals("encrypt")){
val data = call.argument<String>("data")
val key = call.argument<String>("key")
val cipher = CryptoHelper.encrypt(data, key)
result.success(cipher)
}else if(call.method.equals("decrypt")){
val data = call.argument<String>("data")
val key = call.argument<String>("key")
val jsonString = CryptoHelper.decrypt(data, key)
result.success(jsonString)
}else{
result.notImplemented()
}
}
}
}
CryptoHelper.java
package com.example.secureapp;
import android.util.Base64;
import android.util.Log;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class CryptoHelper {
static String keyValue;
private final IvParameterSpec ivSpec;
private final SecretKeySpec keySpec;
private Cipher cipher;
private final static String ivKey = "RF22SW76BV83EDH8"; //16 char secret key
public CryptoHelper() {
ivSpec = new IvParameterSpec(ivKey.getBytes());
keySpec = new SecretKeySpec(keyValue.getBytes(), "AES");
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
}
public static String encrypt(String valueToEncrypt, String key) throws Exception {
keyValue = key;
CryptoHelper enc = new CryptoHelper();
return Base64.encodeToString(enc.encryptInternal(valueToEncrypt), Base64.DEFAULT);
}
public static String decrypt(String valueToDecrypt, String key) throws Exception {
keyValue = key;
CryptoHelper enc = new CryptoHelper();
return new String(enc.decryptInternal(valueToDecrypt));
}
private byte[] encryptInternal(String text) throws Exception {
if (text == null || text.length() == 0) {
throw new Exception("Empty string");
}
byte[] encrypted;
try {
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
encrypted = cipher.doFinal(text.getBytes());
} catch (Exception e) {
throw new Exception("[encrypt] " + e.getMessage());
}
return encrypted;
}
private byte[] decryptInternal(String code) throws Exception {
if (code == null || code.length() == 0) {
throw new Exception("Empty string");
}
byte[] decrypted;
try {
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
decrypted = cipher.doFinal(Base64.decode(code, Base64.DEFAULT));
} catch (Exception e) {
throw new Exception("[decrypt] " + e.getMessage());
}
return decrypted;
}
}
IOS
Using CryptoSwift
library in ios side
Podfile (add this to podfile)
pod 'CryptoSwift'
then run pod install
AppDelegate.swift
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let encryptionChannel = FlutterMethodChannel(name: "enc/dec", binaryMessenger: controller.binaryMessenger)
encryptionChannel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
// Note: this method is invoked on the UI thread.
if(call.method == "encrypt"){
guard let args = call.arguments as? [String : Any] else {return}
let data = args["data"] as! String
let key = args["key"] as! String
let encryptedString = CryptoHelper.encrypt(dataFromFlutter: data, keyFromFlutter: key)
self?.encrypt(result: result, encrypted: encryptedString!)
return
}else if(call.method == "decrypt"){
guard let args = call.arguments as? [String : Any] else {return}
let data = args["data"] as! String
let key = args["key"] as! String
let decryptedString = CryptoHelper.decrypt(dataFromFlutter: data, keyFromFlutter: key)
self?.decrypt(result: result, decrypted: decryptedString!)
return
}else{
result(FlutterMethodNotImplemented)
return
}
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
private func encrypt(result: FlutterResult, encrypted: String) {
result(encrypted)
}
private func decrypt(result: FlutterResult, decrypted: String) {
result(decrypted)
}
}
CryptoHelper.swift
import Foundation
import CryptoSwift
var keyValue :String!
class CryptoHelper{
private static let iv = "RF22SW76BV83EDH8";
// ENC
public static func encrypt(dataFromFlutter :String, keyFromFlutter :String) -> String? {
do{
let encrypted: Array<UInt8> = try AES(key: keyFromFlutter, iv: iv, padding: .pkcs5).encrypt(Array(dataFromFlutter.utf8))
return encrypted.toBase64()
}catch{
return "DATA ERROR"
}
}
// DEC
public static func decrypt(dataFromFlutter :String, keyFromFlutter :String) -> String? {
do{
let data = Data(base64Encoded: dataFromFlutter)
let decrypted = try AES(key: keyFromFlutter, iv: iv, padding: .pkcs5).decrypt(data!.bytes)
return String(data: Data(decrypted), encoding: .utf8)
}catch{
return "DATA ERROR"
}
}
}
Author by
Vettiyanakan
Previously Web, IOS and Android developer, Now Fluttering with Dart.
Updated on December 30, 2022Comments
-
Vettiyanakan over 1 year
I have an app live in store with api AES encryption, I am trying to port the app to Flutter. I am stuck at the encryption/decryption part. I cannot change the encryption in api because its live.
I have tried the Encrypt, aes_crypt, pointycastle packages. Still stuck. not sure what I am missing. I couldn't find
IvParameterSpec
,SecretKeySpec
and("AES/CBC/PKCS5Padding")
options in these packages.This is the
CryptoHelper
java class I am using in the android app.package com.example.secureApp; import android.util.Base64; import java.security.NoSuchAlgorithmException; import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public class CryptoHelper { String keyValue; private final IvParameterSpec ivSpec; private final SecretKeySpec keySpec; private Cipher cipher; private final static String ivKey = "912QWA56CFB3SA3F"; // DUMMY 16 char secret key public CryptoHelper() { keyValue = "d2AQuZZDfTIlZeXW"; // DUMMY 16 char secret key ivSpec = new IvParameterSpec(ivKey.getBytes()); keySpec = new SecretKeySpec(keyValue.getBytes(), "AES"); try { cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { e.printStackTrace(); } } public static String encrypt(String valueToEncrypt) throws Exception { CryptoHelper enc = new CryptoHelper(); return Base64.encodeToString(enc.encryptInternal(valueToEncrypt), Base64.DEFAULT); } public static String decrypt(String valueToDecrypt) throws Exception { CryptoHelper enc = new CryptoHelper(); return new String(enc.decryptInternal(valueToDecrypt)); } private byte[] encryptInternal(String text) throws Exception { if (text == null || text.length() == 0) { throw new Exception("Empty string"); } byte[] encrypted; try { cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); encrypted = cipher.doFinal(text.getBytes()); } catch (Exception e) { throw new Exception("[encrypt] " + e.getMessage()); } return encrypted; } private byte[] decryptInternal(String code) throws Exception { if (code == null || code.length() == 0) { throw new Exception("Empty string"); } byte[] decrypted; try { cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); decrypted = cipher.doFinal(Base64.decode(code, Base64.DEFAULT)); } catch (Exception e) { throw new Exception("[decrypt] " + e.getMessage()); } return decrypted; } }
-
Yash Kadiya almost 3 yearsif nothing works for you than, you can create method channel for that specific java functionality, and call it from flutter.
-
Taha Asif almost 3 yearsI suppose you are not getting the correct encrypted and decrypted text after using above mentioned flutter packages. Show your flutter code as well.
-
Michael Fehr almost 3 yearsJust a note as your system is live already: in your encryptInternal-function your are converting the plaintext to bytes using this code: "encrypted = cipher.doFinal(text.getBytes());". Here is a high chance for errors because you do not define a Charset.
-
-
Arun Sriramula over 2 yearsWhile using flutter code having an issue with encryption channel, "Unhandled Exception: MissingPluginException(No implementation found for method encrypt on channel enc/dec)"
-
Vettiyanakan over 2 years@Arun Sriramula On wich platform? did you add the android and ios platform specific codes? on ios you have to add the library in pod file and run pod install.
-
Arun Sriramula over 2 years@Vettityanakan Platform is Flutter, although I got a simple solution from flutter's own library "encrypt".
-
Vettiyanakan over 2 years@ArunSriramula MissingPluginException this error is occurring because the android or ios specific code have not compiled. I was also using encrypt (pub.dev/packages/encrypt) package. There was some issue with the iv generation. I needed to generate IV with some specific letter. That was not possible with this package. Thats why i came up with this solution.
-
Arun Sriramula over 2 yearsWhat's the reason behind it's not compiling? Cause I've even tried flutter clean and running the project again and tried restarting the Android Studio too.
-
Vettiyanakan over 2 years@ArunSriramula Open android or ios project separately and try to run it.
-
Ramkesh Yadav almost 2 yearsYes, i was looking for this from a day. Thanks Dear!!
-
Ramkesh Yadav almost 2 yearsAndroid decryption is working smoothly, But for iOS its giving error ========== while decryption "2022-06-20 19:04:36.599505+0530 Runner[94495:7319981] Runner/CryptoHelper.swift:29: Fatal error: Unexpectedly found nil while unwrapping an Optional value"============== Nil Error coming in these 2 lines : let data = Data(base64Encoded: dataFromFlutter) let decrypted = try AES(key: keyFromFlutter, iv: iv, padding: .pkcs5).decrypt(data!.bytes)
-
Vettiyanakan almost 2 years@RamkeshYadav Please make sure dataFromFlutter :String, keyFromFlutter :String this values are not null. try to print it before using it.
-
Ramkesh Yadav almost 2 years@Vettiyanakan yes i have did a cross check, the values and key are not null and key is same which we have used for encryption, but "let data = Data(base64Encoded: dataFromFlutter)" unable to decode the data. Kindly suggest.