任务
从https://gitcode.net/tasking/Encrypt-decrypt-files/-/raw/master/AES.zip网址下载AES.zip,编写程序解压AES.zip,将得到若干经过先经过AES算法加密,然后经过Base64编码后的文件,秘钥为1111222233334444,加密模式为CBC,偏移量为5555666677778888,请解密文件中的内容,并且重新打包成一个压缩包。
咱写一个工具类,把上面任务都完成。创建一个工具类Util,代码如下
import sun.misc.BASE64Decoder;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
public class Util {
private static final int KEY_LENGTH_16 = 16;
private static final int KEY_LENGTH_32 = 32;
private static final String AES = "AES";
private static final String AES_CBC_NO_PADDING = "AES/CBC/NoPadding";
/**
* 远程下载文件并读取返回p
* @param filePath 文件网络地址 如http://www.baidu.com/1.txt
* @return String
*/
public static void DownAndReadFile(String filePath,String dirPath){
String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
String dir;
//使用默认项目路径
if(dirPath==null||dirPath.length()==0){
dir=""+date;
}else {
//指定路径下载
dir=dirPath;
}
File savePath = new File(dir);//创建新文件
if (!savePath.exists()) {
savePath.mkdir();
}
String[] urlname = filePath.split("/");
int len = urlname.length-1;
String uname = urlname[len];//获取文件名
try {
File file = new File(savePath+"//"+uname);//创建新文件
if(file!=null && !file.exists()){
file.createNewFile();
}
OutputStream oputstream = new FileOutputStream(file);
URL url = new URL(filePath);
HttpURLConnection uc = (HttpURLConnection) url.openConnection();
uc.setDoInput(true);//设置是否要从 URL 连接读取数据,默认为true
uc.connect();
InputStream iputstream = uc.getInputStream();
System.out.println("file size is:"+uc.getContentLength());//打印文件长度
byte[] buffer = new byte[4*1024];
int byteRead = -1;
while((byteRead=(iputstream.read(buffer)))!= -1){
oputstream.write(buffer, 0, byteRead);
}
oputstream.flush();
iputstream.close();
oputstream.close();
} catch (Exception e) {
System.out.println("读取失败!");
e.printStackTrace();
}
}
/**
* 解压缩包
* @param zipFile 源压缩文件
* @param destFile 压缩目录
*/
public static void unzip(File zipFile,File destFile) throws IOException {
if(zipFile == null)return;
if (destFile.isFile())return;
//destFile目录不存在,创建该目录
if(!destFile.exists()){
destFile.mkdirs();
}
ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipFile));
ZipEntry zipEntry;
while ((zipEntry=zipInputStream.getNextEntry())!=null){
String name = zipEntry.getName();
File file = new File(destFile,name);
if(zipEntry.isDirectory()){
file.mkdirs();
continue;
}
FileOutputStream fileOutputStream = new FileOutputStream(file);
int len = 0;
byte[] bytes = new byte[1024];
while ((len=zipInputStream.read(bytes))!=-1){
fileOutputStream.write(bytes,0,len);
}
zipInputStream.closeEntry();
fileOutputStream.close();
}
zipInputStream.close();
}
/**
* AES ECB模式解密
*
* @param key 加密的秘钥
* @param encrypt 加密后的内容
* @param iv 偏移矢量
* @return 解密后的内容
* @throws Exception 异常信息
*/
public static String decryptByCBC(String key, String encrypt, String iv) throws Exception {
checkKey(key);
checkIV(iv);
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES);
//AES/ECB/PKCS5Padding 格式为 "算法/模式/补码方式"
Cipher cipher = Cipher.getInstance(AES_CBC_NO_PADDING);
//偏移矢量
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
//设置为解密模式,解密的key,偏移矢量
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
//base64解密
byte[] decodeBuffer = new BASE64Decoder().decodeBuffer(encrypt);
//aes解密
byte[] bytes = cipher.doFinal(decodeBuffer);
return new String(bytes);
}
/**
* 检查key是否合法
*
* @param key {@link String}秘钥信息
* @throws Exception 异常信息
*/
private static void checkKey(String key) throws Exception {
if (key == null || key.length() != KEY_LENGTH_16 && key.length() != KEY_LENGTH_32) {
throw new Exception("加密秘钥不正确");
}
}
/**
* 检查偏移矢量是否合法
*
* @param iv {@link String} 偏移矢量
* @throws Exception 异常信息
*/
private static void checkIV(String iv) throws Exception {
if (iv == null || iv.length() != KEY_LENGTH_16) {
throw new Exception("偏移矢量不正确,必须为16位");
}
}
/**
* 读取文件内容
* @param fileName 文件路径
* @return 文件全部内容
*/
public static String readContext(String fileName) throws Exception {
String context="";
if(fileName==null||fileName.length()==0){
throw new Exception("文件路径不能为空!");
}
File file = new File(fileName);
if (file.isDirectory()){
throw new Exception("该文件是目录!");
}
BufferedReader reader = new BufferedReader(new FileReader(file));
int length;
char[] chars = new char[1024];
while ((length=reader.read(chars))!=-1){
context+=new String(chars,0,length);
}
reader.close();
return context;
}
/**
* 将内容写入到指定文件中
* @param fileName 文件路径名
* @param context 写入内容
*/
public static void write(String fileName, String context) throws Exception {
File file = new File(fileName);
if(!file.exists()){
file.createNewFile();
}
if(file.isDirectory()) throw new Exception("该文件是目录!");
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
writer.write(context);
writer.close();
}
/**
* 压缩文件
* @param fileList 待压缩的文件
* @param destFile 目标压缩包文件
*/
public static void zip(List<File> fileList, File destFile) throws IOException {
if(fileList==null||fileList.isEmpty()){
return;
}
OutputStream out = new FileOutputStream(destFile);
ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(out));
//设置压缩等级,范围0-9 压缩等级越高,压缩体积越小,压缩时间越长
zipOutputStream.setLevel(5);
for (File file : fileList){
compress(file,zipOutputStream);
}
zipOutputStream.close();
}
private static void compress(File file, ZipOutputStream zipOutputStream) throws IOException {
if (file.isFile()){
compressFile(file,file.getName(),zipOutputStream);
}else{
compressDir(file,file.getName(),zipOutputStream);
}
}
/**
* 压缩文件类型
* @param file 文件对象
* @param name 文件名称
* @param zipOutputStream zip流
*/
private static void compressFile(File file, String name, ZipOutputStream zipOutputStream) throws IOException {
//zip文件条目
ZipEntry zipEntry = new ZipEntry(name);
zipOutputStream.putNextEntry(zipEntry);
FileInputStream fileInputStream = new FileInputStream(file);
byte[] bytes = new byte[1024];
int len = 0;
while((len=fileInputStream.read(bytes))!=-1){
zipOutputStream.write(bytes,0,len);
}
zipOutputStream.closeEntry();
fileInputStream.close();
}
/**
* 压缩文件夹
* @param file 文件夹对象
* @param name 文件夹名称
* @param zipOutputStream zip流
*/
private static void compressDir(File file, String name, ZipOutputStream zipOutputStream) throws IOException {
if(file.isDirectory()){
File[] files = file.listFiles();
zipOutputStream.putNextEntry(new ZipEntry(name+"/"));
zipOutputStream.closeEntry();
if(files!=null){
for(File f:files){
if(f.isFile()){
compressFile(f,name+"/"+f.getName(),zipOutputStream);
}else {
compressDir(f,name+"/"+f.getName(),zipOutputStream);
}
}
}
}
}
}
写一个测试类,代码如下
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) throws Exception {
//网址下载AES.zip
Util.DownAndReadFile("https://gitcode.net/tasking/Encrypt-decrypt-files/-/raw/master/AES.zip","down");
//解压AES.zip
Util.unzip(new File("down/AES.zip"),new File("downzip"));
//解密文件中的内容
String context = Util.readContext("downzip/1.txt");
String s = Util.decryptByCBC("1111222233334444", context, "5555666677778888");
System.out.println(s);
//解密文件中的内容
String context2 = Util.readContext("downzip/2.txt");
String s2 = Util.decryptByCBC("1111222233334444", context2, "5555666677778888");
System.out.println(s2);
//把解密内容写入到文件中
Util.write("1.txt",s);
Util.write("2.txt",s2);
//重新打包成一个压缩包
List<File> list = new ArrayList<>();
list.add(new File("1.txt"));
list.add(new File("2.txt"));
Util.zip(list,new File("newZIP.zip"));
}
}