解密时最初的几个字符被截断
Initial few characters are getting truncated while decrypting
我是 Java 的新手,正在尝试为文本文件实现 Java encryption/decryption。在这里,我可以看到在解密加密文件时,第 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:
My Encryption testing
This is my test encryption.
加密TestEncryption.txt:
fè"Ç2¨1.ñ|#¼Ê¯—Ã&ýnfK[ùn§Ù @ÛPzΕ¯sö®õ˜ýóK8Äܨ`‹¹*¢ÙF›Ã
解密TestEncryption.txt:
³‰XgÜOÂN‹¶ƒ×N›sting
This is my test encryption.
这里可能有更多的错误,但其中一个肯定是您对 IV 的处理。
每次你调用initializeInitVec()
,它都会在mInitVec
中存储一个随机 IV。因此,您不能将该方法用作检索先前加密操作中使用的 IV 的方法。
不幸的是,您当前的代码正是这样做的。结果,您尝试使用错误的 IV 进行解密,并且可能会得到一个填充异常,该异常被输入流吞没了。
要解决此问题,您必须将 IV 与加密数据一起存储。一种常见的方法是将您的 IV 写入您的文件,然后是您的加密内容。解密需要先阅读本IV,再解密以下内容
我是 Java 的新手,正在尝试为文本文件实现 Java encryption/decryption。在这里,我可以看到在解密加密文件时,第 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:
My Encryption testing
This is my test encryption.
加密TestEncryption.txt:
fè"Ç2¨1.ñ|#¼Ê¯—Ã&ýnfK[ùn§Ù @ÛPzΕ¯sö®õ˜ýóK8Äܨ`‹¹*¢ÙF›Ã
解密TestEncryption.txt:
³‰XgÜOÂN‹¶ƒ×N›sting
This is my test encryption.
这里可能有更多的错误,但其中一个肯定是您对 IV 的处理。
每次你调用initializeInitVec()
,它都会在mInitVec
中存储一个随机 IV。因此,您不能将该方法用作检索先前加密操作中使用的 IV 的方法。
不幸的是,您当前的代码正是这样做的。结果,您尝试使用错误的 IV 进行解密,并且可能会得到一个填充异常,该异常被输入流吞没了。
要解决此问题,您必须将 IV 与加密数据一起存储。一种常见的方法是将您的 IV 写入您的文件,然后是您的加密内容。解密需要先阅读本IV,再解密以下内容