上一篇讲了《AES加密算法入门》,这篇继续讲一下AES加密文件。就以AES/CBC/PKCS5Padding为例。
1. 对字节流加密
1. 代码
修改一下上一篇的AES类
package com.tricycle.aes;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
public class AES {
static Cipher cipher;
static final String AES_ALGORITHMS = "AES";
public static final String ALGORITHMS_ECB_PKCS5Padding = "AES/ECB/PKCS5Padding";
public static final String ALGORITHMS_CBC_PKCS5Padding = "AES/CBC/PKCS5Padding";
public static final String ALGORITHMS_CBC_NoPadding = "AES/CBC/NoPadding";
public static final String ALGORITHMS_ECB_NoPadding = "AES/ECB/NoPadding";
/*
* CBC need an initial vector; IV length must be 16 bytes long
*/
public static byte[] getIV() {
String iv = "1234567812345678";
return iv.getBytes();
}
public static SecretKey generateKey() {
SecretKey secretKey = null;
try {
secretKey = KeyGenerator.getInstance(AES_ALGORITHMS).generateKey();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return secretKey;
}
public static byte[] encryptCbcPKCS5Padding(byte[] source, SecretKey secretKey) {
byte[] encrypt = null;
try{
Cipher cipher = Cipher.getInstance(ALGORITHMS_CBC_PKCS5Padding);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(getIV()));
encrypt = cipher.doFinal(source);
} catch (Exception e) {
e.printStackTrace();
}
return encrypt;
}
public static byte[] decryptCbcPKCS5Padding(byte[] source, SecretKey secretKey) {
byte[] decrypt = null;
try{
Cipher cipher = Cipher.getInstance(ALGORITHMS_CBC_PKCS5Padding);
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(getIV()));
decrypt = cipher.doFinal(source);
} catch (Exception e) {
e.printStackTrace();
}
return decrypt;
}
}
FileEncryptor类:
package com.tricycle.aes;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import org.apache.log4j.Logger;
public class FileEncryptor {
private Logger logger = Logger.getLogger(this.getClass());
private SecretKey mSecretKey = null;
public FileEncryptor(SecretKey secretKey) {
mSecretKey = secretKey;
}
public void encrypt(String input, String output) {
if(input == null || output == null) {
logger.error("input == null || output == null");
return;
}
File inputFile = new File(input);
if(!inputFile.exists()) {
logger.error("file not exist: " + input);
return;
}
byte[] buffer = new byte[1024];
byte[] readBuffer = null;
byte[] encryptedBuffer = null;
int lenEncrypted = 0;
int lenRead = 0;
BufferedInputStream is = null;
BufferedOutputStream os = null;
try {
is = new BufferedInputStream(new FileInputStream(inputFile));
os = new BufferedOutputStream(new FileOutputStream(output));
while((lenRead = is.read(buffer)) != -1) {
readBuffer = Arrays.copyOfRange(buffer, 0, lenRead);
encryptedBuffer = AES.encryptCbcPKCS5Padding(readBuffer, mSecretKey);
lenEncrypted = encryptedBuffer.length;
os.write(encryptedBuffer, 0, lenEncrypted);
}
} catch (Exception e) {
e.printStackTrace();
} finally{
try {
is.close();
os.flush();
os.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}
public void decrypt(String input, String output) {
if(input == null || output == null) {
logger.error("input == null || output == null");
return;
}
File inputFile = new File(input);
if(!inputFile.exists()) {
logger.error("file not exist: " + input);
return;
}
byte[] buffer = new byte[1024];
byte[] readBuffer = null;
byte[] encryptedBuffer = null;
int lenDecrypted = 0;
int lenRead = 0;
BufferedInputStream is = null;
BufferedOutputStream os = null;
try {
is = new BufferedInputStream(new FileInputStream(inputFile));
os = new BufferedOutputStream(new FileOutputStream(output));
while((lenRead = is.read(buffer)) != -1) {
readBuffer = Arrays.copyOfRange(buffer, 0, lenRead);
encryptedBuffer = AES.decryptCbcPKCS5Padding(readBuffer, mSecretKey);
lenDecrypted = encryptedBuffer.length;
os.write(encryptedBuffer, 0, lenDecrypted);
}
} catch (Exception e) {
e.printStackTrace();
} finally{
try {
is.close();
os.flush();
os.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}
}
2. 分析
要注意,一下这种写法是错误的,buffer的长度永远是1024,当buffer都会来的长度不足1024时就会出错。
while((len = is.read(buffer)) != -1) {
os.write(AES.encryptCbcPKCS5Padding(buffer, mSecretKey), 0, len);
}
2.使用CipherOutputStream和CipherInputStream
CipherOutputStream和CipherInputStream是继承OutputStream和InputStream,和BufferedOutputStream、BufferedInputStream类似,都是OutputStream和InputStream的装饰者,后面设想会在讲装饰者模式的时候用这个来举例。
public void encryptByCipherStream(String input, String output) {
if(input == null || output == null) {
logger.error("input == null || output == null");
return;
}
File inputFile = new File(input);
File outputFile = new File(output);
if(!inputFile.exists()) {
logger.error("file not exist: " + input);
return;
}
Cipher cipher;
OutputStream os = null;
InputStream is = null;
try {
cipher = Cipher.getInstance(AES.ALGORITHMS_CBC_PKCS5Padding);
cipher.init(Cipher.ENCRYPT_MODE, mSecretKey, new IvParameterSpec(AES.getIV()));
is = new BufferedInputStream(new FileInputStream(inputFile));
os = new CipherOutputStream(new BufferedOutputStream(new FileOutputStream(outputFile)), cipher);
byte[] buffer = new byte[1024];
int len = 0;
while((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
try {
is.close();
os.flush();
os.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}
public void decryptByCipherStream(String input, String output) {
if(input == null || output == null) {
logger.error("input == null || output == null");
return;
}
File inputFile = new File(input);
File outputFile = new File(output);
if(!inputFile.exists()) {
logger.error("file not exist: " + input);
return;
}
Cipher cipher;
OutputStream os = null;
InputStream is = null;
try {
cipher = Cipher.getInstance(AES.ALGORITHMS_CBC_PKCS5Padding);
cipher.init(Cipher.DECRYPT_MODE, mSecretKey, new IvParameterSpec(AES.getIV()));
is = new CipherInputStream(new BufferedInputStream(new FileInputStream(inputFile)), cipher);
os = new BufferedOutputStream(new FileOutputStream(outputFile));
byte[] buffer = new byte[1024];
int len = 0;
while((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
try {
is.close();
os.flush();
os.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}
3.测试
测试类
package com.tricycle.utils;
import com.tricycle.aes.AES;
import com.tricycle.aes.FileEncryptor;
public class Test {
private static final String AES_SOURCE_FILE = "G:\\javaTest\\AES\\sourceFile.txt";
private static final String AES_ENCRYPT_FILE = "G:\\javaTest\\AES\\encryptFile.txt";
private static final String AES_DECRYPT_FILE = "G:\\javaTest\\AES\\decryptFile.txt";
private static final String AES_ENCRYPT_FILE1 = "G:\\javaTest\\AES\\encryptFile1.txt";
private static final String AES_DECRYPT_FILE1 = "G:\\javaTest\\AES\\decryptFile1.txt";
public static void main(String[] args) {
FileEncryptor fileEncryptor = new FileEncryptor(AES.generateKey());
fileEncryptor.encrypt(AES_SOURCE_FILE, AES_ENCRYPT_FILE);
fileEncryptor.decrypt(AES_ENCRYPT_FILE, AES_DECRYPT_FILE);
fileEncryptor.encryptByCipherStream(AES_SOURCE_FILE, AES_ENCRYPT_FILE1);
fileEncryptor.decryptByCipherStream(AES_ENCRYPT_FILE, AES_DECRYPT_FILE1);
}
}
结果