Шифрование файлов с помощью криптографической архитектуры Java с использованием AES со 128-битным ключом и PBKDF2 - PullRequest
1 голос
/ 19 октября 2019

Я получаю эту ошибку при расшифровке файла

Я использую PBKDF2 для преобразования ключевой фразы в ключ, а затем использую ее. Шифрование работает хорошо, но когда я пытаюсь расшифровать тот же файл, он выдает ошибку ниже. Расшифрованный файл дает правильные данные, за исключением последних нескольких строк (вероятно, области заполнения). Я отлаживал его, выводя IV и ключ во время шифрования и дешифрования, и они оба одинаковы, но ошибка все еще существует.

public class FileEncryptorSkeleton{

    private static final String progName = "FileEncryptor";
    private static final int bufSize = 128;

    /**
        * @param args
        */
    public static void main(String[] args) throws UnsupportedEncodingException {

        BufferedInputStream in = null;          // A buffered input stream to read from
        BufferedOutputStream out = null;        // And a buffered output stream to write to
        SecretKeyFactory kf = null;             // Something to create a key for us
        KeySpec ks = null;                      // This is how we specify what kind of key we want it to generate
        byte[] salt = new byte[20];             // Some salt for use with PBKDF2, only not very salty
        SecretKey key = null;                   // The key that it generates
        Cipher cipher = null;                   // The cipher that will do the real work
        SecretKeySpec keyspec = null;           // How we pass the key to the Cipher
        int bytesRead = 0;                      // Number of bytes read into the input file buffer
        byte[] iv = new byte[16];
        // First, check the user has provided all the required arguments, and if they haven't, tell them then exit
        if(args.length != 4) {
            printUsageMessage(); System.exit(1);
        }

        // Open the input file
        try {
            in = new BufferedInputStream(new FileInputStream(args[1]));
        } catch (FileNotFoundException e) {
            printErrorMessage("Unable to open input file: " + args[1], null);
            System.exit(1);
        }

        // And then the output file
        try {
            out = new BufferedOutputStream(new FileOutputStream(args[2]));
        } catch (FileNotFoundException e) {
            printErrorMessage("Unable to open output file: " + args[2], e);
            System.exit(1);
        }

            try {
                kf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            } catch (NoSuchAlgorithmException ex) {
                Logger.getLogger(FileEncryptorSkeleton.class.getName()).log(Level.SEVERE, null, ex);
            }

        // Set up a KeySpec for password-based key generation of a 128-bit key
            ks = new PBEKeySpec(args[3].toCharArray(), salt, 1000, 128);




        // Now run the passphrase through PBKDF2 to get the key
            try {
                    key = kf.generateSecret(ks);
                    }catch(InvalidKeySpecException e){
        System.exit(1);
                    }

        // Get the byte encoded key value as a byte array
        byte[] aeskey = key.getEncoded();

        // Now generate a Cipher object for AES encryption in ECBC mode with PKCS #5 padding
        // Use ECB for the first task, then switch to CBC for versions 2 and 3
        try {
            cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        } catch (NoSuchAlgorithmException e) {
            printErrorMessage("No Such Algorithm Exception when creating main cipher", e);
            System.exit(2);
        } catch (NoSuchPaddingException e) {
            printErrorMessage("No Such Padding Exception when creating main cipher",e);
            System.exit(2);
        }

        // Set a variable to indicate whether we're in encrypt or decrypt mode, based upon args[0]
        int cipherMode = -1;
        char mode = Character.toLowerCase(args[0].charAt(0));
        switch (mode) {
            case 'e' : cipherMode = Cipher.ENCRYPT_MODE; break;
            case 'd' : cipherMode = Cipher.DECRYPT_MODE; break;
            default: printUsageMessage(); System.exit(1);
        }

        // Set up a secret key specification, based on the 16-byte (128-bit) AES key array previously generated
        keyspec = new SecretKeySpec(aeskey, "AES");
        IvParameterSpec ivspec = new IvParameterSpec(iv);
        // Now initialize the cipher in the right mode, with the keyspec and the ivspec
        try {

            cipher.init(cipherMode, keyspec,ivspec);

        } catch (InvalidKeyException e) {
            printErrorMessage("Invalid Key Spec",e); System.exit(2);
        } catch (InvalidAlgorithmParameterException ex) {
                Logger.getLogger(FileEncryptorSkeleton.class.getName()).log(Level.SEVERE, null, ex);
            }

        // Set up some input and output byte array buffers
        byte[] inputBuffer = new byte[bufSize];
        byte[] outputBuffer = null;

        // "Prime the pump" - we've got to read something before we can encrypt it
        // and not encrypt anything if we read nothing.
        try {
            bytesRead = in.read(inputBuffer);
        } catch (IOException e) {
            printErrorMessage("Error reading input file " + args[1],e); System.exit(1);
        }

        // As long as we've read something, loop around encrypting, writing and reading
        // bytesRead will be zero if nothing was read, or -1 on EOF - treat them both the same
        while (bytesRead > 0) {

            // Now encrypt this block
            outputBuffer = cipher.update(inputBuffer.toString().getBytes("UTF-8"));         
            // Write the generated block to file
            try {
                out.write(outputBuffer);
            } catch (IOException e) {
                printErrorMessage("Error writing to output file " + args[2],e); System.exit(1);
            }

            // And read in the next block of the file
            try {
                bytesRead = in.read(inputBuffer);
            } catch (IOException e) {
                printErrorMessage("Error reading input file " + args[1],e); System.exit(1);
            }
        }

            try {
                // Now do the final processing
                outputBuffer =cipher.doFinal(inputBuffer.toString().getBytes("UTF-8"));
                cipher.init(cipherMode, keyspec,ivspec);
                System.out.println(ivspec+"   "+cipherMode+"   "+keyspec);
            } catch (IllegalBlockSizeException ex) {
                Logger.getLogger(FileEncryptorSkeleton.class.getName()).log(Level.SEVERE, null, ex);
            } catch (BadPaddingException ex) {
                Logger.getLogger(FileEncryptorSkeleton.class.getName()).log(Level.SEVERE, null, ex);
            } catch (InvalidKeyException ex) {
                Logger.getLogger(FileEncryptorSkeleton.class.getName()).log(Level.SEVERE, null, ex);
            } catch (InvalidAlgorithmParameterException ex) {
                Logger.getLogger(FileEncryptorSkeleton.class.getName()).log(Level.SEVERE, null, ex);
            }

        // Write the final block of output
        try {
            out.write(outputBuffer);
        } catch (IOException e) {
            printErrorMessage("Error on final write to output file " + args[2],e); System.exit(1);
        }

        // Close the output files
        try {
            in.close();
            out.close();
        } catch (IOException e) {
            printErrorMessage("Error closing file", e);
        }

        // If we were continuing beyond this point, we should really overwrite key material, drop KeySpecs, etc.
    }

