How to encrypt a string using AES-256 in flutter so that it decrypts on the web using openSSL - flutter

I tried encrypting a string in dart using encrypt library and It works pretty well other than the fact that when I try the generated encrypted string and the key and iv in an online AES decryptor, It never decrypts successfully.
I want to send encrypted data to a server and then that data needs to be decrypted on the server as well as the mobile device and I couldn't find any solution for this
My server is using PHP with OpenSSL, and I couldn't find any library for openSSl in flutter except this one but it has 0 documentation.
This is the sample code I used
Attempt 1:
final plainText = 'My Phone number is: 1234567890';
final key = encrypt.Key.fromLength(32);
final iv = encrypt.IV.fromLength(16);
final encrypter = encrypt.Encrypter(encrypt.AES(key));
final encrypted = encrypter.encrypt(plainText, iv: iv);
final decrypted = encrypter.decrypt(encrypted, iv: iv);
print(key.base64); // prints AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
print(iv.base64); // prints AAAAAAAAAAAAAAAAAAAAAA==
print(encrypted.base64); // prints kezgKMov5+yNJtd58OFSpzp8sNv2dwWNnFWDyf37cYk=
Attempt 2:
This time I used this gist that works using pointy castle to create the same data, but this time the IV was generated in an Array, and my server is expecting it as an int or a string.
Attempt 3:
this time I tried again using encrypt and found a medium link that encrypts data for decryption in the web using cryptoJS. this made me think, are all AES encryption libraries not compatible with each other?
final plainText = 'My Phone number is: 1234567890';
final newKey = Utils.CreateCryptoRandomString(32); // value is lh1uCZN4c8AFL2P4HudHV8B7dEBLzjxarZ09IrCf9cQ=
final encryptedAES = encryptAESCryptoJS(plainText, newKey);
Inside the encryptAESCryptoJS function, I added print statements to print the generated Salt, IV and key, Here are those:
SALT = [112, 161, 85, 133, 146, 178, 232, 83]
KEY = 0IfSLn8F33SIiWlYTyT4j7n6jnNP74xNaKTivqNeksE=
IV = QCl8fNQtg+QQYTQCINV6IA==
I can encrypt and decrypt locally easily using all the methods, but how can I add support so that the encrypted data can be decrypted on the server as well.
some of the websites I tried using to decrypt the data were
https://string-o-matic.com/aes-decrypt
and
https://www.devglan.com/online-tools/aes-encryption-decryption
both threw errors on adding the key, and iv on the specified fields
Any help would be much appreciated.

