我正在处理文件加密/解密应用。我正在使用一个简单的.txt文件进行测试。当我从应用程序中选择文件并选择加密时,整个文件数据都会被加密。但是,当我解密时,只有部分文件数据被解密。由于某种原因,前16个字节/字符不会被解密。
test_file.txt内容:"This sentence is used to check file encryption/decryption results."
加密结果:"¾mÁSTÐÿT:Y„"O¤]ÞPÕµß~ëqrÈb×ßq²¨†ldµJ,O|56\e^-’@þûÝû"
解密结果:"£ÿÒÜÑàh]VÄþ„- used to check file encryption/decryption results."
logcat中没有任何错误。
我做错了什么?
加密文件的方法:
public void encryptFile(String password, String filePath) {
byte[] encryptedFileData = null;
byte[] fileData = null;
try {
fileData = readFile(filePath);//method provided below
// 64 bit salt for testing only
byte[] salt = "goodsalt".getBytes("UTF-8");
SecretKey key = generateKey(password.toCharArray(), salt);//method provided below
byte[] keyData = key.getEncoded();
SecretKeySpec sKeySpec = new SecretKeySpec(keyData, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec);
encryptedFileData = cipher.doFinal(fileData);
saveData(encryptedFileData, filePath);//method provided below
}
catch (Exception e) {
e.printStackTrace();
}
}
读取文件内容的方法:
public byte[] readFile(String filePath) {
byte[] fileData;
File file = new File(filePath);
int size = (int) file.length();
fileData = new byte[size];
try {
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(file));
inputStream.read(fileData);
inputStream.close();
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
return fileData;
}
生成密钥的方法:
private SecretKey generateKey(char[] password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
// Number of PBKDF2 hardening rounds to use. Larger values increase computation time. You
// should select a value that causes computation to take >100ms.
final int iterations = 1000;
// Generate a 256-bit key
final int outputKeyLength = 256;
SecretKeyFactory secretKeyFactory;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// Use compatibility key factory -- only uses lower 8-bits of passphrase chars
secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1And8bit");
}
else {
// Traditional key factory. Will use lower 8-bits of passphrase chars on
// older Android versions (API level 18 and lower) and all available bits
// on KitKat and newer (API level 19 and higher).
secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
}
KeySpec keySpec = new PBEKeySpec(password, salt, iterations, outputKeyLength);
return secretKeyFactory.generateSecret(keySpec);
}
将加密/解密数据保存到文件的方法:
private void saveData(byte[] newFileData, String filePath) {
File file = new File(filePath);
try {
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(file));
outputStream.write(newFileData);
outputStream.flush();
outputStream.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
解密文件的方法:
public void decryptFile(String password, String filePath) {
byte[] decryptedFileData = null;
byte[] fileData = null;
try {
fileData = readFile(filePath);
byte[] salt = "goodsalt".getBytes("UTF-8");//generateSalt();
SecretKey key = generateKey(password.toCharArray(), salt);
byte[] keyData = key.getEncoded();
SecretKeySpec sKeySpec = new SecretKeySpec(keyData, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, sKeySpec);
decryptedFileData = cipher.doFinal(fileData);
saveData(decryptedFileData, filePath);
}
catch (Exception e) {
e.printStackTrace();
}
}
这行代码加密文件:
//simple password for testing only
encryptor.encryptFile("password", "storage/emulated/0/Download/test_file.txt");
此行解密文件:
encryptor.decryptFile("password", "storage/emulated/0/Download/test_file.txt");
编辑:感谢DarkSquirrel42和Oncaphillis。你们真棒!
将这行代码添加到加密和解密函数中解决了我的问题。
//note: the initialization vector (IV) must be 16 bytes in this case
//so, if a user password is being used to create it, measures must
//be taken to ensure proper IV length; random iv is best and should be
//stored, possibly alongside the encrypted data
IvParameterSpec ivSpec = new IvParameterSpec(password.getBytes("UTF-8"));
然后,
cipher.init(Cipher.XXXXXXX_MODE, sKeySpec, ivSpec);
答案 0 :(得分:7)
您的问题与密码的操作模式有关... cbc或密码块链接模式
一般来说,CBC很简单......无论你以前的加密模块的输出是什么,在加密之前将xor加到当前输入上
对于第一个块我们显然有问题...没有先前的块...因此我们引入了一个叫做IV的东西...一个初始化向量...一个随机字节块...
现在......你可以想象,当你想解密时,你需要相同的IV ......
因为你不能保存,所以AES实现每次都会给你一个随机的IV ......
因此,你没有解密第1块的所有信息...这是AES的前16个字节......
当处理CBC模式数据时,它总是一个很好的选择,只需在您的cypertext输出中添加使用过的IV ...... IV应该是随机的......这不是秘密......
答案 1 :(得分:2)
像@ÐarkSquirrel42已经指出CBC的en / decryption例程似乎将前16个字节解释为初始化向量。这对我有用:
// got to be random
byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
IvParameterSpec ivspec = new IvParameterSpec(iv);
cipher.init(Cipher.XXXXX_MODE, sKeySpec,ivspec);