我正在尝试使文件解密在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。
答案 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将其余部分解密。
希望这个答案对其他任何人都有用。