Search code examples
flutterdartencryptionaessalt

Converting AES decryption function from Java to Flutter


I have been trying to convert an AES decryption function from Java to Dart.

It uses AES with, IV and salt. No padding operations are performed.

This is the Java code:

    public byte[] Decrypt(byte[] Data, String Password) throws InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
        byte[] salt = new byte[8];
        byte[] iv = new byte[16];
        System.arraycopy(Data, 0, salt, 0, 8);
        System.arraycopy(Data, 8    , iv, 0, 16);
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        KeySpec keySpec =
                new PBEKeySpec(Password.toCharArray(),salt, 1024, 128);
        SecretKey tmp = factory.generateSecret(keySpec);
        SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
        Cipher d = Cipher.getInstance("AES/CBC/PKCS5Padding");
        d.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
        byte[] t = new byte[Data.length - 24];
        System.arraycopy(Data, 24, t, 0, t.length);
        
        return d.doFinal(t);
    }

and this is my Dart attempt:

 static Uint8List? decrypt(String ciphertext, String password) {
    Uint8List rawCipher = base64.decode(ciphertext);
    var salt = rawCipher.sublist(0, 0 + 8);
    var iv = rawCipher.sublist(8, 8 + 16);
    var encrypted = rawCipher.sublist(8 + 16);

    Uint8List key = generateKey(password, salt);
    print('key => $key');
    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(false, paddingParams);

    var val = paddingCipher.process(encrypted);

    String res = String.fromCharCodes(val);

    print('res => $res');

    return val;
  }



  static Uint8List generateKey(String passphrase, Uint8List salt) {
    Uint8List passphraseInt8List = Uint8List.fromList(passphrase.codeUnits);

    KeyDerivator derivator = PBKDF2KeyDerivator(HMac(SHA1Digest(), 32));
    Pbkdf2Parameters params = Pbkdf2Parameters(salt, 1024, 16);
    derivator.init(params);
    return derivator.process(passphraseInt8List);
  }

base64 encrypted string :

7MT6+nT/grYuh6vAwMGtt/2YXddNbdPY2uijM3HrHQ4q5Iv8Q8zm2emMuuXtaMdHHDId23hNYdrs
rhK2pUXfvRE7S/evuQa2UwjV9PsSMicSFxT8LZgOYsjL73zGuUGGhCTqk3NwtXGSzNsPMMAMsjOO
bbrl5N42TAouLvsaOmiMqjDYPItE3l0GfVYtdIf3vDN5hc7wUDI0Kq4WuXLDaSEK1IyO90KY4+LC
fc/9hwRv1iW96UK71wEYeWL1UV6ij3ACRNTR4uM=

password:

fafafafa

The Dart code produces an exception:

Unhandled exception:
Invalid argument(s): Invalid or corrupted pad block

,which I suspect happens because in the Dart code, I make use of PKCS7Padding while in my code there is not padding performed on the data.

I am a bit lost, I couldn't find a way to process the data while using salt without unpadding.

If I skip the PaddedBlockCipherImpl and plain call

  CBCBlockCipher cipher = CBCBlockCipher(AESEngine());


    ParametersWithIV<KeyParameter> params =
    ParametersWithIV<KeyParameter>(KeyParameter(key), iv);

    cipher.init(false, params);


    var val = cipher.process(encrypted);
    String res = String.fromCharCodes(val);

    print('res => $res');

I get garbage values.


Solution

  • You are using the wrong block size in the HMAC - it should be 64 bytes. Change generateKey to this:

    Uint8List generateKey(String passphrase, Uint8List salt) {
      final derivator = PBKDF2KeyDerivator(HMac(SHA1Digest(), 64))
        ..init(Pbkdf2Parameters(salt, 1024, 16));
      return derivator.process(utf8.encode(passphrase) as Uint8List);
    }
    

    It's tidier to create the cipher in a few lines like this:

      final cipher = PaddedBlockCipherImpl(
        PKCS7Padding(),
        CBCBlockCipher(AESEngine()),
      )..init(
        false,
        PaddedBlockCipherParameters(
          ParametersWithIV(KeyParameter(key), iv),
          null,
        ),
      );
    
      final pt = cipher.process(encrypted);