Расшифровка Android WRONG_FINAL_BLOCK_LENGTH (файл, зашифрованный с помощью Python) - PullRequest
0 голосов
/ 14 января 2019

Я пытаюсь, чтобы расшифровка файлов работала в Android. Файл, который у меня есть, был зашифрован из python с использованием Crypto.Cipher AES: полный код:

import os, binascii, struct
from Crypto.Cipher import AES

def encrypt_file():
    chunksize=64*1024
    iv = "96889af65c391c69"
    k1 = "cb3a44cf3cb120cc7b8b3ab777f2d912"
    file = "tick.png"
    out_filename = "entick.png"
    dir = os.path.dirname(__file__)+"\\"
    print(iv)
    encryptor = AES.new(key, AES.MODE_CBC, iv)
    in_filename = dir+file
    filesize = os.path.getsize(in_filename)
    with open(in_filename, 'rb') as infile:
        with open(out_filename, 'wb') as outfile:
            outfile.write(struct.pack('<Q', filesize))
            outfile.write(iv)
            while True:
                chunk = infile.read(chunksize)
                if len(chunk) == 0:
                    break
                elif len(chunk) % 16 != 0:
                    chunk += ' ' * (16 - len(chunk) % 16)
                outfile.write(encryptor.encrypt(chunk))

if __name__ == "__main__":
    encrypt_file()

Функция расшифровки Android (основная):

private static File main(String fname, File enfile, String IV, String key) {
    try {
        byte[] bkey = key.getBytes("UTF-8");
        byte[] bIV = IV.getBytes("UTF-8");
        Log.d("ByteLen","bkey:"+Integer.toString(bkey.length));
        Log.d("ByteLen","bIV:"+ Integer.toString(bIV.length));
        File aesFile;
        aesFile = enfile;
        Log.d("AESFILELENGTH", "aes length: " + aesFile.length());
        File aesFileBis = new File(String.valueOf(Environment.getExternalStorageDirectory().toPath()), "tick.png"); //to be replaced with fname

        FileInputStream fis;
        FileOutputStream fos;
        CipherInputStream cis;

        SecretKeySpec secretKey = new SecretKeySpec(bkey, "AES");
        Cipher decrypt = Cipher.getInstance("AES/CBC/PKCS5Padding");
        IvParameterSpec ivSpec = new IvParameterSpec(bIV);

        decrypt.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);

        fis = new FileInputStream(aesFile);

        cis = new CipherInputStream(fis, decrypt);

        fos = new FileOutputStream(aesFileBis);
        try {

            byte[] mByte = new byte[8];
            int i = cis.read(mByte);
            Log.i("MBYTE", "mbyte i: " + i);
            while (i != -1) {
                fos.write(mByte, 0, i);
                i = cis.read(mByte);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        fos.flush();
        fos.close();
        cis.close();
        fis.close();
        return aesFileBis;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

Модуль Crypto.Cipher вставляет IV в файл в виде байтов 8-24, поэтому я создал этот метод для их извлечения:

private String IV(File enfile) throws UnsupportedEncodingException, FileNotFoundException {
    int size = 24;
    byte bytes[] = new byte[size];
    byte tmpBuff[] = new byte[size];
    if(enfile.canRead()){
        //run decryption code

        FileInputStream fis= new FileInputStream(enfile);
        try {

            int read = fis.read(bytes, 0, size);
            if (read < size) {
                int remain = size - read;
                while (remain > 0) {
                    read = fis.read(tmpBuff, 0, remain);
                    System.arraycopy(tmpBuff, 0, bytes, size - remain, read);
                    remain -= read;
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    String IV = new String(bytes, "US-ASCII");
    IV = IV.substring(8,24);
    return IV;
}

Из функции расшифровки, которую я проверил и проверил, длина ключа составляет 32 байта, длина ключа - 16 байтов, и оба являются правильными ключом IV и ключом. Я знаю, что переключаюсь с байтового массива на строковый и обратно, но это только для тестирования.

Я просмотрел несколько постов, касающихся этой проблемы, и пока нашел только посты, касающиеся того, что ключ имеет неправильный размер в байтах или расшифровывает строки, а не файлы, и поэтому переключение кодировки base64, по-видимому, неприменимо. Я думаю, что проблема заключается в том, как Crypto.Cipher дополняет файлы, так как первые 8 байтов выглядят как ненужные (SO и NULL байты), а затем есть 16 байтов IV.

1 Ответ

0 голосов
/ 15 января 2019

Благодаря комментарию я добавил модуль Padding из crypto: https://github.com/dlitz/pycrypto/blob/master/lib/Crypto/Util/Padding.py

Я добавил свой код на Python:

from Crypto.Util.py3compat import * #solves bchr error

Я также скопировал функцию pad () из Padding.py в конец моего кода.

в функции записи в файл:

with open(in_filename, 'rb') as infile:
    with open(out_filename, 'wb') as outfile:
        outfile.write(iv) ##IV becomes the first 16 bytes, not using struct.pack() anymore
        while True:
            chunk = infile.read(chunksize)
            if len(chunk) == 0:
                break
            elif len(chunk) % 16 != 0:
                chunk += ' ' * (16 - len(chunk) % 16)
            outfile.write(encryptor.encrypt(pad(chunk, 16))) ##added padding here

Наконец, в коде Java я удалил функцию поиска IV и обновил основную функцию:

private static File main(String fname, File enfile, String key) {
    try {
        FileInputStream fis;
        File aesFile;
        aesFile = enfile;
        byte[] bkey = key.getBytes("UTF-8");
        fis = new FileInputStream(aesFile);
        byte[] IV = new byte[16];
        for(Integer i =0; i < 16; i++){
            IV[i] = (byte) fis.read();
        }
        Log.e("IV:",""+new String(IV, "US-ASCII"));
        Log.d("ByteLen","bkey:"+Integer.toString(bkey.length));
        Log.d("ByteLen","bIV:"+ Integer.toString(IV.length));
        aesFile = enfile;
        Log.d("AESFILELENGTH", "aes length: " + aesFile.length());
        File aesFileBis = new File(String.valueOf(Environment.getExternalStorageDirectory().toPath()), "file.png"); //to be replaced with fname
        FileOutputStream fos;
        CipherInputStream cis;
        SecretKeySpec secretKey = new SecretKeySpec(bkey, "AES");
        Cipher decrypt = Cipher.getInstance("AES/CBC/PKCS5Padding");
        IvParameterSpec ivSpec = new IvParameterSpec(IV);
        decrypt.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
        cis = new CipherInputStream(fis, decrypt);
        fos = new FileOutputStream(aesFileBis);
        try {
            byte[] mByte = new byte[8];
            int i = cis.read(mByte);
            Log.i("MBYTE", "mbyte i: " + i);
            while (i != -1) {
                fos.write(mByte, 0, i);
                i = cis.read(mByte);
            }
        } catch (IOException e) { e.printStackTrace();}
        fos.flush();
        fos.close();
        cis.close();
        fis.close();
        return aesFileBis;
    }catch(Exception e) {e.printStackTrace(); }
    return null;
}

Новые части кода берут первые 16 байтов из FileInputStream и помещают их в байтовый массив для использования в качестве IV, остальные затем дешифруются с использованием CBC / PKCS5Padding.

Надеюсь, этот ответ может быть полезен для всех остальных.

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