Android解密WRONG_FINAL_BLOCK_LENGTH(使用Python加密的文件)

时间:2019-01-14 14:58:19

标签: android python encryption

我正在尝试使文件解密在Android中工作。我已使用Crypto.Cipher AES从python加密了我的文件:完整代码:

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;
}

从Decrypt函数中,我检查并验证了密钥长度为32个字节,iv为16个字节长,并且两者都是正确的IV和密钥。我知道我正在从字节数组切换到字符串,然后再返回,但这只是为了测试。

我看过几篇有关此问题的文章,到目前为止,仅发现与密钥大小错误或解密字符串而不是文件有关的密钥的文章,因此切换base64编码似乎并不适用。我认为问题与Crypto.Cipher填充文件的方式有关,因为前8个字节看起来像垃圾(SO和NULL字节),然后有16个字节的IV。

1 个答案:

答案 0 :(得分:1)

感谢评论,我从crypto:https://github.com/dlitz/pycrypto/blob/master/lib/Crypto/Util/Padding.py

添加了Padding模块

我添加的我的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 finder功能并更新了主要功能:

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;
}

代码的新部分从FileInputStream中获取前16个字节,并将它们放入一个字节数组中以用作IV,然后使用CBC / PKCS5Padding将其余部分解密。

希望这个答案对其他任何人都有用。