javax.crypto.BadPaddingException: Decryption Error - Not able to decrypt multiple blocks from file

384 views Asked by At

Hoping somebody can point me in the right direction here.

I'm not particularly familiar with encryption (in Java or otherwise), but I said that I'd help somebody out with a task they have that specifies using RSA encrypt/decrypt.

To familiarise myself with what is trying to be accomplished, I've put together a little example program that:

  • Generates 6 random numbers, and puts them in a comma-separated string
  • Encrypts the sequence of numbers
  • Appends the encrypted numbers sequence to a file - the same file can be written to more than once if the program is run multiple times
  • Reads the encrypted file - should get all sequences that have been written
  • Decrypt each sequence and print out a collection of comma-separated strings

This works fine on the first attempt - i.e. when the first sequence is written into an empty file - and the correct number sequence is returned.

When the file contains more than one encrypted sequence, this causes the decrypt routine to crash with a BadPaddingException. I've done a step-through at this point, and each of the byte[] (representing an encrypted sequence) to be decrypted is 128 bytes, so it's not like an irregular number of bytes is causing the problem.

I know that RSA isn't the recommended way to go about it anymore, but this is the specification that I'm having to get my head around. I'm also aware that there's a ton of SO questions to do with RSA and BadPaddingException, but I haven't come across one that deals with this issue.

My example code is below:

public class EncryptDecrypt {
    
    private static Cipher cipher;
    private static KeyPair keyPair;

    public static void main(String[] args)
    {
        String[] numbers = getNumbers();
        String numbersStr = String.join(", ", numbers);
        System.out.println("Generated: " + numbersStr + ", NumBytes: " + numbersStr.getBytes().length);
        
        byte[] encryptedNumbers = encrypt(numbersStr);
        System.out.println("Encrypted: " + encryptedNumbers.toString() + ", NumBytes: " + encryptedNumbers.length);
        
        writeToFile(encryptedNumbers);
        System.out.println("Encrypted numbers written to data.txt");
        
        ArrayList<byte[]> encryptedData = readFromFile();
        System.out.println("Encrypted numbers read from data.txt, NumSequences: " + encryptedData.size());

        ArrayList<String> decryptedSequences = decrypt(encryptedData);
        for (int i = 0; i < decryptedSequences.size(); i++)
        {
            String sequence = decryptedSequences.get(i);
            System.out.println("Sequence " + i + ": " + sequence);
        }
    }
    
    private static String[] getNumbers()
    {
        String[] numbers = new String[6];
        
        int min = 1;
        int max = 60;
        
        for (int i = 0; i < numbers.length; i++)
        {
            double number = (Math.random() * (max - min) + min);
            numbers[i] = number >= 10 ? Integer.toString((int) number) : "0" + Integer.toString((int) number);
        }
        
        return numbers;
    }
    
    private static byte[] encrypt(String data)
    {
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048);
            keyPair = keyPairGenerator.generateKeyPair();
            PublicKey publicKey = keyPair.getPublic();
            cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            cipher.update(data.getBytes());
            byte[] encrypted = cipher.doFinal();
            
            return encrypted;
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException ex) {
            Logger.getLogger(EncryptDecrypt.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        return null;
    }
    
    private static void writeToFile(byte[] data)
    {
        FileOutputStream fileOut = null;
        try {
            File file = new File("data.txt");
            file.createNewFile();
            fileOut = new FileOutputStream(file, true);
            
            fileOut.write(data);
            fileOut.flush();
            fileOut.close();
        } catch (FileNotFoundException ex) {
            Logger.getLogger(EncryptDecrypt.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(EncryptDecrypt.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                fileOut.close();
            } catch (IOException ex) {
                Logger.getLogger(EncryptDecrypt.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    
    private static ArrayList<byte[]> readFromFile()
    {
        File file = new File("data.txt");
        if (file.exists())
        {
            try {
                ArrayList<byte[]> encryptedSequences = new ArrayList<>();
                FileInputStream fileIn = new FileInputStream(file);
                int blockSize = 128;
                int numBlocks = fileIn.available() / blockSize;
                
                for (int i = 0; i < numBlocks; i++)
                {
                    byte[] encryptedSequence = new byte[blockSize];
                    fileIn.read(encryptedSequence);
                    
                    encryptedSequences.add(encryptedSequence);
                }
                
                fileIn.close();
                
                return encryptedSequences;
            } catch (FileNotFoundException ex) {
                Logger.getLogger(EncryptDecrypt.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IOException ex) {
                Logger.getLogger(EncryptDecrypt.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        
        return null;
    }
    
    private static ArrayList<String> decrypt(ArrayList<byte[]> data)
    {
        try {
            cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            PrivateKey privateKey = keyPair.getPrivate();
            
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            
            ArrayList<String> decryptedStrings = new ArrayList<>();
            
            for (byte[] sequence : data)
            {
                byte[] decryptedBytes = cipher.doFinal(sequence);
                String decryptedString = new String(decryptedBytes);
                decryptedStrings.add(decryptedString);
            }
            
            return decryptedStrings;
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException ex) {
            Logger.getLogger(EncryptDecrypt.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        return null;
    }
}

If anyone can spot what's wrong with this, I'd really appreciate it!

Thanks

0

There are 0 answers