AES encryption, extra garbage characters in the decrypted file

Im doing the debug loggin function in an android app. I have a simple class that is written to the .txt file using 128-bit AES-encryption.

After logging is complete, I decrypt the registered file using a simple JAVA program.

The problem is when I decrypt the encrypted log , I received some strange content in it , I also received the encrypted content, but there are additional characters, see below.

Android Application Registration Part:

public class FileLogger { //file and folder name public static String LOG_FILE_NAME = "my_log.txt"; public static String LOG_FOLDER_NAME = "my_log_folder"; static SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss_SSS"); //My secret key, 16 bytes = 128 bit static byte[] key = {1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6}; //Appends to a log file, using encryption public static void appendToLog(Context context, Object msg) { String msgStr; String timestamp = "t:" + formatter.format(new java.util.Date()); msgStr = msg + "|" + timestamp + "\n"; File sdcard = Environment.getExternalStorageDirectory(); File dir = new File(sdcard.getAbsolutePath() + "/" + LOG_FOLDER_NAME); if (!dir.exists()) { dir.mkdir(); } File encryptedFile = new File(dir, LOG_FILE_NAME); try { //Encryption using my key above defined Key secretKey = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] outputBytes = cipher.doFinal(msgStr.getBytes()); //Writing to the file using append mode FileOutputStream outputStream = new FileOutputStream(encryptedFile, true); outputStream.write(outputBytes); outputStream.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } } } 

And this is the JAVA decrypter program:

 public class Main { //output file name after decryption private static String decryptedFileName; //input encrypted file private static String fileSource; //a prefix tag for output file name private static String outputFilePrefix = "decrypted_"; //My key for decryption, its the same as in the encrypter program. static byte[] key = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6 }; //Decrypting function public static void decrypt(byte[] key, File inputFile, File outputFile) throws Exception { try { Key secretKey = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, secretKey); FileInputStream inputStream = new FileInputStream(inputFile); byte[] inputBytes = new byte[(int) inputFile.length()]; inputStream.read(inputBytes); byte[] outputBytes = cipher.doFinal(inputBytes); FileOutputStream outputStream = new FileOutputStream(outputFile, true); outputStream.write(outputBytes); inputStream.close(); outputStream.close(); } catch (Exception ex) { ex.printStackTrace(); } } //first argument is the intput file source public static void main(String[] args) { if (args.length != 1) { System.out.println("Add log file name as a parameter."); } else { fileSource = args[0]; try { File sourceFile = new File(fileSource); if (sourceFile.exists()) { //Decrption decryptedFileName = outputFilePrefix + sourceFile.getName(); File decryptedFile = new File(decryptedFileName); decrypt(key, sourceFile, decryptedFile); } else { System.out.println("Log file not found: " + fileSource); } } catch (Exception e) { e.printStackTrace(); } System.out.println("Decryption done, output file: " + decryptedFileName); } } } 

Decrypted log output (opened with notepad ++):

enter image description here

Here is the valid content, but you can also see additional thrash characters. If I open the default Windows text editor, I also have trash characters, but different ones.

This is my first attempt with encrypt -decrypt, what am I doing wrong? Any ideas?

0
source share
2 answers

AES is a block cipher that works only on blocks. The plaintext that you want to encrypt can be any length, so the cipher should always fill in the plaintext to fill it to a multiple of the block size (or add a full block when it is already a multiple of the block size). In this add-on, PKCS # 5 / PKCS # 7, each fill byte indicates the number of bytes filled.

An easy fix would be to repeat over outputBytes during decryption and delete those padding bytes, which are always on the next line. This will break as soon as you use multi-line log messages or use semantically safe mode (more on this later).

A better solution would be to write the number of bytes for each log message before the message, read this and decrypt only that many bytes. It is also probably easier to implement with file streams.

Currently using Cipher.getInstance("AES"); which is an incomplete version of Cipher.getInstance("AES/ECB/PKCS5Padding"); . ECB mode is not semantically safe. It simply encrypts each block (16 bytes) using AES and a key. Thus, blocks that are the same will be the same in the ciphertext. This is especially bad because some log messages start the same way and an attacker can distinguish them. This is also the reason that decrypting the entire file worked, even though it was encrypted in pieces. You must use CBC mode with random IV.

Here is a sample code for using AES correctly in CBC mode with a random IV using streams:

 private static SecretKey key = generateAESkey(); private static String cipherString = "AES/CBC/PKCS5Padding"; public static void main(String[] args) throws Exception { ByteArrayOutputStream log = new ByteArrayOutputStream(); appendToLog("Test1", log); appendToLog("Test2 is longer", log); appendToLog("Test3 is multiple of block size!", log); appendToLog("Test4 is shorter.", log); byte[] encLog = log.toByteArray(); List<String> logs = decryptLog(new ByteArrayInputStream(encLog)); for(String logLine : logs) { System.out.println(logLine); } } private static SecretKey generateAESkey() { try { return KeyGenerator.getInstance("AES").generateKey(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null; } private static byte[] generateIV() { SecureRandom random = new SecureRandom(); byte[] iv = new byte[16]; random.nextBytes(iv); return iv; } public static void appendToLog(String s, OutputStream os) throws Exception { Cipher cipher = Cipher.getInstance(cipherString); byte[] iv = generateIV(); cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv)); byte[] data = cipher.doFinal(s.getBytes("UTF-8")); os.write(data.length); os.write(iv); os.write(data); } public static List<String> decryptLog(InputStream is) throws Exception{ ArrayList<String> logs = new ArrayList<String>(); while(is.available() > 0) { int len = is.read(); byte[] encLogLine = new byte[len]; byte[] iv = new byte[16]; is.read(iv); is.read(encLogLine); Cipher cipher = Cipher.getInstance(cipherString); cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); byte[] data = cipher.doFinal(encLogLine); logs.add(new String(data, "UTF-8")); } return logs; } 
+3
source

You have encrypted every log message with a clear encryption context. When you call the doFinal method on an encryption object, the plaintext is supplemented by a multiple of 16. Effectively, your log file is a sequence of many small encrypted messages. However, when decrypting, you ignore these message boundaries and treat the file as one encrypted message. As a result, padding characters will not be correctly separated. What you see as "garbage" is most likely such filling bytes. You will need to change the format of your log file in order to preserve message boundaries so that the decoder can detect or completely eliminate them.

Also, do not use the default values ​​in Java cryptography: they are not portable. For example, Cipher.getInstance() takes a line of the form alg/mode/padding . Always list all three. I noticed that you also use the no-args String.getBytes() method by default. Always indicate Charset, and almost always β€œUTF8” is the best choice.

+3
source

Source: https://habr.com/ru/post/1242344/


All Articles