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.
Related
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;
}
I'm writing a parse tool to extract each field of P12 file in C language, OpenSSL is too huge for my project.
After reading PKCS# series documents and ASN.1 documents, I understand the basic parse step.
I use OpenSSL to generate a self-signed P12 file,there're some questions during parsing:
Why Certificate is not stored in a Safebag, in my case it's stored in EncryptedData field?
What does the localKeyId attribute mean? it has an OctetString, what is the OctectString used for?
Why the contentType of encryptedContentInfo is id-data? I think it should be id-encryptedData. In my case, Certificate is stored in encryptedContentInfo field.
Thanks,
CZ
The PKCS12 standard also available as rfc7292 formally allows a very wide range and combination of options, but in practice only a few of these options are used. There are basically 3 levels:
the file has type/structure PFX consisting mostly of a PKCS7/CMS ContentInfo which theoretically can be 'data' or 'signed-data' but in practice is always the former (with the nominally optional MacData appended) and contains
'AuthenticatedSafe' which is a sequence of one or more (almost always more) ContentInfo(s) each of which (separately) may be encrypted or not and contains (after decryption if applicable)
a sequence of one of more 'bag'(s) each of which contains actual data of a certain type such as an encrypted privatekey or a certificate along with optional attributes.
In practice there is usually:
one CI (at level 2) with PKCS7/CMS type 'encrypted-data' using a very weak algorithm (RC2-40) containing one or more CertBag(s) each containing a cert plus any attributes for it, and
one or more CI(s) (each) with type 'data' containing a PKCS8ShroudedKeyBag containing an encrypted privatekey (using PKCS8 as stated) usually using a strong algorithm commonly 3DES, plus attributes.
My answer here shows the first levels of parsing to find the encryption details; further parsing requires decryption as shown (for a specific case) in my answer here.
As mentioned all bags can have attributes; in practice depending on the implementation some bags may have the 'friendlyName' attribute with a value intended for people to use, and if a matching privatekey and cert are present they both have a 'localKeyId' attribute with the same value to tie them together, as explained in my answer to a different but related Q. 'localKeyId' is not intended for people to use and you should not normally present it to people.
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?
OK, I'm probably just having a bad Monday, but I have the following need and I'm seeing lots of partial solutions but I'm sure I'm not the first person to need this, so I'm wondering if I'm missing the obvious.
$client has 50 to 500 bytes worth of binary data that must be inserted into the middle of a URL and roundtrip to their customer's browser. Since it's part of the URL, we're up against the 1K "theoretical" limit of a GET URL. Also, $client doesn't want their customer decoding the data, or tampering with it without detection. $client would also prefer not to store anything server-side, so this must be completely standalone. Must be Perl code, and fast, in both encoding and decoding.
I think the last step can be base64. But what are the steps for encryption and hashing that make the most sense?
I have some code in a Cat App that uses Crypt::Util to encode/decode a user's email address for an email verification link.
I set up a Crypt::Util model using Catalyst::Model::Adaptor with a secret key. Then in my Controller I have the following logic on the sending side:
my $cu = $c->model('CryptUtil');
my $token = $cu->encode_string_uri_base64( $cu->encode_string( $user->email ) );
my $url = $c->uri_for( $self->action_for('verify'), $token );
I send this link to the $user->email and when it is clicked on I use the following.
my $cu = $c->model('CryptUtil');
if ( my $id = $cu->decode_string( $cu->decode_string_uri_base64($token) ) ) {
# handle valid link
} else {
# invalid link
}
This is basically what edanite just suggested in another answer. You'll just need to make sure whatever data you use to form the token with that the final $url doesn't exceed your arbitrary limit.
Create a secret key and store it on the server. If there are multiple servers and requests aren't guaranteed to come back to the same server; you'll need to use the same key on every server. This key should be rotated periodically.
If you encrypt the data in CBC (Cipher Block Chaining) mode (See the Crypt::CBC module), the overhead of encryption is at most two blocks (one for the IV and one for padding). 128 bit (i.e. 16 byte) blocks are common, but not universal. I recommend using AES (aka Rijndael) as the block cipher.
You need to authenticate the data to ensure it hasn't been modified. Depending on the security of the application, just hashing the message and including the hash in the plaintext that you encrypt may be good enough. This depends on attackers being unable to change the hash to match the message without knowing the symmetric encryption key. If you're using 128-bit keys for the cipher, use a 256-bit hash like SHA-256 (you can use the Digest module for this). You may also want to include some other things like a timestamp in the data to prevent the request from being repeated multiple times.
I see three steps here. First, try compressing the data. With so little data bzip2 might save you maybe 5-20%. I'd throw in a guard to make sure it doesn't make the data larger. This step may not be worth while.
use Compress::Bzip2 qw(:utilities);
$data = memBzip $data;
You could also try reducing the length of any keys and values in the data manually. For example, first_name could be reduced to fname.
Second, encrypt it. Pick your favorite cipher and use Crypt::CBC. Here I use Rijndael because its good enough for the NSA. You'll want to do benchmarking to find the best balance between performance and security.
use Crypt::CBC;
my $key = "SUPER SEKRET";
my $cipher = Crypt::CBC->new($key, 'Rijndael');
my $encrypted_data = $cipher->encrypt($data);
You'll have to store the key on the server. Putting it in a protected file should be sufficient, securing that file is left as an exercise. When you say you can't store anything on the server I presume this doesn't include the key.
Finally, Base 64 encode it. I would use the modified URL-safe base 64 which uses - and _ instead of + and / saving you from having to spend space URL encoding these characters in the base 64 string. MIME::Base64::URLSafe covers that.
use MIME::Base64::URLSafe;
my $safe_data = urlsafe_b64encode($encrypted_data);
Then stick it onto the URL however you want. Reverse the process for reading it in.
You should be safe on size. Encrypting will increase the size of the data, but probably by less than 25%. Base 64 will increase the size of the data by a third (encoding as 2^6 instead of 2^8). This should leave encoding 500 bytes comfortably inside 1K.
How secure does it need to be? Could you just xor the data with a long random string then add an MD5 hash of the whole lot with another secret salt to detect tampering?
I wouldn't use that for banking data, but it'd probably be fine for most web things...
big
We're developing a service that will accept a POST request. Some of the POST data will need to be encrypted before the POST as it will be stored in hidden fields on a form.
The application is written in C#, but we want third party clients to be able to easily integrate with it. We find that most clients use PHP, Classic ASP or VB.Net.
The third parties should only be doing the encryption. We'd do the decryption. There is no two-way communication.
What are the most compatible combinations of encryption algorithm, padding mode and other options?
Assuming that you have a safe way of sharing a key (whether RSA encryption of it, retrieval over an SSH or HTTPS link, or callling the other developer on a secured phone line), any of the major modern encryptions (like AES, as mentioned by #Ed Haber) would be suitable. I would second his suggestion of AES. There should be libraries for PHP, VB, Ruby, etc.
However, remember that with "no two-way communication" you will have to find an out-of-channel method for securely getting the symmetric key to the encrypting party.
If you mean that it should be impossible for third-parties to decrypt data, then you will want to use an asymmetric encryption algorithm such as RSA. This will the third-party to encrypt data with your public key, and then only you can decrypt the data with your private key, which you do not disclose. There should be implementations of RSA available for all the languages you mentioned.
If you don't care if the third-party can decrypt the data, then AES is the way to go. You will have one key which you share with the third-parties. This key is used both for encryption and decryption.
I would use AES for the bulk data encryption and RSA for encrypting the AES Key.
If the data is small enough then just encrypt the whole thing with RSA.
Ed Haber said
I would use AES for the bulk data
encryption and RSA for encrypting the
AES Key. If the data is small enough
then just encrypt the whole thing with
RSA.
I think this is a good solution. What I would do is have your application publish an API for getting a public RSA key. When I third party wants to send you something it gets the public key. It then generates a session key to do the actual encryption using a block cipher, (ie AES), and sends the key to you by encrypting with your public key. You decrypt the session key with your private key. The third party then encrypts the data it wants to send you with AES (using a padding scheme that you also publish) and sends it to you. You decrypt it using the session key.
There are some problems with the method above. Since you are not sending any information (other than publishing your public key, you cannot control how the session key is generated. This means that third parties can use very insecure ways to of generating the session key and you will never know. A second problem is everyone who wants to send you data has to pad data for AES in the same way you do. So you will have to make sure every one co-ordinates. The second issue isn't to big, but the first could be a problem especially if you don't trust the third parties all that much to generate really good session keys from a good cryptographically secure random number generator
You could very easily implement your own XOR key-based bit encryption. With a little thought and ingenuity, you can come up with something that's more than suitable for you application.
Here's a PHP example:
function XOREncryption($InputString, $KeyPhrase){
$KeyPhraseLength = strlen($KeyPhrase);
for ($i = 0; $i < strlen($InputString); $i++){
$rPos = $i % $KeyPhraseLength;
$r = ord($InputString[$i]) ^ ord($KeyPhrase[$rPos]);
$InputString[$i] = chr($r);
}
return $InputString;
}
ColdFusion has the encrypt and decrypt functions capable of handling a range of algorithms and encodings, including the AES recommended above.
Information at: http://www.cfquickdocs.com/cf8/?getDoc=encrypt#Encrypt
Quick example code:
Key = generateSecretKey( 'AES' , 128 )
EncryptedText = encrypt( Text , Key , 'AES' , 'Hex' )
Text = decrypt( EncryptedText , Key, 'AES' , 'Hex' )
Similar functionality is available with this library for PHP:
http://www.chilkatsoft.com/p/php_aes.asp
...and Java, Python, Ruby, and others...
http://www.example-code.com/java/crypt2_aes_matchPhp.asp
http://www.example-code.com/python/aes_stringEncryption.asp
Sounds like RSA is the algorithm for you.
Why not have your server exposed over HTTPS? That way, any client which can handle HTTPS can consume the service securely.