0
votes

I'm working on a personal project and need some help. It's similar to another question that was asked, namely this one, except I'm trying to encrypt something in Dart and decrypt it in Java using AES256-CBC.

With the code provided in the link, I can currently encrypt something in Java & Dart and decrypt it in Dart, but I can't currently decrypt it in Java, so hence my ask for help. I'm using the pointycastle package in Dart.

Here's my Dart code:

import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:pointycastle/api.dart';
import 'package:pointycastle/block/aes.dart';
import 'package:pointycastle/block/modes/cbc.dart';
import 'package:pointycastle/digests/sha1.dart';
import 'package:pointycastle/key_derivators/api.dart';
import 'package:pointycastle/key_derivators/pbkdf2.dart';
import 'package:pointycastle/macs/hmac.dart';
import 'package:pointycastle/padded_block_cipher/padded_block_cipher_impl.dart';
import 'package:pointycastle/paddings/pkcs7.dart';

void main() {
  runApp(const MyApp());

  //Salt = 20 bytes
  var randomSalt = Random.secure();
  var salt = List<int>.generate(20, (i) => randomSalt.nextInt(255));
  var saltU = Uint8List.fromList(salt);
  print("Salt: $saltU");

  //IV = 16 bytes
  var randomIV = Random.secure();
  var IV = List<int>.generate(16, (i) => randomIV.nextInt(255));
  var IVU = Uint8List.fromList(IV);
  print("IV: $IVU");

  Uint8List encriptedText = encryptList(Uint8List.fromList(utf8.encode("testcrypt")),saltU, IVU);
  print("Encrypted text: $encriptedText");

  var ciphertextFinal = <int>[];
  ciphertextFinal.addAll(saltU);
  ciphertextFinal.addAll(IVU);
  ciphertextFinal.addAll(encriptedText);
  var ciphertextFinal64 = base64.encode(ciphertextFinal);
  print("CiphertextFinal: $ciphertextFinal64");
  decrypt("yjQrc7Old3EbC+Vn7EM6jN93bNZ4e1Pf5UFFGJhPpGXJgdkwY3BzXo5IMa9KgI8VLG0j7A==");

}

String decrypt(String ciphertext) {

  Uint8List ciphertextlist = base64.decode(ciphertext);
  var salt = ciphertextlist.sublist(0, 20);
  var iv = ciphertextlist.sublist(20, 20 + 16);
  var encrypted = ciphertextlist.sublist(20 + 16);

  print('ciphertextlist => $ciphertextlist');

  Uint8List key = generateKey("Hello", 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 decrypted = String.fromCharCodes(val);
  print(decrypted);

  return "0";

}

Uint8List generateKey(String passphrase, Uint8List salt) {           // pass salt
  Uint8List passphraseInt8List = Uint8List.fromList(passphrase.codeUnits);
  KeyDerivator derivator = PBKDF2KeyDerivator(HMac(SHA1Digest(), 64));      // 64 byte block size
  Pbkdf2Parameters params = Pbkdf2Parameters(salt, 65556, 32);              // 32 byte key size
  derivator.init(params);
  return derivator.process(passphraseInt8List);
}

Uint8List encryptList(Uint8List data, Uint8List salt, Uint8List IV) {
  final CBCBlockCipher cbcCipher = CBCBlockCipher(AESEngine());
  final ParametersWithIV<KeyParameter> ivParams = ParametersWithIV<KeyParameter>(KeyParameter(generateKey("Hello", salt)), IV);
  final PaddedBlockCipherParameters<ParametersWithIV<KeyParameter>, Null> paddingParams = PaddedBlockCipherParameters<ParametersWithIV<KeyParameter>, Null>(
      ivParams, null);

  final PaddedBlockCipherImpl paddedCipher = PaddedBlockCipherImpl(PKCS7Padding(), cbcCipher);
  paddedCipher.init(true, paddingParams);

  return paddedCipher.process(data);
}

And here's my Java code:

import java.security.AlgorithmParameters;
import java.security.SecureRandom;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;

public class Main {
    public static void main(String[] args) throws Exception {

        //encrypt("testCrypto");
        decrypt("NOLGiYlfERwx3eTkpc6xCbUxsFOSjeLIB96qDjlseklJrcxxweS4MnT5yrN5EKlTC2lZYQ==");
    }

    public static void encrypt(String item) throws Exception {
        byte[] ivBytes;
        String password="Hello";
        /*you can give whatever you want for password. This is for testing purpose*/
        SecureRandom random = new SecureRandom();
        byte bytes[] = new byte[20];
        random.nextBytes(bytes);

        byte[] saltBytes = bytes;
        // Derive the key
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");

        PBEKeySpec spec = new PBEKeySpec(password.toCharArray(),saltBytes,65556,256);

        SecretKey secretKey = factory.generateSecret(spec);
        System.out.println(secretKey.toString());

        SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");

        System.out.println("saltBytes : " + Arrays.toString(saltBytes));


        //encrypting the word
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secret);
        AlgorithmParameters params = cipher.getParameters();
        ivBytes =   params.getParameterSpec(IvParameterSpec.class).getIV();
        System.out.println("ivBytes : " + Arrays.toString(ivBytes));

        byte[] encryptedTextBytes = cipher.doFinal(item.getBytes("UTF-8"));
        //prepend salt and vi
        byte[] buffer = new byte[saltBytes.length + ivBytes.length + encryptedTextBytes.length];
        System.arraycopy(saltBytes, 0, buffer, 0, saltBytes.length);
        System.arraycopy(ivBytes, 0, buffer, saltBytes.length, ivBytes.length);
        System.arraycopy(encryptedTextBytes, 0, buffer, saltBytes.length + ivBytes.length, encryptedTextBytes.length);
        String val = new Base64().encodeToString(buffer);
        System.out.println(val);


    }

    public static String decrypt(String encrypted) {
        final String key = "aesEncryptionKey";
        final String initVector = "encryptionIntVec";

        try {
            IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));

            return new String(original);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }

}

Trying to decrypt the ciphertext generated with the Java decrypt method will give the following error:

"Input length must be multiple of 16 when decrypting with padded cipher"

Any ideias? Thanks!

Cross-platform examples for en- and decryption using AES in CBC mode and PBKDF2 for key derivation in Java and Dart can be found here (Disclaimer: I'm the author): github.com/java-crypto/cross_platform_crypto/tree/main/…Michael Fehr