Search code examples
flutterdartencryptionpointycastle

Dart: Encrypt and decrypt in AES with PointyCastle plug-in


I am trying to perform encryption and decryption using the plug-in that I will show you below.

The decryption works perfectly for me as I have used an AES cipher from my backend and it converts it perfectly.

The problem is the following: When you encrypt, get the result and want to decrypt in dart it does not give the expected result.

I guess the IV and SALT I'm doing it wrong, I hope to get some advice or help, thanks.

PointyCastle in pub.dev

crypto.dart

import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
import 'package:pointycastle/export.dart';

class Crypto {
  static const String password = "password_here";
  static const String algorithm = "AES";
  static FortunaRandom? _secureRandom;

  /// [decrypt] Method
  /// Requires one parameter: ```cipherText```
  static String decrypt( String cipherText ) {
    CBCBlockCipher cipher = CBCBlockCipher(BlockCipher( algorithm ));
    
    Uint8List ciphertextlist = base64.decode(cipherText);
    Uint8List salt = generateRandomBytes(32);
    Uint8List key = _generateKey(password, salt);
    Uint8List iv = generateRandomBytes(128 ~/ 8);
    Uint8List encrypted = ciphertextlist.sublist(20 + 16);

    ParametersWithIV<KeyParameter> params = ParametersWithIV<KeyParameter>(KeyParameter(key), iv);
    PaddedBlockCipherParameters<ParametersWithIV<KeyParameter>, KeyParameter> paddingParams = PaddedBlockCipherParameters<ParametersWithIV<KeyParameter>, KeyParameter>(params, null);
    PaddedBlockCipherImpl paddingCipher = PaddedBlockCipherImpl(PKCS7Padding(), cipher);
    paddingCipher.init(false, paddingParams);
    var val = paddingCipher.process(encrypted);
    String decrypted = String.fromCharCodes(val);
    return decrypted;
  }

  /// [encrypt] Method
  /// Requieres one parameter: ```plainText```
  static String encrypt( String plainText ) {
    final CBCBlockCipher cbcCipher = CBCBlockCipher(BlockCipher( algorithm ));
    List<int> data = utf8.encode( plainText );
    Uint8List iv = generateRandomBytes(128 ~/ 8);
    Uint8List salt = generateRandomBytes(32);
    Uint8List key = _generateKey(password, Uint8List.fromList(salt));
    final ParametersWithIV<KeyParameter> ivParams = ParametersWithIV<KeyParameter>(KeyParameter(key), iv);
    final PaddedBlockCipherParameters<ParametersWithIV<KeyParameter>, KeyParameter> paddingParams =PaddedBlockCipherParameters<ParametersWithIV<KeyParameter>, KeyParameter>(ivParams, null);
    final PaddedBlockCipherImpl paddedCipher = PaddedBlockCipherImpl(PKCS7Padding(), cbcCipher);
    paddedCipher.init(true, paddingParams);

    try {
      
      return base64.encode(paddedCipher.process(Uint8List.fromList(data)));
    } catch (e) {
      return '';
    }
  }

  /// [_generateKey] Method
  /// Generates the key to Uint8List for encryption and description.
  static Uint8List _generateKey(String passphrase, Uint8List salt) {    
    Uint8List passphraseInt8List = Uint8List.fromList(passphrase.codeUnits);
    KeyDerivator derivator = PBKDF2KeyDerivator(HMac(SHA1Digest(), 64));    
    Pbkdf2Parameters params = Pbkdf2Parameters(salt, 65556, 32);            
    derivator.init(params);
    return derivator.process(passphraseInt8List);
  }

  
  static Uint8List generateRandomBytes(int numBytes) {
    if (_secureRandom == null) {
      _secureRandom = FortunaRandom();

      final seedSource = Random.secure();
      final seeds = <int>[];
      for (int i = 0; i < 32; i++) {
        seeds.add(seedSource.nextInt(256));
      }
      _secureRandom!.seed(KeyParameter(Uint8List.fromList(seeds)));
    }
    return _secureRandom!.nextBytes(numBytes);
  }
}

crypto_test.dart

import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'models/crypto.dart';

void main() {
    test('Descrypt Data', () async {
      if( kDebugMode ){    
        String cipherText = "2FA4qsjxXzk5kp6cnbv8QrlcKPyxO/PihUky8HVkNf2hRlIAq25yCc1RbidvM7chE7JzuZUuNLIeoiEKr+vWx4AhSQclh94iC4eZMIGjyFllOn8qZl2zM+cYMuVp0zS5klVIgefBDd+SJSvsIElCBIAmsnY=";
        final decryptToHuman = Crypto.decrypt( cipherText );
        print( decryptToHuman );
      }
    });

    test('Encrypt Data', () async {
      if( kDebugMode ){
        Map<String, dynamic> toCipherData = {
          'email': '[email protected]',
          'passowrd': '12345678'
        };
        final String json = jsonEncode( toCipherData );
        final cipherText = Crypto.encrypt( json );
        print( "Texto cifrado: $cipherText" );
      }
    });
}

Error debug console:

Invalid argument(s): Input data length must be a multiple of cipher's block size
PaddedBlockCipherImpl.process
package:pointycastle/padded_block_cipher/padded_block_cipher_impl.dart:60
Crypto.decrypt
test\…\models\crypto.dart:38
main.<fn>
test\…\crypto\encrypt_test.dart:10
2

✖ Descrypt Data
Exited (1)

Solution

  • The solution to my problem was solved in this way.

    Thanks for your help.

    In encrypt method

        Uint8List encryptedTextBytes = paddingCipher.process( plainTextBytes );
        final buffer = Uint8List(salt.length + iv.length + encryptedTextBytes.length);
        List.copyRange(buffer, 0, salt, 0, salt.length);
        List.copyRange(buffer, salt.length, iv, 0, iv.length);
        List.copyRange(buffer, salt.length+iv.length, encryptedTextBytes, 0, encryptedTextBytes.length);