Encrypted in Java and Decrypting in dart flutter

1,016

In the Java code, the ciphertext is converted into a string using a charset encoding (like UTF-8). This generally corrupts the data. Here a binary-to-text encoding like base64 should be used.

The Java code posted in the question provides with the following changes:

import java.util.Base64;
...
Cipher desCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
...
String plainText = "The quick brown fox jumps over the lazy dog";
...
System.out.println("encrypted: " + Base64.getEncoder().encodeToString(textencrypted));
...

the following Base64 encoded ciphertext:

encrypted: Mj48sRIlEidcVHVHIw8i8PPXUQiZB3toykI7ODbzXvbrFyOO957Euy0mzgfbVGVF

This ciphertext can be decrypted using the Pointy Castle package with the following Dart code:

import 'dart:convert';
import 'dart:typed_data';
import 'package:pointycastle/api.dart';
import 'package:pointycastle/block/aes_fast.dart';
import 'package:pointycastle/block/modes/ecb.dart';
import 'package:pointycastle/padded_block_cipher/padded_block_cipher_impl.dart';
import 'package:pointycastle/paddings/pkcs7.dart';
...
Digest sha1 = Digest("SHA-1");
Uint8List key = sha1.process(utf8.encode('1234567890123456')).sublist(0, 16);
Uint8List ciphertext = base64.decode('Mj48sRIlEidcVHVHIw8i8PPXUQiZB3toykI7ODbzXvbrFyOO957Euy0mzgfbVGVF');
ECBBlockCipher cipher = ECBBlockCipher(AESFastEngine());
PaddedBlockCipher paddedCipher = new PaddedBlockCipherImpl(new PKCS7Padding(), cipher);
PaddedBlockCipherParameters<CipherParameters, CipherParameters> paddingParams = new PaddedBlockCipherParameters(KeyParameter(key), null);
paddedCipher.init(false, paddingParams);
Uint8List plainText = paddedCipher.process(ciphertext);
print(utf8.decode(plainText));

Please note the following: ECB is an insecure mode. More secure are CBC (see here for a dart example) or GCM, where the latter allows encryption as well as data authentication. Also the derivation of a key from a password with SHA-1 is insecure. Here a reliable key derivation function, e.g. PBKDF2, should be used.

Edit:
Converting a ciphertext into a string with a charset like UTF-8 will damage the data. You can find many posts on Stackoverflow, e.g. this one, which explains the problem in detail. This means that the Java code posted in your question won't work reliably if the ciphertext is converted with UTF-8.

However, there are charsets like ISO-8859-1 that do not corrupt the data. It is possible that such a charset is used on the server. This cannot be determined from the posted code, because no charset is specified during decoding (i.e. in new String(textencrypted)), so the default charset of the respective platform is applied, see here. Therefore, to check this possibility, you have to determine which default charset is used on the server.
Another way to analyze which encoding is used would be to check (or post) the code that is used to decrypt the ciphertext created by the server.

Most reliable is the use of encodings that are dedicated to convert binary data into a string, so called binary-to-text encodings, such as Base64, see here, which is why I used Base64 in the posted example.


You can verify the effects of a conversion using UTF-8 or ISO-8859-1 with the following Java code. The data from the posted example above is used as ciphertext:

convertDataWith(StandardCharsets.UTF_8);
convertDataWith(StandardCharsets.ISO_8859_1);
...
private static void convertDataWith(Charset charset) {
    String ciphertextBeforeB64 = "Mj48sRIlEidcVHVHIw8i8PPXUQiZB3toykI7ODbzXvbrFyOO957Euy0mzgfbVGVF";    // Data from posted example
    byte[] ciphertextBefore = Base64.getDecoder().decode(ciphertextBeforeB64);
    String ciphertextCharset = new String(ciphertextBefore, charset);                                   // Convert to String with specified charset
    byte[] ciphertextAfter = ciphertextCharset.getBytes(charset);                                       // Convert from String with specified charset
    String ciphertextAfterB64 = Base64.getEncoder().encodeToString(ciphertextAfter);
    System.out.println("Ciphertext BEFORE conversion: " + ciphertextBeforeB64);
    System.out.println("Ciphertext AFTER conversion:  " + ciphertextAfterB64);  
}

