This is my c# code for string encryption using RijndaelManaged,
I'm not able to encrypt like same in flutter, i tried many packages. but no result.
i need to encrypt a string in flutter and i need to decrypt in c#
public static string key = Environment.GetEnvironmentVariable("ENCR_KEY");
private const int Keysize = 256;
private const int DerivationIterations = 100;
public string Encrypt(string plainText)
{
var saltStringBytes = Generate256BitsOfRandomEntropy();
var ivStringBytes = Generate256BitsOfRandomEntropy();
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
using (var password = new Rfc2898DeriveBytes(key, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = new RijndaelManaged())
{
symmetricKey.BlockSize = 128;
symmetricKey.Mode = CipherMode.CBC;
//symmetricKey.Padding = PaddingMode.PKCS7;
using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
var cipherTextBytes = saltStringBytes;
cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
memoryStream.Close();
cryptoStream.Close();
return Convert.ToBase64String(cipherTextBytes);
}
}
}
}
}
}
private static byte[] Generate256BitsOfRandomEntropy()
{
var randomBytes = new byte[16];
using (var rngCsp = new RNGCryptoServiceProvider())
{
rngCsp.GetBytes(randomBytes);
}
return randomBytes;
}
public string Decrypt(string cipherText)
{
string password = key;
byte[] cipherBytes = Convert.FromBase64String(cipherText);
using (Aes encryptor = Aes.Create())
{
var salt = cipherBytes.Take(16).ToArray();
var iv = cipherBytes.Skip(16).Take(16).ToArray();
var encrypted = cipherBytes.Skip(32).ToArray();
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, salt, 100);
encryptor.Key = pdb.GetBytes(32);
encryptor.Padding = PaddingMode.PKCS7;
encryptor.Mode = CipherMode.CBC;
encryptor.IV = iv;
using (MemoryStream ms = new MemoryStream(encrypted))
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Read))
{
using (var reader = new StreamReader(cs, Encoding.UTF8))
{
return reader.ReadToEnd();
}
}
}
}
}
When i try encrypted cipher to decrypt in c#. I'm getting this error,
"Padding is invalid and cannot be removed."
This is my Dart code
import 'dart:convert';
import 'package:pointycastle/block/aes_fast.dart';
import 'dart:typed_data';
import 'package:pointycastle/export.dart';
import 'package:pointycastle/key_derivators/pbkdf2.dart';
import 'package:pointycastle/paddings/pkcs7.dart';
import 'package:pointycastle/pointycastle.dart';
const KEY_SIZE = 32; // 32 byte key for AES-256
const ITERATION_COUNT = 2;
const SALT = "XXXXXXXXXXXXXXXXX";
const INITIAL_VECTOR = "ZZZZZZZZZZZZZZZZ";
const PASS_PHRASE = "YYYYYYYYYYYYYYYYYYY";
Future<String> cryptString(String text) async {
String encryptedString = "";
final mStrPassPhrase = toUtf8(PASS_PHRASE);
encryptedString = AesHelperMethod2.encrypt(
mStrPassPhrase,
toUtf8(text),
mode: AesHelperMethod2.CBC_MODE,
);
return encryptedString;
}
Future<String> decryptString(String text) async {
String decryptedString = "";
final mStrPassPhrase = toUtf8(PASS_PHRASE);
decryptedString = AesHelperMethod2.decrypt(mStrPassPhrase, toUtf8(text),
mode: AesHelperMethod2.CBC_MODE);
return decryptedString;
}
///MARK: AesHelper class
class AesHelperMethod2 {
static const CBC_MODE = 'CBC';
static const CFB_MODE = 'CFB';
static Uint8List deriveKey(dynamic password,
{String salt = '',
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);
Pbkdf2Parameters params =
new Pbkdf2Parameters(saltBytes, iterationCount, derivedKeyLength);
KeyDerivator keyDerivator =
new PBKDF2KeyDerivator(new HMac(new SHA1Digest(), 64));
keyDerivator.init(params);
return keyDerivator.process(password);
}
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}) {
String salt = toASCII(SALT);
Uint8List derivedKey = deriveKey(password, salt: salt);
KeyParameter keyParam = new KeyParameter(derivedKey);
BlockCipher aes = new AESFastEngine();
var ivStr = toASCII(INITIAL_VECTOR);
Uint8List iv = createUint8ListFromString(ivStr);
BlockCipher cipher;
ParametersWithIV params = new ParametersWithIV(keyParam, iv);
switch (mode) {
case CBC_MODE:
cipher = new CBCBlockCipher(aes);
break;
case CFB_MODE:
cipher = new CFBBlockCipher(aes, aes.blockSize);
break;
default:
throw new ArgumentError('incorrect value of the "mode" parameter');
break;
}
cipher.init(true, params);
Uint8List textBytes = createUint8ListFromString(plaintext);
Uint8List paddedText = pad(textBytes, aes.blockSize);
Uint8List cipherBytes = _processBlocks(cipher, paddedText);
final enc = base64.encode(cipherBytes);
print("enc : " "$enc");
return base64.encode(cipherBytes);
}
static String decrypt(String password, String ciphertext,
{String mode = CBC_MODE}) {
String salt = toASCII(SALT);
Uint8List derivedKey = deriveKey(password, salt: salt);
KeyParameter keyParam = new KeyParameter(derivedKey);
BlockCipher aes = new AESFastEngine();
var ivStr = toASCII(INITIAL_VECTOR);
Uint8List iv = createUint8ListFromString(ivStr);
Uint8List cipherBytesFromEncode = base64.decode(ciphertext);
Uint8List cipherIvBytes =
new Uint8List(cipherBytesFromEncode.length + iv.length)
..setAll(0, iv)
..setAll(iv.length, cipherBytesFromEncode);
BlockCipher cipher;
ParametersWithIV params = new ParametersWithIV(keyParam, iv);
switch (mode) {
case CBC_MODE:
cipher = new CBCBlockCipher(aes);
break;
case CFB_MODE:
cipher = new CFBBlockCipher(aes, aes.blockSize);
break;
default:
throw new ArgumentError('incorrect value of the "mode" parameter');
break;
}
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);
Uint8List textBytes = unpad(paddedText);
return new String.fromCharCodes(textBytes);
}
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;
}
}
///MARK: HELPERS
Uint8List createUint8ListFromString(String s) {
Uint8List ret = Uint8List.fromList(s.codeUnits);
return ret;
}
String toUtf8(value) {
var encoded = utf8.encode(value);
var decoded = utf8.decode(encoded);
return decoded;
}
String toASCII(value) {
var encoded = ascii.encode(value);
var decoded = ascii.decode(encoded);
return decoded;
}
The C# encrypt() method does the following:
Generating a random 16 bytes salt and a random 16 bytes IV
Deriving a key with PBKDF2 using the following parameters
Key size 32 bytes
Digest: Sha-1
Iteration count: 100 (generally much too small for PBKDF2!)
Encrypting with AES in CBC mode and PKCS#7 padding
Concatenating salt, IV and ciphertext in that order and Base64 encoding
These functional building blocks must be replicated in the Dart code: For this you need a function that generates random values. So far, there is no such thing in the posted code. Also, the deriveKey() function for key derivation needs to be refactored. Other functionalities that could be encapsulated in functions are encryption with AES in CBC mode and PKCS#7 Padding as well as concatenation and encoding of the data.
Possible implementation to generate random values:
SecureRandom getSecureRandom() {
List<int> seed = List<int>.generate(32, (_) => Random.secure().nextInt(256));
return FortunaRandom()..seed(KeyParameter(Uint8List.fromList(seed)));
}
Refactoring of the deriveKey() method
Uint8List deriveKey(Uint8List salt, Uint8List passphrase){
KeyDerivator derivator = KeyDerivator('SHA-1/HMAC/PBKDF2');
Pbkdf2Parameters params = Pbkdf2Parameters(salt, 100, 256~/8);
derivator.init(params);
return derivator.process(passphrase);
}
Implementation of a method that encrypts with AES in CBC mode and PKCS#7 padding:
Uint8List encryptAesCbcPkcs7(Uint8List plaintext, Uint8List key, Uint8List iv){
CBCBlockCipher cipher = CBCBlockCipher(AESEngine());
ParametersWithIV<KeyParameter> params = ParametersWithIV<KeyParameter>(KeyParameter(key), iv);
PaddedBlockCipherParameters<ParametersWithIV<KeyParameter>, Null> paddingParams = PaddedBlockCipherParameters<ParametersWithIV<KeyParameter>, Null>(params, null);
PaddedBlockCipherImpl paddingCipher = PaddedBlockCipherImpl(PKCS7Padding(), cipher);
paddingCipher.init(true, paddingParams);
Uint8List ciphertext = paddingCipher.process(plaintext);
return ciphertext;
}
And finally, a method for concatenating and encoding the data:
String concatAndEncode(Uint8List salt, Uint8List iv, Uint8List ciphertext){
BytesBuilder saltIvCiphertext = BytesBuilder();
saltIvCiphertext.add(salt);
saltIvCiphertext.add(iv);
saltIvCiphertext.add(ciphertext);
String saltIvCiphertextB64 = base64Encode(saltIvCiphertext.toBytes());
return saltIvCiphertextB64;
}
Then these functional blocks only need to be wired:
import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
import 'package:pointycastle/export.dart';
...
Uint8List plaintext = Uint8List.fromList(utf8.encode("The quick brown fox jumps over the lazy dog"));
Uint8List passphrase = Uint8List.fromList(utf8.encode("my passphrase"));
// Generate random 16 bytes salt and random 16 bytes IV
SecureRandom secureRandom = getSecureRandom();
Uint8List salt = secureRandom.nextBytes(16);
Uint8List iv = secureRandom.nextBytes(16);
// Derive 32 bytes key via PBKDF2
Uint8List key = deriveKey(salt, passphrase);
// Encrypt with AES-256/CBC/PKCS#7 padding
Uint8List ciphertext = encryptAesCbcPkcs7(plaintext, key, iv);
// Concat salt|nonce|ciphertext and Base64 encode
String saltIvCiphertextB64 = concatAndEncode(salt, iv, ciphertext);
print(saltIvCiphertextB64); // e.g. 3igL9PVjgWpCTwYHP2GluZ/8lUaNblnGFEjZFDEiGvdnjoR/RkXIEtcPmgsnC4MmsfesGXo8Jls2vnCISoVAkzIZvadxbw5Dq1QddeMPnS0=
A ciphertext generated with this Dart code can be decrypted with the C# code.
We have this code on encrypting/decrypting a particular message and I'd like to decrypt the value in Flutter (dart).
/* Encrypt text
* #param text
*/
export const encrypt = (text: string): string => {
const encJson = CryptoJS.AES.encrypt(JSON.stringify(text), SECRET_KEY).toString();
return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(encJson));
};
/* Decrypt text
* #param ciphertext
*/
export const decrypt = (ciphertext: string): string => {
const decData = CryptoJS.enc.Base64.parse(ciphertext).toString(CryptoJS.enc.Utf8);
const bytes = CryptoJS.AES.decrypt(decData, SECRET_KEY).toString(CryptoJS.enc.Utf8);
return JSON.parse(bytes);
};
I have tried the example mentioned in this article but could not make it work.
https://medium.com/#chingsuehok/cryptojs-aes-encryption-decryption-for-flutter-dart-7ca123bd7464
I really appreciate if anyone can help or point me out on what to change on my code.
Current code:
String decryptAESCryptoJS(String encrypted, String passphrase) {
try {
Uint8List encryptedBytesWithSalt = base64.decode(encrypted);
Uint8List encryptedBytes =
encryptedBytesWithSalt.sublist(16, encryptedBytesWithSalt.length);
final salt = encryptedBytesWithSalt.sublist(8, 16);
var keyndIV = deriveKeyAndIV(passphrase, salt);
final key = encrypt.Key(keyndIV.item1);
final iv = encrypt.IV(keyndIV.item2);
final encrypter = encrypt.Encrypter(
encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: "PKCS7"));
final decrypted =
encrypter.decrypt64(base64.encode(encryptedBytes), iv: iv);
return decrypted;
} catch (error) {
throw error;
}
}
Tuple2<Uint8List, Uint8List> deriveKeyAndIV(String passphrase, Uint8List salt) {
var password = createUint8ListFromString(passphrase);
Uint8List concatenatedHashes = Uint8List(0);
List<int> currentHash = Uint8List(0);
bool enoughBytesForKey = false;
Uint8List preHash = Uint8List(0);
while (!enoughBytesForKey) {
int preHashLength = currentHash.length + password.length + salt.length;
if (currentHash.length > 0)
preHash = Uint8List.fromList(currentHash + password + salt);
else
preHash = Uint8List.fromList(password + salt);
currentHash = md5.convert(preHash).bytes;
concatenatedHashes = Uint8List.fromList(concatenatedHashes + currentHash);
if (concatenatedHashes.length >= 48) enoughBytesForKey = true;
}
var keyBtyes = concatenatedHashes.sublist(0, 32);
var ivBtyes = concatenatedHashes.sublist(32, 48);
return new Tuple2(keyBtyes, ivBtyes);
}
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;
}
Uint8List genRandomWithNonZero(int seedLength) {
final random = Random.secure();
const int randomMax = 245;
final Uint8List uint8list = Uint8List(seedLength);
for (int i = 0; i < seedLength; i++) {
uint8list[i] = random.nextInt(randomMax) + 1;
}
return uint8list;
}
The CryptoJS code unnecessarily Base64 encodes the ciphertext twice during encryption. So the most reasonable solution would be to fix the CryptoJS code.
In encrypt(), encJson is already the Base64 encoded ciphertext, i.e. the body of the encrypt() method should actually be:
return CryptoJS.AES.encrypt(JSON.stringify(text), SECRET_KEY).toString();
and analogously the body of decrypt():
const bytes = CryptoJS.AES.decrypt(ciphertext, SECRET_KEY).toString(CryptoJS.enc.Utf8);
return JSON.parse(bytes);
With this fix, successful decryption with the unmodified Dart code is possible.
If for some reason the CryptoJS code must not be changed, the Dart code in decryptAESCryptoJS() must also Base64 decode twice:
Uint8List encryptedBytesWithSalt = base64.decode(utf8.decode(base64.decode(encrypted)));
With this fix, the ciphertext of the unmodified CryptoJS code can be successfully decrypted.
I want to encrypt a message using RSA with a provided PEM public key in Javascript, using SubtleCrypto window.crypto.subtle and then decode it with Python (PyCryptodome) in the back-end. However, I get a ValueError: Incorrect decryption.. I'm not sure if the data is being correctly handled though. Here is my code:
JavaScript:
var publicKey;
var pemPublicKey = `public.pem key with stripped header and footer and newlines (just the base64 data)`;
function base64ToArrayBuffer(b64) {
var byteString = window.atob(b64);
var byteArray = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) { byteArray[i] = byteString.charCodeAt(i); }
return byteArray;
}
function arrayBufferToBase64(buffer) {
var binary = '';
var bytes = new Uint8Array(buffer);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) { binary += String.fromCharCode(bytes[i]); }
return window.btoa(binary);
}
window.crypto.subtle.importKey(
"spki",
base64ToArrayBuffer(pemPublicKey),
{ name: "RSA-OAEP", hash: { name: "SHA-256" } },
false,
["encrypt"])
.then(function (key) {
publicKey = key
})
console.log(publicKey)
var enc = new TextEncoder()
var encmessage = enc.encode("test14")
var encryptedData;
window.crypto.subtle.encrypt({
name: "RSA-OAEP"
}, publicKey, encmessage).then(function (encrypted) { encryptedData = encrypted })
var encodedData = arrayBufferToBase64(encryptedData);
console.log(encodedData)
What the code above does is convert the public PEM key, generate a CryptoKey object out of it (using crypto.subtle.importKey) and then encrypts a simple message "test14".
Python backend:
import base64
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import AES, PKCS1_OAEP
with open('private.pem', 'r') as f: keypair = RSA.import_key(f.read())
decryptor = PKCS1_OAEP.new(keypair)
decrypted = decryptor.decrypt(base64.b64decode(encrypted)) # encrypted is the data that is returned by JavaScript code
print(decrypted)
Directly from the documentation of Crypto.Cipher.PKCS1_OAEP.new(key, hashAlgo=None, mgfunc=None, label='', randfunc=None):
...
hashAlgo (hash object) - The hash function to use. This can be a module under Crypto.Hash or an existing hash object created from any of such modules. If not specified, Crypto.Hash.SHA1 is used.
...
For an api I am using, I have to convert a string password and a salt to a md5 string and call that string in a url post request,
my code looks like this:
generateMd5(String data) {
var content = new Utf8Encoder().convert(sp);
var md5 = crypto.md5;
var digest = md5.convert(content);
return digest.toString();
}
problem is i can't call it as a string. when i put "print(data)" it says its an Undefined name.
You need a method like this for your salted password token.
String makeToken(String password, String salt) =>
md5.convert(utf8.encode(password + salt)).toString().toLowerCase();
Calling print(makeToken('sesame', 'c19b2d')); yields 26719a1196d2a940705a59634eb18eab as shown in the test vector.
Supply the token as the t parameter and the salt as the s parameter of your API call.
You might find the following useful for your salt creation:
final _random = Random();
String randomToken(int length) => String.fromCharCodes(
List.generate(length, (_) {
var ch = _random.nextInt(52);
if (ch > 25) {
ch += 6;
}
return ch + 0x41;
}),
);
String newSalt() => randomToken(6);
All information i got are encrypted data (AES) and a key. The data must be a URL. I 've tried so many code-snippets (from stackoverflow) and found one snippets that worked for me partially.
// Decode the base64 data so we can separate iv and crypt text.
var rawData = atob(data);
var iv = btoa(rawData.substring(0,16));
var crypttext = btoa(rawData.substring(16));
// Decrypt...
var plaintextArray = CryptoJS.AES.decrypt(
{
ciphertext: CryptoJS.enc.Base64.parse(crypttext),
salt: ""
},
CryptoJS.enc.Hex.parse(key),
{ iv: CryptoJS.enc.Base64.parse(iv) }
);
// Convert hex string to ASCII.
function hex2a(hex) {
var str = '';
for (var i = 0; i < hex.length; i += 2)
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
return str;
}
console.log(hex2a(plaintextArray.toString()));
The URL must be
http://test-example.com/hjhdsdfuisd
but the output is only
-example.com/hjhdsdfuisd.
I changed
var crypttext = btoa(rawData.substring(16));
to
var crypttext = btoa(rawData);
and got
ô#XÍäÜ7±H4-example.com/hjhdsdfuisd.
What is my mistake?