how to find or creat private.pem and public.pem in flutter - flutter

I want use rsa in flutter
I have the following code for flutter
But I do not know about the part test/private.pem and test/public.pem how it is made in flutter
Of course, I have private and public keys that are made in Java with a length of 1024
Can I put them here? Or not, and must the PEM file be created? How do I generate a PEM file?
Thank you for your help
Future<void> main () async {
final publicKey = await parseKeyFromFile<RSAPublicKey>('test/public.pem');
final privKey = await parseKeyFromFile<RSAPrivateKey>('test/private.pem');
final plainText = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit';
final encrypter = Encrypter(RSA(publicKey: publicKey, privateKey: privKey));
final encrypted = encrypter.encrypt(plainText);
final decrypted = encrypter.decrypt(encrypted);
print(decrypted); // Lorem ipsum dolor sit amet, consectetur adipiscing elit
print(encrypted.base64); // kO9EbgbrSwiq0EYz0aBdljHSC/rci2854Qa+nugbhKjidlezNplsEqOxR+pr1RtICZGAtv0YGevJBaRaHS17eHuj7GXo1CM3PR6pjGxrorcwR5Q7/bVEePESsimMbhHWF+AkDIX4v0CwKx9lgaTBgC8/yJKiLmQkyDCj64J3JSE=
}

there are 2 ways to set publicKey and privateKey.
from your project :
create a folder named as test/
inside it
like this
inside private.pem and public.pem paste your private and public key
respectively .
and get it using
final publicKey = await parseKeyFromFile<RSAPublicKey>('test/public.pem');
final privKey = await parseKeyFromFile<RSAPrivateKey>('test/private.pem');
above approach works good when public and private keys are constant
from string :
import 'package:encrypt/encrypt.dart';
import 'package:pointycastle/asymmetric/api.dart';
String privateKeyString="key goes here";
String publicKeyString="key goes here";
//create a instance of RSA key praser
RSAKeyParser keyParser = RSAKeyParser();
//and parse those string keys
RSAAsymmetricKey privateKeyParser = keyParser.parse(privateKeyString);
RSAAsymmetricKey publicKeyParser =keyParser.parse(publicKeyString);
final publicKey = RSAPublicKey(publicKeyParser.modulus!, publicKeyParser.exponent!);
final privKey;
if (privateKeyParser is RSAPrivateKey) {
privKey = RSAPrivateKey(privateKeyParser.modulus!,privateKeyParser.exponent!, privateKeyParser.p,privateKeyParser.q);
final plainText = 'hello world';
final encrypter = Encrypter(RSA(publicKey: publicKey, privateKey:privKey));
final encrypted = encrypter.encrypt(plainText);
final decrypted = encrypter.decrypt(encrypted);
}
it worked for me!!

You can use pointy castle package to do that: https://pub.dev/packages/pointycastle
Just generate a key pair (code below) and use it in your app. Good luck!
import 'package:pointycastle/export.dart';
import 'dart:math';
import 'dart:typed_data';
AsymmetricKeyPair<RSAPublicKey, RSAPrivateKey> generateRSAkeyPair(
SecureRandom secureRandom,
{int bitLength = 2048}) {
// Create an RSA key generator and initialize it
final keyGen = RSAKeyGenerator()
..init(ParametersWithRandom(
RSAKeyGeneratorParameters(BigInt.parse('65537'), bitLength, 64),
secureRandom));
// Use the generator
final pair = keyGen.generateKeyPair();
// Cast the generated key pair into the RSA key types
final myPublic = pair.publicKey as RSAPublicKey;
final myPrivate = pair.privateKey as RSAPrivateKey;
return AsymmetricKeyPair<RSAPublicKey, RSAPrivateKey>(myPublic, myPrivate);
}
SecureRandom exampleSecureRandom() {
final secureRandom = FortunaRandom();
final seedSource = Random.secure();
final seeds = <int>[];
for (int i = 0; i < 32; i++) {
seeds.add(seedSource.nextInt(255));
}
secureRandom.seed(KeyParameter(Uint8List.fromList(seeds)));
return secureRandom;
}
// here is how you generate key pair
final pair = generateRSAkeyPair(exampleSecureRandom());
final public = pair.publicKey; // to get public
final private = pair.privateKey; // i know, you get it :D

Related

Send an encrypted request to APi in Dart