with the output:

Ciphertext BEFORE conversion: Mj48sRIlEidcVHVHIw8i8PPXUQiZB3toykI7ODbzXvbrFyOO957Euy0mzgfbVGVF
Ciphertext AFTER conversion:  Mj4877+9EiUSJ1xUdUcjDyLvv73vv73vv71RCO+/vQd7aO+/vUI7ODbvv71e77+977+9FyPvv73vv73vv73Euy0m77+9B++/vVRlRQ==

Ciphertext BEFORE conversion: Mj48sRIlEidcVHVHIw8i8PPXUQiZB3toykI7ODbzXvbrFyOO957Euy0mzgfbVGVF
Ciphertext AFTER conversion:  Mj48sRIlEidcVHVHIw8i8PPXUQiZB3toykI7ODbzXvbrFyOO957Euy0mzgfbVGVF

As explained above, UTF-8 corrupts the data.

Share:
1,016
Shubham
Author by

Shubham

Updated on December 25, 2022

Comments

  • Shubham
    Shubham over 1 year

    I am decrypting a text in dart which is encrypted in java. Here is the code of java which is used for encryption.

    package aes;
    
    import java.security.InvalidKeyException;
    import java.security.MessageDigest;
    import java.util.Arrays;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import javax.crypto.Cipher;
    import javax.crypto.spec.SecretKeySpec;
    
    public class Aes {
    
        public static void main(String[] args) {
            try {
    
                String keyString = "1234567890123456";//length of key is 16
                Cipher desCipher = Cipher.getInstance("AES");
                byte[] key = keyString.getBytes("UTF-8");
                MessageDigest sha = MessageDigest.getInstance("SHA-1");
                key = sha.digest(key);
                key = Arrays.copyOf(key, 16); // use only first 128 bit
    
                SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
                desCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
    
                String plainText = "abcd";
    
                byte[] text = plainText.getBytes("UTF-8");
                byte[] textencrypted = desCipher.doFinal(text);
                System.out.println("encrypted: " + new String(textencrypted));
    
            } catch (Exception ex) {
                Logger.getLogger(Aes.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    
    }
    
    

    Cipher desCipher = Cipher.getInstance("AES"); here mode of encryption is not defined. I found that when mode is not defined it uses AES/ECB/PKCS5Padding. Also IV is not used during encryption.

    I am migrating the java android application to flutter. data on the server is encrypted using this above java code. Now I can't change the all the in use data from the server. I have to decrypt it in flutter to show the data in flutter app.

    I am using encrypt: ^4.1.0 package for decryption in dart.

      Encrypted encryptedText = Encrypted.fromUtf8('ßȶ8)\œå7£');
      final key = Key.fromUtf8('1234567890123456');
      final iv = IV.fromLength(16);
    
      final encrypter = Encrypter(AES(key, mode: AESMode.ecb));
    
      final decrypted = encrypter.decrypt(encryptedText, iv: iv);
    
      print(decrypted); 
    

    but this code gives error as follows

    E/flutter (18070): [ERROR:flutter/lib/ui/ui_dart_state.cc(171)] Unhandled Exception: Invalid argument(s): Input data length must be a multiple of cipher's block size
    E/flutter (18070): #0      PaddedBlockCipherImpl.process (package:pointycastle/padded_block_cipher/padded_block_cipher_impl.dart:60:9)
    E/flutter (18070): #1      AES.decrypt (package:encrypt/src/algorithms/aes.dart:55:22)
    E/flutter (18070): #2      Encrypter.decryptBytes (package:encrypt/src/encrypter.dart:25:17)
    E/flutter (18070): #3      Encrypter.decrypt (package:encrypt/src/encrypter.dart:31:17)
    E/flutter (18070): #4      _MyHomePageState.sha1 (package:flutter_decrypt_video/main.dart:196:33)
    E/flutter (18070): #5      _MyHomePageState.build (package:flutter_decrypt_video/main.dart:85:5)
    E/flutter (18070): #6      StatefulElement.build (package:flutter/src/widgets/framework.dart:4681:28)
    E/flutter (18070): #7      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4564:15)
    E/flutter (18070): #8      StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4737:11)
    E/flutter (18070): #9      Element.rebuild (package:flutter/src/widgets/framework.dart:4280:5)
    E/flutter (18070): #10     ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4543:5)
    E/flutter (18070): #11     StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4728:11)
    E/flutter (18070): #12     ComponentElement.mount (package:flutter/src/widgets/framework.dart:4538:5)
    E/flutter (18070): #13     Element.inflateWidget (package:flutter/src/widgets/framework.dart:3508:14)
    E/flutter (18070): #14     Element.updateChild (package:flutter/src/widgets/framework.dart:3266:18)
    E/flutter (18070): #15     SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:5892:14)
    E/flutter (18070): #16     Element.inflateWidget (package:flutter/src/widgets/framework.dart:3508:14)
    E/flutter (18070): #17     Element.updateChild (package:flutter/src/widgets/framework.dart:3266:18)
    E/flutter (18070): #18     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4589:16)
    E/flutter (18070): #19     Element.rebuild (package:flutter/src/widgets/framework.dart:4280:5)
    E/flutter (18070): #20     ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4543:5)
    E/flutter (18070): #21     ComponentElement.mount (package:flutter/src/widgets/framework.dart:4538:5)
    E/flutter (18070): #22     Element.inflateWidget (package:flutter/src/widgets/framework.dart:3508:14)
    E/flutter (18070): #23     Element.updateChild (package:flutter/src/widgets/framework.dart:3266:18)
    E/flutter (18070): #24     SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:5892:14)
    E/flutter (18070): #25     Element.inflateWidget (package:flutter/src/widgets/framework.dart:3508:14)
    E/flutter (18070): #26     Element.updateChild (package:flutter/src/widgets/framework.dart:3266:18)
    E/flutter (18070): #27     SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:5892:14)
    E/flutter (18070): #28     Element.inflateWidget (package:flutter/src/widgets/framework.dart:3508:14)
    E/flutter (18070): #29     Element.updateChild (package:flutter/src/widgets/framework.dart:3266:18)
    E/flutter (18070): #30     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4589:16)
    E/flutter (18070): #31     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4737:11)
    E/flutter (18070): #32     Element.rebuild (package:flutter/src/widgets/framework.dart:4280:5)
    E/flutter (18070): #33     ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4543:5)
    E/flutter (18070): #34     StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4728:11)
    E/flutter (18070): #35     ComponentElement.mount (package:flutter/src/widgets/framework.dart:4538:5)
    E/flutter (18070): #36     Element.inflateWidget (package:flutter/src/widgets/framework.dart:3508:14)
    E/flutter (18070): #37     Element.updateChild (package:flutter/src/widgets/framework.dart:3266:18)
    E/flutter (18070): #38     SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:5892:14)
    E/flutter (18070): #39     Element.inflateWidget (package:flutter/src/widgets/framework.dart:3508:14)
    E/flutter (18070): #40     Element.updateChild (package:flutter/src/widgets/framework.dart:3266:18)
    E/flutter (18070): #41     SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:5892:14)
    E/flutter (18070): #42     Element.inflateWidget (package:flutter/src/widgets/framework.dart:3508:14)
    E/flutter (18070): #43     Element.updateChild (package:flutter/src/widgets/framework.dart:3266:18)
    E/flutter (18070): #44     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4589:16)
    E/flutter (18070): #45     StatefulElement.performRebuild (package:flutter/src/widgets/fram
    
    

    If anyone know how to do this please put your answer.

    In java code which is used for encryption SHA-1 hashing algorithm is used to get 160 bit of key and then it uses first 128 bits for encryption, may it causes any problem?

    If anyone knows any another package which can solve my issue please let me know.

    If anyone want any further information please let me know.

    Thanks in advance !!