我是Java的新手,并试图为文本文件实现Java加密/解密。在这里,我可以看到第1行中的一些起始字符在解密加密文件时被截断。
以下是我的代码。请告知我是否遗漏了任何东西。
package com.myprotection;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class Crypto
{
private String mPassword = null;
byte [] mInitVec = null;
byte [] mSalt = null;
Cipher mEcipher = null;
Cipher mDecipher = null;
private final int KEYLEN_BITS = 256;
private final int ITERATIONS = 65536;
private final int MAX_FILE_BUF = 1024;
public Crypto (String password, String hint) throws Exception
{
mPassword = password;
mSalt = getSaltedByteFromString( hint );
}
public void WriteEncryptedFile (String input, String output) throws Exception {
FileInputStream fin = new FileInputStream (new File(input));
FileOutputStream fout = new FileOutputStream (new File(output));
int nread = 0;
byte [] inbuf = new byte [MAX_FILE_BUF];
//Initialize Encryption Vector
initializeInitVec();
while ((nread = fin.read (inbuf)) > 0 )
{
// create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0
// and results in full blocks of MAX_FILE_BUF being written.
byte [] trimbuf = new byte [nread];
for (int i = 0; i < nread; i++)
trimbuf[i] = inbuf[i];
// encrypt the buffer using the cipher obtained previosly
byte [] tmp = mEcipher.update (trimbuf);
// I don't think this should happen, but just in case..
if (tmp != null)
fout.write (tmp);
}
// finalize the encryption since we've done it in blocks of MAX_FILE_BUF
byte [] finalbuf = mEcipher.doFinal ();
if (finalbuf != null)
fout.write (finalbuf);
fout.flush();
fin.close();
fout.close();
fout.close ();
}
public void ReadEncryptedFile (String input, String output) throws Exception {
FileInputStream fin = new FileInputStream (new File(input));
FileOutputStream fout = new FileOutputStream (new File(output));
CipherInputStream cin;
int nread = 0;
byte [] inbuf = new byte [MAX_FILE_BUF];
//Initializing decrypting
setupDecrypt();
// creating a decoding stream from the FileInputStream above using the cipher created from setupDecrypt()
cin = new CipherInputStream (fin, mDecipher);
while ((nread = cin.read (inbuf)) > 0 )
{
// create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0
byte [] trimbuf = new byte [nread];
for (int i = 0; i < nread; i++)
trimbuf[i] = inbuf[i];
// write out the size-adjusted buffer
fout.write (trimbuf);
}
fout.flush();
cin.close();
fin.close ();
fout.close();
}
private void setupDecrypt () throws Exception {
SecretKeyFactory factory = null;
SecretKey tmp = null;
SecretKey secret = null;
//Get initialized the vector
initializeInitVec();
factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(mPassword.toCharArray (), mSalt, ITERATIONS, KEYLEN_BITS);
tmp = factory.generateSecret(spec);
secret = new SecretKeySpec(tmp.getEncoded(), "AES");
/* Decrypt the message, given derived key and initialization vector. */
mDecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
mDecipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(mInitVec));
}
private void initializeInitVec() throws Exception {
SecretKeyFactory factory = null;
SecretKey tmp = null;
factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec (mPassword.toCharArray (), mSalt, ITERATIONS, KEYLEN_BITS);
tmp = factory.generateSecret (spec);
SecretKey secret = new SecretKeySpec (tmp.getEncoded(), "AES");
//Create the Encryption cipher object and store as a member variable
mEcipher = Cipher.getInstance ("AES/CBC/PKCS5Padding");
mEcipher.init (Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = mEcipher.getParameters ();
// get the initialization vectory and store as member var
mInitVec = params.getParameterSpec (IvParameterSpec.class).getIV();
}
private byte[] getSaltedByteFromString( String hintString ) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(hintString.getBytes("UTF-8"));
return md.digest();
}
}
测试上述代码:
public class TestCrypto {
public static void main(String[] args) throws Exception {
String pwd = "Tipu";
String hint = "My Dog Name";
Crypto cpo = new Crypto(pwd, hint);
cpo.WriteEncryptedFile("C:\\Users\\roul\\Desktop\\TestEncryption.txt", "C:\\Users\\roul\\Desktop\\en-TestEncryption.txt");
cpo.ReadEncryptedFile("C:\\Users\\roul\\Desktop\\en-TestEncryption.txt", "C:\\Users\\roul\\Desktop\\de-TestEncryption.txt");
}
}
测试用例文件内容 TestEncryption.txt :
我的加密测试
这是我的测试加密。
加密 TestEncryption.txt :
fè“Ç2¨1.ñ|#¼Ê¯-Ã&amp;ýnfK[ùn§Ù@ÛPzΕ¯sö®õ〜óK8Äܨ`<¹*¢ÙF>Ã
解密 TestEncryption.txt :
³‰XgÜOÂN<¶ƒ×N>刺
这是我的测试加密。
答案 0 :(得分:1)
这里可能会有更多错误,但肯定其中一个是你对IV的处理。
每次拨打initializeInitVec()
时,它都会在mInitVec
中存储随机 IV。因此,您不能使用该方法来检索先前加密操作中使用的IV。
不幸的是,您当前的代码确实如此。因此,您尝试使用错误的IV进行解密,并且可能会获得填充异常,该异常会被输入流吞噬。
要解决此问题,您必须使用加密数据存储IV。一种常见的方法是将IV写入文件,然后是加密内容。对于解密,您必须首先阅读此IV,然后解密以下内容。