Here below the code of dart Aes encryption logic
static String encryptAESGCM(String plaintext, String key) {
try {
final keyBytes = Uint8List.fromList(utf8.encode(key));
// SecureRandom secureRandom = getSecureRandom();
// Uint8List data = secureRandom.nextBytes(12);
Uint8List data = generateSecureRandomData(12);
// Uint8List data = generateNonSecureRandomData(12);
final cipher = GCMBlockCipher(AESEngine());
final params = AEADParameters(KeyParameter(keyBytes), 128, data, Uint8List(0));
cipher.init(true, params); // Initialize for encryption
final plaintextBytes = utf8.encode(plaintext);
final ciphertextBytes = cipher.process(Uint8List.fromList(plaintextBytes)); // Process all data at once
return base64.encode(ciphertextBytes); // Base64 encode for transmission
} catch (error) {
// Handle encryption errors gracefully
print("Encryption error: $error");
return ""; // Or return a suitable error message
}
}
static Uint8List generateSecureRandomData(size) {
return Uint8List.fromList(List<int>.generate(size, (_) => Random.secure().nextInt(256))); // cryptographically secure
}
here in this code i am trying to pass my username & password plain text and return it to an encrypted one but when i use those encrypted text on my api call it shows invalid credential may be my encryption logic is not correct here below i am pasting my server side C# code
public string UserNamePasswordEncryption(stringplaintext)
{
var key = _configuration.GetSection("keypath").GetValue<string>("key");
using var aes = new AesCcm(Encoding.UTF8.GetBytes(key));
var nonce = new byte[AesGcm.NonceByteSizes.MaxSize];
RandomNumberGenerator.Fill(nonce);
var plaintextBytes = Encoding.UTF8.GetBytes(plaintext);
var ciphertextBytes = new byte[plaintextBytes.Length];
var tag = new byte[AesGcm.TagByteSizes.MaxSize];
aes.Encrypt(nonce, plaintextBytes, ciphertextBytes, tag);
return new AesGcmCiphertext(nonce, tag, ciphertextBytes).ToString();
}
i don't know my dart is the alternative code for this C# one
Both codes differ in the following points:
So that both implementations are compatible and since the C# code is the reference code, the Dart code must be adapted accordingly, e.g:
import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
import 'package:pointycastle/api.dart';
import 'package:pointycastle/block/aes.dart';
import 'package:pointycastle/block/modes/ccm.dart';
import 'package:pointycastle/random/fortuna_random.dart';
...
final keyBytes = Uint8List.fromList(utf8.encode(key));
final nonce = getSecureRandom().nextBytes(12);
final plaintextBytes = utf8.encode(plaintext);
final cipher = CCMBlockCipher(AESEngine()) // apply CCM
..init(true, AEADParameters(KeyParameter(keyBytes), 128, nonce, Uint8List(0)));
final ciphertextBytes = cipher.process(Uint8List.fromList(plaintextBytes)); // returns ciphertext | tag
return base64.encode(nonce + ciphertextBytes); // create nonce | ciphertext | tag
...
SecureRandom getSecureRandom() {
List<int> seed = List<int>.generate(32, (_) => Random.secure().nextInt(256));
return FortunaRandom()..seed(KeyParameter(Uint8List.fromList(seed)));
}
Notes:
About the C# code: In the C# code, CCM is used for encryption, but sometimes GCM classes are applied. This is a bad style as it is misleading (as it happened to you). Instead, the corresponding CCM classes should be used (or renamed in the case of user-defined classes) and if the constants differ (e.g. AesGcm.NonceByteSizes.MaxSize
= 12 and AesCcm.NonceByteSizes.MaxSize
= 13), these should be defined explicitly.
Regarding the CSPRNG used: With the SecureRandom
class, PointyCastle provides a CSPRNG for which there are implementations for various algorithms, e.g. the Fortuna algorithm, which is implemented in the FortunaRandom
class. Since you are already using PointyCastle anyway because of the encryption (so this does not mean an additional dependency), the CSPRNG of PointyCastle can also be used. This is better documented and therefore more transparent than the poorly specified Random.secure()
(which is usually only applied as entropy source to generate the seed when using SecureRandom
/FortunaRandom
).
About the key: In general, a UTF-8 encoding means a password or a passphrase, which generally has a smaller entropy than a random byte sequence. In this case, the passphrase should not be used directly as key, but instead a key derivation function (such as Argon2 or at least PBKDF2) should be applied to derive the actual key from the password in conjunction with a random salt for each encryption. The salt is not secret and is given to the decrypting side together with the ciphertext (usually concatenated).
Regarding the choice of the algorithm: There may be reasons for you to use CCM instead of GCM (legacy applications etc.), but normally (the more modern) GCM is considered superior to CCM and should therefore be applied, see e.g. here.