I am trying to send an encrypted request to a specific API in dart, but without success - I don't have any experience with the Dart language.
This are the requirements:
The JSON to be sent is encrypted as follows: "AES/CBC/ZeroBytePadding", IV is generated according to SHA1PRNG with a length of 16 bytes.
The encrypted bytes are Base64 encoded. This results in the encryptedJson.
The hmac is generated from base64 encoded IV and the encryptedJson with "HmacSHA256".
A json will be generated: {"value":encryptedJson,"iv":initialisationVector,"mac":hmac}
This json will be base64 encoded and sent as an encrypted payload.
Can anyone help me? Thanks in advance!
This is the Dart Code so far.
import 'dart:convert';
import 'dart:core';
import 'package:crypto/crypto.dart' as crypto;
import 'package:encrypt/encrypt.dart' as enc;
String encrypt(String string) {
// json encryption
final enc.Key key = enc.Key.fromUtf8(env.get('password'));
final enc.IV iv = enc.IV.fromSecureRandom(IV_LENGTH);
final enc.Encrypter encrypter = enc.Encrypter(enc.AES(key, mode: enc.AESMode.cbc));
final encryptedJson = encrypter.encrypt(string, iv: iv);
final String IVBase64String = base64.encode(iv.bytes);
print('encrypted JSON: '+encryptedJson.base64);
print('decrypted JSON: '+encrypter.decrypt(encryptedJson, iv: iv));
crypto.Hmac hmacSha256 = new crypto.Hmac(crypto.sha256, key.bytes);
crypto.Digest sha256Result = hmacSha256.convert(iv.bytes + encryptedJson.bytes);
print('data: ' + encryptedJson.base64);
print('iv: ' + IVBase64String);
print('hmac: ' + sha256Result.toString());
// Payload
final encryptedText = "{\"value\":\""+encryptedJson.base64+"\",\"iv\":\""+IVBase64String+"\",\"mac\":\""+sha256Result.toString()+"\"}";
print('final: ' + jsonEncode(encryptedText));
return base64.encode(utf8.encode(encryptedText));
}
This is the JavaExample
import java.io.UnsupportedEncodingException;
import java.util.Base64;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class ApiJavaSample
{
private final Cipher cipher;
private final SecretKeySpec key;
private static final String TAG = "AESCrypt";
private static final int IV_LENGTH = 16;
private String cypher_mode = "AES/CBC/NoPadding";
private String cypher_mode_iv = "SHA1PRNG";
public static void main (String[] args)
{
try{
System.out.println("encrypting");
ApiJavaSample test = new ApiJavaSample("password");
String encryptedString = test.encrypt("{\"coupon_key\":\"011205358365\",\"location_id\":\"2\",\"device_key\":\"test_1234\"}");
System.out.println("encrpyted");
System.out.println(encryptedString);
}
catch(Exception e)
{
System.out.println(e);
}
}
public ApiJavaSample(String password) throws Exception
{
// hash password with SHA-256 and crop the output to 128-bit for key
//MessageDigest digest = MessageDigest.getInstance("SHA-256");
//digest.Updater(password.getBytes("UTF-8"));
byte[] keyBytes = password.getBytes();
cipher = Cipher.getInstance(cypher_mode);
key = new SecretKeySpec(keyBytes, "AES");
}
private String hmacDigest(String msg, String algo)
{
String digest = null;
try
{
//SecretKeySpec key = new SecretKeySpec((keyString).getBytes("UTF-8"), algo);
Mac mac = Mac.getInstance(algo);
mac.init(key);
byte[] bytes = mac.doFinal(msg.getBytes("UTF-8"));
StringBuilder hash = new StringBuilder();
for (int i = 0; i < bytes.length; i++)
{
String hex = Integer.toHexString(0xFF & bytes[i]);
if (hex.length() == 1)
{
hash.append('0');
}
hash.append(hex);
}
digest = hash.toString();
}
catch (UnsupportedEncodingException | InvalidKeyException e)
{
e.printStackTrace();
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
return digest;
}
public String encrypt(String plainText) throws Exception
{
byte[] iv_bytes = generateIv();
AlgorithmParameterSpec spec = new IvParameterSpec(iv_bytes);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
int blockSize = cipher.getBlockSize();
while (plainText.length() % blockSize != 0) {
plainText += "\0";
}
byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8"));
String encryptedText = Base64.getEncoder().encodeToString(encrypted);
String iv_base64_string = Base64.getEncoder().encodeToString(iv_bytes);
String mac = hmacDigest(iv_base64_string + encryptedText.trim(), "HmacSHA256");
//JSONObject encryptedJson = new JSONObject();
//encryptedJson.put("value", encryptedText.trim());
//encryptedJson.put("iv", iv_base64_string);
//encryptedJson.put("mac", mac);
String base64Encrypt = "{\"value\":\""+encryptedText.trim()+"\",\"iv\":\""+iv_base64_string+"\",\"mac\":\""+mac+"\"}";
return Base64.getEncoder().encodeToString(base64Encrypt.getBytes());
}
private byte[] generateIv() throws NoSuchAlgorithmException
{
SecureRandom random = SecureRandom.getInstance(cypher_mode_iv);
byte[] iv = new byte[IV_LENGTH];
random.nextBytes(iv);
return iv;
}
}
Here is my test data:
Plaintext:
"{\"coupon_key\":\"382236526272\",\"location_id\":\"2\",\"device_key\":\"test_1234\"}"
Key:
33a485cb146e1153c69b588c671ab474
The following has to be changed/optimized in the Dart code:
The Java code uses Zero padding. PointyCastle and the encrypt package (a PointyCastle wrapper) do not support Zero padding (to my knowledge). A possible approach for the Dart code is to disable the default PKCS#7 padding in combination with a custom implementation for Zero padding.
The Java code applies the Base64 encoded data for the HMAC, while the Dart code uses the raw data. This has to be changed.
The Base64 encoding of the IV is obtained more efficiently with iv.base64.
Thus, the code is to be changed as follows:
import 'package:crypto/crypto.dart' as crypto;
import 'package:encrypt/encrypt.dart' as enc;
import 'package:convert/convert.dart';
import 'dart:typed_data';
import 'dart:convert';
String encrypt(String string) {
final enc.Key key = enc.Key.fromUtf8(env.get('password')); // Valid AES key
final enc.IV iv = enc.IV.fromSecureRandom(IV_LENGTH); // IV_LENGTH = 16
final dataPadded = pad(Uint8List.fromList(utf8.encode(string)), 16);
final enc.Encrypter encrypter = enc.Encrypter(enc.AES(key, mode: enc.AESMode.cbc, padding: null));
final encryptedJson = encrypter.encryptBytes(dataPadded, iv: iv);
crypto.Hmac hmacSha256 = crypto.Hmac(crypto.sha256, key.bytes);
crypto.Digest sha256Result = hmacSha256.convert(utf8.encode(iv.base64 + encryptedJson.base64));
final encryptedText = "{\"value\":\""+encryptedJson.base64+"\",\"iv\":\""+iv.base64+"\",\"mac\":\""+sha256Result.toString()+"\"}";
return base64.encode(utf8.encode(encryptedText));
}
Uint8List pad(Uint8List plaintext, int blockSize){
int padLength = (blockSize - (plaintext.lengthInBytes % blockSize)) % blockSize;
if (padLength != 0) {
BytesBuilder bb = BytesBuilder();
Uint8List padding = Uint8List(padLength);
bb.add(plaintext);
bb.add(padding);
return bb.toBytes();
}
else {
return plaintext;
}
}
Test (using a static IV to allow comparison between the ciphertexts of the two codes):
Key: enc.Key.fromUtf8("5432109876543210")
IV: enc.IV.fromUtf8("0123456789012345")
Plaintext: "{\"coupon_key\":\"011205358365\",\"location_id\":\"2\",\"device_key\":\"test_1234\"}"
Result: eyJ2YWx1ZSI6InNRTjJ0OWc5ZWY2RzdNV2RsOFB3emlXSlQwclNxUWJ2ZnN0eCtpMmNtSTQyUXJjUGRNV0JLbTlRZ2kxdmM0dElna2NOZEJsOVpEM0JlYTFPZ1kxaHNSeklSbHM1TnlaN0s1T2NqMTEzdkdvPSIsIml2IjoiTURFeU16UTFOamM0T1RBeE1qTTBOUT09IiwibWFjIjoiMzkwYzlhMzAxMjAxYjc1MWUxNjBhM2JlZTdmZGU5YzE5ZDY0MzJlNTBjOTJhNTg0ODBhMTJkNTYyNWRkYWMyNSJ9
After the changes, both codes return the above result for the above input data.
Security:
Typically, an AES key is a randomly generated byte sequence and not a string. If the key is to be derived from a passphrase/string, a reliable key derivation like PBKDF2 is to be used.
Zero padding is unreliable, so the reliable PKCS#7 padding that most libraries use by default should be applied. If the Java code had used PKCS#7 padding, porting would have been easier.
For encoding/decoding the charset should be specified (e.g. getBytes(StandardCharsets.UTF_8)), otherwise the default encoding will be used (which might not be wanted).
Using the same key for encryption and integrity checking for AES/HMAC is not a pressing security issue, but should be avoided as a preventive measure, see here.
The code is partially inefficient, e.g. when concatenating the Base64 encoded data instead of the raw data to determine the HMAC.

convert this function c# encryption and decryption to Dart/Flutter?

welcome everybody
I moved from xamarin to Flutter
I encountered some problems
Including encryption and decryption
How can I convert this function to Dart/Flutter?
This function is required to communicate with the api
Thank you everyone
public static string encryp(string x, string encrypt)//function
{
try
{
string y = x;
byte[] etext = UTF8Encoding.UTF8.GetBytes(y);
string Code = encrypt;
MD5CryptoServiceProvider mdhash = new MD5CryptoServiceProvider();
byte[] keyarray = mdhash.ComputeHash(UTF8Encoding.UTF8.GetBytes(Code));
TripleDESCryptoServiceProvider tds = new TripleDESCryptoServiceProvider();
tds.Key = keyarray;
tds.Mode = CipherMode.ECB;
tds.Padding = PaddingMode.PKCS7;
ICryptoTransform itransform = tds.CreateEncryptor();
byte[] result = itransform.TransformFinalBlock(etext, 0, etext.Length);
string encryptresult = Convert.ToBase64String(result);
return encryptresult.ToString();
}
catch (Exception ex)
{
return (ex.Message==null ?"": ex.Message);
}
}
public static string decrypt(string x, string keyai)
{
try
{
string y = x.Replace("\0", null);
byte[] etext = Convert.FromBase64String(y);
string key = keyai;
MD5CryptoServiceProvider mdhash = new MD5CryptoServiceProvider();
byte[] keyarray = mdhash.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
TripleDESCryptoServiceProvider tds = new TripleDESCryptoServiceProvider();
tds.Key = keyarray;
tds.Mode = CipherMode.ECB;
tds.Padding = PaddingMode.PKCS7;
ICryptoTransform itransform = tds.CreateDecryptor();
byte[] result = itransform.TransformFinalBlock(etext, 0, etext.Length);
string dencryptresult = UTF8Encoding.UTF8.GetString(result);
return dencryptresult.ToString();
}
catch (Exception ex)
{
return (ex.Message==null ?"": ex.Message);
}
}
update
I wrote this code on Flutter
import 'package:dart_des/dart_des.dart' as des3;
String encryptDataE(String _plainText, String _key) {
var bytes = new List<int>.from(utf8.encode(_plainText));
var key = md5.convert(utf8.encode(_key)).bytes; //The key is any letters
des3.DES3 mDes3CBC = des3.DES3(
key: key,
mode: des3.DESMode.ECB,
paddingType: des3.DESPaddingType.PKCS7,
);
final encrypted = mDes3CBC.encrypt(bytes);
String value = base64Encode(encrypted);
return value;
}
String decryptDataD(String _plainText, String _key) {
String plainText = _plainText.replaceAll("\0", null);
var bytes = base64.decode(plainText);
var key = md5.convert(utf8.encode(_key)).bytes; //The key is any letters
des3.DES3 mDes3CBC = des3.DES3(
key: key,
mode: des3.DESMode.ECB,
paddingType: des3.DESPaddingType.PKCS7,
);
final decrypt= mDes3CBC.decrypt(bytes);
String value = utf8.decode(decrypt);
return value;
}
After experimenting with encryption and decoding, this works now
One point left, how can this be achieved?
//string y = x.Replace("\0", null);//c#
String plainText = _plainText.replaceAll("\0", null); //I tried with this and it gets an error
Consider using this tool: "Use the tool e.g. for porting your Xamarin/UWP project to Flutter"
Otherwise, there is a pretty easy Flutter Encrypt package here
//Package example
import 'package:encrypt/encrypt.dart';
void main() {
final plainText = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit';
final key = Key.fromUtf8('my 32 length key................');
final iv = IV.fromLength(16);
final encrypter = Encrypter(AES(key));
final encrypted = encrypter.encrypt(plainText, iv: iv);
final decrypted = encrypter.decrypt(encrypted, iv: iv);
print(decrypted); // Lorem ipsum dolor sit amet, consectetur adipiscing elit
print(encrypted.base64); // R4PxiU3h8YoIRqVowBXm36ZcCeNeZ4s1OvVBTfFlZRdmohQqOpPQqD1YecJeZMAop/hZ4OxqgC1WtwvX/hP9mw==
}

Flutter how to use AES GCM 256 algorithm to cipher and decrypt using pointycastle package [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed last year.
Improve this question
I have two functions witch cipher and decrypt data with AES-GCM algorithm but using cryptography package. I don't think its bad, it helped me a lot, but I want to translate them so I can use pointycastle package for all my different encryption and decryption algorithms.
Functions using cryptography package are:
Future<CipherDataHolder> cipher(String clearValue) async {
final algorithm = AesGcm.with256bits(nonceLength: 12); //128bits MAC default
final SecretKey secretKey = await algorithm.newSecretKey();
final List<int> nonce = algorithm.newNonce();
final secretBox = await algorithm.encrypt(
utf8.encode(clearValue),
secretKey: secretKey,
nonce: nonce,
);
String cipherText = base64.encode(secretBox.concatenation());
String passphrase = base64.encode(await secretKey.extractBytes());
return CipherDataHolder(ciphertext: cipherText, passphrase: passphrase);
}
Future<String> decrypt(CipherDataHolder secretData) async {
final Uint8List cipherData = base64.decode(secretData.ciphertext);
final Uint8List ciphertext = cipherData.sublist(12, cipherData.length - 16);
final Uint8List iv = cipherData.sublist(0, 12);
final Uint8List mac = cipherData.sublist(cipherData.length - 16);
List<int> passphrase = base64.decode(secretData.passphrase);
final SecretKey secretKey = SecretKey(passphrase);
final SecretBox secretBox = SecretBox(ciphertext, nonce: iv, mac: Mac(mac));
final List<int> clearValue = await AesGcm.with256bits().decrypt(secretBox, secretKey: secretKey);
return utf8.decode(clearValue);
}
CipherDataHolder is just a class to hold the values
class CipherDataHolder {
CipherDataHolder({required this.ciphertext, required this.passphrase});
String ciphertext;
String passphrase;
}
/////////////////////////////////////////////////////////////////////
UPDATE
/////////////////////////////////////////////////////////////////////
Here is what I have tried till now to translate those functions
Functions using pointycastle package are:
Future<CipherDataHolder> cipherWithGCMyPointyCastle({required String clearValue}) async {
final Uint8List key = genKey(); //32 bytes key
final Uint8List nonce = generateRandomNonce(); //12 bytes nonce
final List<int> plainTextBytes = utf8.encode(clearValue);
final cipher = pointy.GCMBlockCipher(pointy.AESEngine())
..init(
true, // encrypt
pointy.AEADParameters(
pointy.KeyParameter(key), // the 256 bit (32 byte) key
128, //Mac length
nonce, // the 12 byte nonce
Uint8List(0), // empty extra data
));
//Last 16 is mac data, rest is plaintext Bytes
Uint8List cipherTextBytes = cipher.process(Uint8List.fromList(plainTextBytes));
//Concatenate nonce + cipherText + mac bytes
String cipherText = base64.encode(concatenateCipherData(nonceBytes: cipher.nonce, cipherTextBytes: cipherTextBytes.sublist(0, cipherTextBytes.length - 16), macBytes: cipher.mac));
return CipherDataHolder(ciphertext: cipherText, passphrase: base64.encode(key));
}
Future<String> decryptWithGCMyPointyCastle(CipherDataHolder secretData) async {
final Uint8List cipherData = base64.decode(secretData.ciphertext);
final Uint8List ciphertext = cipherData.sublist(12, cipherData.length - 16); //Rest between 12 and last 16
final Uint8List nonce = cipherData.sublist(0, 12); //First 12 bytes
final Uint8List mac = cipherData.sublist(cipherData.length - 16); //last 16 bytes
List<int> passphrase = base64.decode(secretData.passphrase);
final cipher = pointy.GCMBlockCipher(pointy.AESEngine())
..init(
false, // decrypt
pointy.AEADParameters(
pointy.KeyParameter(Uint8List.fromList(passphrase)),
128,
nonce,
Uint8List(0),
));
BytesBuilder bb = BytesBuilder();
bb.add(ciphertext);
bb.add(mac);
Uint8List ciphertextWithTag = bb.toBytes();
return String.fromCharCodes(cipher.process(ciphertextWithTag));
}
I still have some doubts and I don't know if I'm doing things right. But cipher and decrypt are working now and when I cipher data I'm getting similar results with cryptography package.
Main problem now is that when I cipher with pointycastle, for example this value: abc123|##¢∞¬÷“”≠
Results with cryptography: abc123|##¢∞¬÷“”≠
Results with pointycastle: abc123|##¢â¬÷âââ
I understand it can be some kind of codification problem, but I don't see where :(
Where could be the problem or What am I doing wrong?
These are the auxiliar functions
Uint8List concatenateCipherData({required List<int> nonceBytes, required List<int> cipherTextBytes, required List<int> macBytes}) {
int n = cipherTextBytes.length + nonceBytes.length + macBytes.length;
Uint8List result = Uint8List(n);
int i = 0;
result.setAll(i, nonceBytes);
i += nonceBytes.length;
result.setAll(i, cipherTextBytes);
i += cipherTextBytes.length;
result.setAll(i, macBytes);
return result;
}
Uint8List generateRandomNonce() {
final _sGen = Random.secure();
final _seed = Uint8List.fromList(List.generate(32, (n) => _sGen.nextInt(256)));
pointy.SecureRandom sec = pointy.SecureRandom("Fortuna")..seed(pointy.KeyParameter(_seed));
return sec.nextBytes(12);
}
Uint8List genKey() {
final _sGen = Random.secure();
final _seed = Uint8List.fromList(List.generate(32, (n) => _sGen.nextInt(256)));
pointy.SecureRandom sec = pointy.SecureRandom("Fortuna")..seed(pointy.KeyParameter(_seed));
return sec.nextBytes(32);
}
All the pointcastle ciphers are instantiated in basically the same way. Here's the way to instantiate AES/GCM, with examples for the inputs and outputs.
final keyBytes = Uint8List(32); // dummy key - replace with 256 bit key
final nonce = Uint8List(12); // dummy nonce - replace with random value
final plainTextBytes = Uint8List(5); // dummy input - 5 bytes (5 is just an example)
final cipher = GCMBlockCipher(AESEngine())
..init(
true, // encrypt (or decrypt)
AEADParameters(
KeyParameter(keyBytes), // the 256 bit (32 byte) key
16 * 8, // the mac size (16 bytes)
nonce, // the 12 byte nonce
Uint8List(0), // empty extra data
));
final cipherTextBytes = cipher.process(plainTextBytes);
print(cipherTextBytes.length); // prints 21 = 16 (mac) + 5 (plain text length)

How to encrypt a key correctly? _ FLUTTER

I am trying to implement private key encryption within my application.
However, this key that I have to encrypt is of the List type and when I try to encrypt I get the following error:
Expected a value of type 'String', but got one of type 'List <int>'
how can I encrypt my key of type List correctly without getting errors so that I can later pass the parameter as type Uint8List to my method:
AccountEntity.account (account, Uint8List.fromList (privateKey));
this is my code:
var privateKey = await account.keyPair.extractPrivateKeyBytes();
privateKey = AesEncryptionDecryption
.encryptKey(
privateKey).bytes ;
final entity =
AccountEntity.account(account, Uint8List.fromList(privateKey));
while this is the class which contains the encryption methods, as API I used encrypt: ^ 5.0.1 from pubdev :
class AesEncryptionDecryption{
static final key = encrypt.Key.fromLength(32);
static final iv = encrypt.IV.fromLength(16);
static final encrypter = encrypt.Encrypter(encrypt.AES(key));
static encryptAES(text) {
final encrypted = encrypter.encrypt(text, iv: iv);
return encrypted;
}
static encryptKey(text) {
final encrypted = encrypter.encryptBytes(text, iv: iv);
return encrypted as List<int>;
}
static String decryptAES(String base64Text) {
print(base64Text);
String decrypted = encrypter.decrypt(
Encrypted.fromBase64(base64Text),
iv: iv,
);
print(decrypted);
return decrypted;
}
does anyone know how to help me? thank you very much

Decrypt AES/CBC/PKCS5Padding Encryption in Dart

I am already having encryption code in java. Now I want to consume APIs from my server. Even after trying various tutorials and sample codes I am not able to successfully decrypt the hash.
I know fixed salt and IV is not recommended at all. But for simplicity and to understand the issue I have kept salt and IV to "00000000000000000000000000000000";
Hash After Encryption from Java = "XjxCg0KK0ZDWa4XMFhykIw==";
Private key used = "Mayur12354673645"
Can someone please help me to decrypt above string using dart.
JAVA Code
public String encrypt(String salt, String iv, String passphrase,
String plaintext) {
try {
SecretKey key = generateKey(salt, passphrase);
byte[] encrypted = doFinal(Cipher.ENCRYPT_MODE, key, iv, plaintext
.getBytes("UTF-8"));
return base64(encrypted);
} catch (UnsupportedEncodingException e) {
throw fail(e);
}
}
public String decrypt(String salt, String iv, String passphrase,
String ciphertext) {
try {
SecretKey key = generateKey(salt, passphrase);
byte[] decrypted = doFinal(Cipher.DECRYPT_MODE, key, iv,
base64(ciphertext));
return new String(decrypted, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw fail(e);
}
}
private SecretKey generateKey(String salt, String passphrase) {
try {
SecretKeyFactory factory = SecretKeyFactory
.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), hex(salt),
iterationCount, keySize);
SecretKey key = new SecretKeySpec(factory.generateSecret(spec)
.getEncoded(), "AES");
return key;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private byte[] doFinal(int encryptMode, SecretKey key, String iv,
byte[] bytes) {
try {
cipher.init(encryptMode, key, new IvParameterSpec(hex(iv)));
return cipher.doFinal(bytes);
} catch (Exception e) {
e.printStackTrace();
throw fail(e);
}
}
My Dart Code
import 'package:pointycastle/block/aes_fast.dart';
import 'package:pointycastle/block/modes/cbc.dart';
import 'package:pointycastle/digests/sha1.dart';
import 'package:pointycastle/key_derivators/pbkdf2.dart';
import 'package:pointycastle/macs/hmac.dart';
import 'package:pointycastle/paddings/pkcs7.dart';
import 'package:pointycastle/pointycastle.dart';
import 'dart:convert';
import 'dart:typed_data';
import 'package:convert/convert.dart';
import 'dart:developer';
import 'package:pointycastle/random/fortuna_random.dart';
const KEY_SIZE = 16;
const ITERATION_COUNT = 5;
class EncryptionHandler {
static const CBC_MODE = 'CBC';
static Uint8List deriveKey(dynamic password,
{String salt = '0000000000000000',
int iterationCount = ITERATION_COUNT,
int derivedKeyLength = KEY_SIZE}) {
if (password == null || password.isEmpty) {
throw new ArgumentError('password must not be empty');
}
if (password is String) {
password = createUint8ListFromString(password);
}
Uint8List saltBytes = createUint8ListFromString(salt);
String hexSalt = formatBytesAsHexString(saltBytes);
KeyDerivator keyDerivator =
new PBKDF2KeyDerivator(new HMac(new SHA1Digest(), 64));
Pbkdf2Parameters params =
new Pbkdf2Parameters(saltBytes, iterationCount, derivedKeyLength);
keyDerivator.init(params);
return keyDerivator.process(password);
}
Uint8List createUint8ListFromHexString(String hex) {
var result = new Uint8List(hex.length ~/ 2);
for (var i = 0; i < hex.length; i += 2) {
var num = hex.substring(i, i + 2);
var byte = int.parse(num, radix: 16);
result[i ~/ 2] = byte;
}
return result;
}
static String formatBytesAsHexString(Uint8List bytes) {
var result = new StringBuffer();
for (var i = 0; i < bytes.lengthInBytes; i++) {
var part = bytes[i];
result.write('${part < 16 ? '0' : ''}${part.toRadixString(16)}');
}
return result.toString();
}
static Uint8List pad(Uint8List src, int blockSize) {
var pad = new PKCS7Padding();
pad.init(null);
int padLength = blockSize - (src.length % blockSize);
var out = new Uint8List(src.length + padLength)..setAll(0, src);
pad.addPadding(out, src.length);
return out;
}
static Uint8List unpad(Uint8List src) {
var pad = new PKCS7Padding();
pad.init(null);
int padLength = pad.padCount(src);
int len = src.length - padLength;
return new Uint8List(len)..setRange(0, len, src);
}
static String encrypt(String password, String plaintext,
{String mode = CBC_MODE}) {
Uint8List derivedKey = deriveKey(password);
KeyParameter keyParam = new KeyParameter(derivedKey);
BlockCipher aes = new AESFastEngine();
var rnd = FortunaRandom();
rnd.seed(keyParam);
Uint8List iv = createUint8ListFromString("0000000000000000");
BlockCipher cipher;
ParametersWithIV params = new ParametersWithIV(keyParam, iv);
cipher = new CBCBlockCipher(aes);
cipher.init(true, params);
Uint8List textBytes = createUint8ListFromString(plaintext);
Uint8List paddedText = pad(textBytes, aes.blockSize);
Uint8List cipherBytes = _processBlocks(cipher, paddedText);
Uint8List cipherIvBytes = new Uint8List(cipherBytes.length + iv.length)
..setAll(0, iv)
..setAll(iv.length, cipherBytes);
return base64.encode(cipherIvBytes);
}
static String decrypt(String password, String ciphertext) {
log('Password: $password');
Uint8List derivedKey = deriveKey(password);
log('derivedKey: $derivedKey');
KeyParameter keyParam = new KeyParameter(derivedKey);
log('keyParam: $keyParam');
BlockCipher aes = new AESFastEngine();
Uint8List cipherIvBytes = base64.decode(ciphertext);
log('cipherIvBytes: $cipherIvBytes');
Uint8List iv = createUint8ListFromString("0000000000000000");
// Uint8List iv = new Uint8List(aes.blockSize)
// ..setRange(0, aes.blockSize, cipherIvBytes);
log('iv: $iv');
BlockCipher cipher;
ParametersWithIV params = new ParametersWithIV(keyParam, iv);
log('params: $params');
cipher = new CBCBlockCipher(aes);
log('cipher: $cipher');
cipher.init(false, params);
int cipherLen = cipherIvBytes.length - aes.blockSize;
Uint8List cipherBytes = new Uint8List(cipherLen)
..setRange(0, cipherLen, cipherIvBytes, aes.blockSize);
Uint8List paddedText = _processBlocks(cipher, cipherBytes);
log('cipher: $paddedText');
Uint8List textBytes = paddedText;
// Uint8List textBytes = unpad(paddedText);
return new String.fromCharCodes(textBytes);
}
static Uint8List createUint8ListFromString(String s) {
var ret = new Uint8List(s.length);
for (var i = 0; i < s.length; i++) {
ret[i] = s.codeUnitAt(i);
}
return ret;
}
static Uint8List _processBlocks(BlockCipher cipher, Uint8List inp) {
var out = new Uint8List(inp.lengthInBytes);
for (var offset = 0; offset < inp.lengthInBytes;) {
var len = cipher.processBlock(inp, offset, out, offset);
offset += len;
}
return out;
}
}
The code can be simplified by using existing Dart libraries for the conversion binary to hex and vice versa. PointyCastle also supports the (PKCS7) padding, so that a custom implementation is not necessary, which also reduces the code. On the Internet you can find several dart implementations for AES/CBC/PKCS7Padding in combination with PBKDF2 that use PointyCastle, e.g. here and here.
A possible Dart implementation for decryption using the pointycastle and convert package is e.g. (for simplicity without exception handling):
import 'dart:typed_data';
import "package:pointycastle/export.dart";
import 'package:convert/convert.dart';
import 'dart:convert';
...
static Uint8List decrypt(Uint8List ciphertext, Uint8List key, Uint8List iv) {
CBCBlockCipher cipher = new CBCBlockCipher(new AESFastEngine());
ParametersWithIV<KeyParameter> params = new ParametersWithIV<KeyParameter>(new KeyParameter(key), iv);
PaddedBlockCipherParameters<ParametersWithIV<KeyParameter>, Null> paddingParams = new PaddedBlockCipherParameters<ParametersWithIV<KeyParameter>, Null>(params, null);
PaddedBlockCipherImpl paddingCipher = new PaddedBlockCipherImpl(new PKCS7Padding(), cipher);
paddingCipher.init(false, paddingParams);
return paddingCipher.process(ciphertext);
}
static Uint8List generateKey(Uint8List salt, Uint8List passphrase){
KeyDerivator derivator = new PBKDF2KeyDerivator(new HMac(new SHA1Digest(), 64));
Pbkdf2Parameters params = new Pbkdf2Parameters(salt, 5, 16);
derivator.init(params);
return derivator.process(passphrase);
}
With the posted test data:
String saltHex = '00000000000000000000000000000000';
String ivHex = '00000000000000000000000000000000';
String passphraseUtf8 = 'Mayur12354673645';
String ciphertextBase64 = "XjxCg0KK0ZDWa4XMFhykIw==";
Uint8List salt = hex.decode(saltHex);
Uint8List passphrase = utf8.encode(passphraseUtf8);
Uint8List key = generateKey(salt, passphrase);
Uint8List ciphertext = base64.decode(ciphertextBase64);
Uint8List iv = hex.decode(ivHex);
Uint8List decrypted = decrypt(ciphertext, key, iv);
print(utf8.decode(decrypted)); // This is working
the ciphertext can be decrypted to: This is working.
An alternative to PointyCastle is the cryptography package, which allows even a more compact implementation in the current case:
import 'package:cryptography/cryptography.dart';
import 'package:convert/convert.dart';
import 'dart:convert';
import 'dart:typed_data';
...
static Uint8List decrypt(Uint8List ciphertext, Uint8List key, Uint8List iv) {
SecretKey secretKey = new SecretKey(key);
Nonce nonce = new Nonce(iv);
Uint8List decrypted = aesCbc.decryptSync(ciphertext, secretKey: secretKey, nonce: nonce);
return decrypted;
}
static Uint8List generateKey(Uint8List salt, Uint8List passphrase){
Pbkdf2 pbkdf2 = Pbkdf2(macAlgorithm: new Hmac(sha1), iterations: 5, bits: 128);
return pbkdf2.deriveBitsSync(passphrase, nonce: Nonce(salt));
}
Note that in practice IV and salt must be generated randomly for each encryption (which you already mentioned in your question). Apart from that the iteration count of 5 is generally much too low.