    /**
        * Print an error message on , optionally picking up additional detail from
        * a passed exception
        * @param errMsg
        * @param e
        */
    private static void printErrorMessage(String errMsg, Exception e) {
        System.err.println(errMsg);
        if (e != null) 
            System.err.println(e.getMessage());
    }

    /**
        * Print a usage message
        */
    private static void printUsageMessage() {
        System.out.println(progName + " $Revision: 1.1 $: Usage: " + progName + " E/D infile outfile passphrase");
    }

}

Oct 18, 2019 11:27:46 PM FileEncryptorSkeleton main
SEVERE: null
javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
        at com.sun.crypto.provider.CipherCore.unpad(CipherCore.java:975)
        at com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1056)
        at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:853)
        at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
        at javax.crypto.Cipher.doFinal(Cipher.java:2164)
        at FileEncryptorSkeleton.main(FileEncryptorSkeleton.java:174)

1 Ответ

0 голосов
/ 19 октября 2019

И метод Cipher#update - и Cipher#doFinal используют inputBuffer.toString(), который содержит только имя класса объекта и хэш-код, но не фактические данные в буфере.

Было бы правильно прочитать первые bytesRead байты из inputBuffer-byte[] (которые ранее были прочитаны из in-BufferedInputStream) и обработать их (Cipher#update):

outputBuffer = cipher.update(inputBuffer, 0, bytesRead); 

Цикл, содержащий cipher#update -колл, остается только тогда, когда ни один байт не был прочитан в inputBuffer-byte[], так что для окончательной обработки применяется (Cipher#doFinal):

outputBuffer = cipher.doFinal();

Кроме того, второй cipher#init звонок сразу после cipher#doFinal звонка не нужен (Cipher#init).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...