couldn't find any library for openSSl in flutter except this one but it has 0 documentation.
Yes, seems this is a problem. As well I consider important that someone understands basics regardless of the language implementation
I want to send encrypted data to a server and then that data needs to be decrypted on the server as well as the mobile device and I couldn't find any solution for this
That is a task of the TLS
The data needs to be stored encrypted as well so that no one working in the backend can look at the data
Just use the same encryption and decryption parameters. The problem with your code I see is it's missing some of the parameters and using defaults (defaults can differ in different libraries) or assuming you are using different parameters.
Symmetric encryption (AES specifically) needs to define:
key - for AES it's always 128, 192 or 256 bit (depending on the strength). Some libraries zero-pad or trim the input to match the required key length what I consider a terrible practice. Simply - a key needs to be a byte array of the specific length.
When encrypting multiple blocks of data:
padding - how input is padded to match the encryption block size (usually pkcs#7 padding)
mode of operation
IV - see the documentation about the mode of operation, IV must be unique and for some modes IV needs to be unpredictable (random).
SALT is used to create an encryption key from a password. So where you see any salt in use, check if you are providing a key or a password. Password can have any length and is usually user-handled (having lower entropy) and there are multiple ways how to derive a key from the password and salt.
var encrypted = encryptAESCryptoJS(plainText, "password");
See the source code, the encryptAESCryptoJS expects a password as input and then generates a salt and derives a key and IV (this is a practice from OpenSSL, but may not be compatible with other libraries).
This is a problem with some libraries, mainly when missing documentation.
Are all AES encryption libraries not compatible with each other?
AS cipher is AES cipher. You need to get the Cipher, Key, Padding, IV and the mode of operation the same for encryption and decryption regardless the programming language or platform. There are some most common defaults (AES-128, CBC mode, PKCS#7 padding, ..) but it's better to properly specify the parameters to be sure.
but this time the IV was generated in an Array, and my server is expecting it as an int or a string.
Encryption always works on top of byte arrays. You may encode a byte array as base64 or hex encoded string.
Edit: extra security measure
What I miss in this solution (in many other solutions in fact) is an authentication tag. Most of the encryption modes are malleable, the ciphertext can be changed and then the decryption would successfully decrypt to a different plaintext without detecting any problem with integrity. I consider using any HMAC necessary, but missing in many implementations.

I had the same problem, since in php the openssl_decrypt with aes-256-cbc is used to decrypt but in dart it didn't work for me, until I found a code snippet on github solutions, which served as the basis for proposing a solution to make it decode a text encrypted with php Lumen and AES openssl, I hope it will help you.
// code decrypt in PHP
$key = '**********key secred';
$encrypted = $request->get('encrypted');
$payload = json_decode(base64_decode($encrypted), true);
$iv = base64_decode($payload['iv']);
$decrypted = openssl_decrypt($payload['value'], 'aes-256-cbc',
base64_decode($key), 0, $iv, '');
$response['decrypted'] = unserialize($decrypted);
return $this->successResponse($response);
/// code decrypt in dart
import 'dart:convert';
import 'package:encrypt/encrypt.dart' as enc;
import 'dart:async';
import 'package:php_serializer/php_serializer.dart';
Future<String> decryptInfo(String data) async {
var encodedKey = 'FCAcEA0HBAoRGyALBQIeCAcaDxYWEQQPBxcXH****** example';
var decoded = base64.decode(data);
var payload = json.decode(String.fromCharCodes(decoded));
String encodedIv = payload["iv"]?? "";
String value = payload["value"] ?? "";
print(decoded);
print(payload);
print (encodedIv);
final key1 = enc.Key.fromBase64(encodedKey);
final iv = enc.IV.fromBase64(encodedIv);
final encrypter = enc.Encrypter(enc.AES(key1, mode: enc.AESMode.cbc));
final decrypted = encrypter.decrypt(enc.Encrypted.fromBase64(value), iv: iv);
print(phpDeserialize(decrypted));
return decrypted;
}

Related

How to encrypt data using AES with CBC mode and PCS5Padding status in dart?

I need to encrypt some data with AES and CBC mode, PCS5Padding status in dart.
I searched but I haven't found any package in pub.dev with PCS5Padding status.
I tried the below code but encryption here with PCS7Padding not PCS5Padding using encrypt package.
List plainTranData = [JSON data];
final iv = IV.fromUtf8(vector value);
final key = Key.fromUtf8(key);
final encrypter = Encrypter(AES(key, mode: AESMode.cbc));
final encrypted = encrypter.encrypt(plainTranData.toString(), iv: iv);
print(encrypted.base16); // <-- encrypted data in Hex/base16
As Richard Heap notes in the comments, there's no practical difference between them. PKCS5 is really just a special case of PKCS7. In many cases, implementations that call themselves "PKCS5" actually implement PKCS7. (This shouldn't be taken to mean that the "5" and "7" here are version numbers. They're not. They refer to specific Public Key Cryptography Standards documents. Two somewhat unrelated documents include the same basic padding strategy. There is no document devoted just to this padding strategy.)
For more details on this, see What is the difference between PKCS#5 padding and PKCS#7 padding on crypto.se.

Asymmetric zip file encryption with dart

I have an app that writes data to a file line by line. Once finished, the data needs to be zipped and transmitted to a server. I want to make sure that after zipping but before transmitting the data to the server, the data is secure. Thus, I want to encrypt the zip (or the contents). Moreover, I want to use asymmetric encryption because the source code will be viewed by others.
Is there any way to do this in flutter/dart?
My alternative solution would be to read the data back into the app, encrypt it, write it again, and then zip it. What are your thoughts?
As #Topaco accurately stated, asymmetric encryption of a large file comes with important performance drawbacks.
It can be achieved by splitting the file into smaller chunks of data and encrypting each part. But again, this is not recommended.
That said, you encrypt/decrypt a String with RSA using the rsa_encrypt package for Flutter:
import 'package:rsa_encrypt/rsa_encrypt.dart';
import 'package:pointycastle/api.dart' as crypto;
//Future to hold our KeyPair
Future<crypto.AsymmetricKeyPair> futureKeyPair;
//to store the KeyPair once we get data from our future
crypto.AsymmetricKeyPair keyPair;
Future<crypto.AsymmetricKeyPair<crypto.PublicKey, crypto.PrivateKey>> getKeyPair()
{
var helper = RsaKeyHelper();
return helper.computeRSAKeyPair(helper.getSecureRandom());
}
/*
- Generate KeyPair with the function getKeyPair() store the returned value in futureKeyPair.
- Once we get data from the future we can store that data in keyPair (Now we have acces to our private and public key).
- In order to view our keys as "a string" we need to use two functions encodePrivateKeyToPemPKCS1(keyPair.privateKey) & encodePublicKeyToPemPKCS1(keyPair.publicKey).
- In order to encrypt and decrypt strings you can use two functions
- encrypt() : use this function to encrypt a string, pass your string as first argument and a public key as the second one. [IMPORTANT]: this will return a string so you should store the returned value in a variable.
- decrypt() : use this function to decrypt an encrypted String, pass your encrypted String as first argument and a private key as the second. this will also return a string dont forget to store it :)
*/
A solution to encrypt a file would be to use a safe symmetric encryption algorithm with a random secret key, which is then encrypted with an asymmetric algorithm. This approach is commonly referred to as hybrid cryptosystem.

Decrypting AES GCM with Python without Authentication Tag

I will start with a disclaimer that I am out of my depth here. A colleague was showing me a decryption routine he wrote with pycryptodomex. He had an encrypted file, a key, and a nonce (extracted from the file). He was able to decrypt the file contents in a very straight forward way.
c = Crypto.Cipher.AES.new(key, AES.MODE_GCM, nonce)
c.decrypt(encrypted_data)
You can see a similar implementation in the pycryptodome test for GCM:
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
pt = get_tag_random("plaintext", 16 * 100)
ct = cipher.encrypt(pt)
cipher = AES.new(self.key_128, AES.MODE_GCM, nonce=self.nonce_96)
pt2 = cipher.decrypt(ct)
Unfortunately, pycryptdomex is an additional dependency that I would need to carry around and I am looking to avoid this. I have a base installation of Anaconda, which brings with it the pyCrypto and pyCA/cryptography packages. It appears that pycryptodomex is a fork of pyCrytpo, which didn't have a stable GCM implementation to begin with. When I look at the implementation for PyCA/cryptography, it looks straight forward:
cipher = Cipher(algorithms.AES(key), modes.GCM(nonce), backend=default_backend())
d = cipher.decryptor()
But when we want to decrypt content we have to call finalize_with_tag and produce an authentication tag:
d.update(encrypted_data) + d.finalize_with_tag(tag)
Unfortunately, I don't have an authentication tag nor do I know where to find it. I can't set the value to None as there is a minimum length requirement. I'm also not sure why I need to produce an authentication tag in the first place for AES GCM decryption with PyCA/Cryptography but I do not need to produce a tag when decrypting with the pycryptodomex. I'm ultimately looking for clarity on the following:
Is it possible to implement AES/GCM decryption with the Anaconda PyCA/cryptography package if I only have access to the key, nonce, and encrypted data?
Why do I need to provide an authentication tag for decryption with one implementation and not the other?
Is pycryptodomex doing something under the hood to determine the tag?
GCM without authentication tag is equivalent to CTR mode. (except the + 1 difference in starting counter value)
Calling decrypt does not verify the tag (as far as I know). You can test this yourself by altering the ciphertext just one byte. It will decrypt just fine (to a plaintext that is off by one byte). Use decrypt_and_verify (see test_invalid_mac test).
See 2.
Apologies as I can't reply to comments. Is it possible to derive the tag from the decrypted data after decryption? This PR associated with PyCA/cryptography seems to imply the exact scenario considered here.
According to the GCM spec (section 7.2: “Algorithm for the
Authenticated Decryption Function”), the tag itself is not needed
until the ciphertext has been decrypted.
Does calling d.update(encrypted_data) decrypt data successfully and d.finalize() is only needed to verify the integrity of the data?

How to reduce the length of a message encrypted with Hybrid encryption

I was looking for a good encryption scheme to encrypt my message and i founded that the Hybrid encryption is good for large and small messages. but i have a problem with the length of the output cipher message which is large.
if the input was "hello", then the length of the output message will be 586, and twice if if the message larger
here is the Encrypt function that i use:
def encrypt(username, msg):
#get the reciever's public key
f = open("{}.pem".format(username)) # a.salama.pem
recipient_key = RSA.import_key(f.read())
f.close()
# Encrypt the session key with the reciever's public RSA key
cipher_rsa = PKCS1_OAEP.new(recipient_key)
# Encrypt the data with the AES128 session key
session_key = get_random_bytes(16)
cipher_aes = AES.new(session_key, AES.MODE_EAX)
ciphertext, tag = cipher_aes.encrypt_and_digest(msg)
#finishing your processing
encrypted_data = cipher_rsa.encrypt(session_key) + cipher_aes.nonce + tag + ciphertext
encrypted_data = hexlify(encrypted_data).decode("utf-8")
return encrypted_data
There's a fixed number of extra bytes in the header regardless of the amount of plaintext being encrypted. That's evident from your line of code
encrypted_data = cipher_rsa.encrypt(session_key) + cipher_aes.nonce + tag + ciphertext
This extra data will be dominated by the RSA-encrypted session key. A more space-efficient choice would be ECIES using a well-known 256-bit elliptic curve.
However, you also have expansion of the data due to encoding. Your choice of encoding is hex encoding which doubles the amount of data. A more efficient and well-supported encoding is base64 encoding. Base64 encoding expands the data by a factor of 4/3. The most space-efficient is avoid encoding altogether and just store and transmit raw bytes. You only need to encode the data if it will transit over channel that cannot handle binary data.

Generate exportable AES keys in nCipher with JCE

I need to use nCipher HSM to generate AES256 keys and then export(write to file) them in external systems.
KeyGenerator kg = KeyGenerator.getInstance("AES", "nCipherKM");
kg.init(256);
SecretKey key = kg.generateKey();
//No problem until here
byte[] raw = key.getEncoded();
Throws a security exception. Wont give out the key. So I used below approach. Are both keys generated the same way ? Is there a better way to accomplish what I need?
SecureRandom random = SecureRandom.getInstance("RNG", "nCipherKM");
byte[] rand = new byte[32];
random.nextBytes(rand);
SecretKey key = new SecretKeySpec(rand, "AES");
byte[] raw = key.getEncoded();
Thanks,
Vedat
Vedat, the nCipherKM Provider generates actual key material on the Hardware Security Module(s) it connects to. The HSM is designed to not let you have the actual key bits: keeping those secret is its purpose in life.
The second sequence will obtain random data from the HSM's random generator, which is a NIST Special Publication 800-90A compliant Deterministic Random Bit Generator seeded by hardware-based entropy. This is the same type of random material the HSM uses internally to generate keys. Since you don't seem to have any HSM protection requirements for the resulting keys, you should be good to go.