# java 压缩与解压文件 - 加密与解密文件
# Java KeyGenerator 加密的概述:
AES/CBC/PKCS5Padding 是一种常见的加密配置组合,用于加密和解密数据。以下是每个部分的详细解释
# AES(Advanced Encryption Standard)
AES (高级加密标准) 是一种对称加密算法,用于加密和解密数据。它支持三种不同的密钥长度:
128 位,192 位和 256 位。AES 是一种分组密码,每次处理固定长度的数据块 (128 位,即 16 字节)
# CBC(Cipher Block Chaining)
CBC (密码分组链接) 是一种分组密码的工作模式。在 CBC 模式中,每个明文块在加密之前会与前一个密文块进行异或 (XOR) 操作。这使得相同的明文块在加密后会生成不同的密文块,从而增强了安全性。
- 初始向量 (IV):CBC 模式需要一个初始向量 (IV) 来开始第一个明文块的加密过程。IV 必须是唯一且不可预测的,但不需要保密。IV 的长度与分组大小相同 (对于 AES 是 16 字节)
- 加密过程:第一个明文块与 IV 进行异或后加密,生成第一个密文块。第二个明文块与第一个密文块异或后加密,生成第二个密文块,以此类推
# PKCS5Padding
PKCS5Padding 是一种填充 (Padding) 方案,用于确保待加密数据的长度是分组大小的整数倍。
AES 是一种分组密码,需要数据块的长度是分组大小的整数倍 (16 字节)。如果待加密数据的长度不是 16 字节的整数倍,就需要填充。
- 填充规则:PKCS5Padding 规定每个填充字节的值等于填充的字节数。例如,如果需要填充 4 个字节,则每个填充字节的值等于填充的字节数。例如,如果需要填充 4 个字节,则每个填充字节的值为
0x04
。如果需要填充 1 个字节,则填充值为0x01
。如果数据长度正好是分组大小的整数倍,则添加一个完整的填充块 (16 字节,每个字节值为0x10
)
# 结合在一起
AES/CBC/PKCS5Padding 结合使用时,数据先经过填充 (如果需要),然后分块。每个数据块在加密前会与前一个密文块异或,增强了安全性
明文 -> 填充 -> 分块 -> AES加密 (使用CBC模式和IV)
# 总结:
AES/CBC/PKCS5Padding 是一种安全的加密方案,适用于许多应用场景。AES 提供强大的加密能力,CBC 模式通过链式加密提高安全性,PKCS5Padding 确保数据块的长度适合加密处理。
# 代码如下:
import javax.crypto.*; | |
import javax.crypto.spec.IvParameterSpec; | |
import javax.crypto.spec.SecretKeySpec; | |
import java.io.*; | |
import java.nio.charset.Charset; | |
import java.nio.file.Files; | |
import java.nio.file.Paths; | |
import java.security.NoSuchAlgorithmException; | |
import java.security.SecureRandom; | |
import java.util.zip.ZipEntry; | |
import java.util.zip.ZipInputStream; | |
import java.util.zip.ZipOutputStream; | |
/** | |
* zipIO 学习 | |
* | |
* @author: 窦凯欣 | |
* @date: 2024-07-30 11:20 | |
**/ | |
@SuppressWarnings("all") | |
public class Demo { | |
// 加密算法 | |
private static final String ALGORITHM = "AES"; | |
// 转型 | |
private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding"; | |
// 加密秘钥大小 | |
private static final int KEY_SIZE = 128; | |
private static final int IV_SIZE = 16; | |
public static void main(String[] args) { | |
// 压缩文件 | |
String fileName = "D:\\决策支持平台\\五年规划报告模板"; | |
String descName = "D:\\解压缩目录\\刚压缩.zip"; | |
new Demo().zipFile(fileName, descName); | |
System.out.println("压缩文件完毕!"); | |
// 解压缩文件 | |
String zipFileName = "D:\\解压缩目录\\刚压缩.zip"; | |
String descFileName = "D:\\解压缩目录\\"; | |
new Demo().unzipFile(zipFileName, descFileName); | |
System.out.println("文件解压缩完毕!"); | |
// 生成密钥 | |
SecretKey secretKey = generateKey(); | |
// 将密钥保存到磁盘 | |
saveKey(secretKey, "D:\\secret.txt"); | |
// 生成 IV | |
IvParameterSpec iv = generateIv(); | |
// IV 保存到磁盘 | |
saveIv(iv, "D:\\iv.txt"); | |
System.out.println("密钥与IV生成并保存磁盘完毕,请做检查。"); | |
// 加密文件 | |
String fileNamePwd = "D:\\解压缩目录\\五年规划报告模板\\0-天然气管网业务“XX五”发展规划-发展基础(1-43).docx"; | |
String descFileNamePwd = "D:\\解压缩目录\\加密文件.dat"; | |
new BugFixes().encryptFile(secretKey, iv, fileNamePwd, descFileNamePwd); | |
System.out.println("文件加密完毕!"); | |
// 从文件加载密钥和 IV | |
SecretKey secretKeyHF = loadKey("D:\\secret.txt"); | |
IvParameterSpec ivHF = loadIv("D:\\iv.txt"); | |
System.out.println("secretKeyHF = " + secretKeyHF); | |
// 解密文件 | |
String fileNameHF = "D:\\解压缩目录\\加密文件.dat"; | |
String descFileNameHF = "D:\\解压缩目录\\解密文件.docx"; | |
new BugFixes().decryptFile(secretKeyHF, ivHF, fileNameHF, descFileNameHF); | |
System.out.println("文件解密完毕!"); | |
} | |
// 压缩文件 | |
public void zipFile(String srcDir, String zipFileName) { | |
try (FileOutputStream fos = new FileOutputStream(zipFileName); | |
ZipOutputStream zos = new ZipOutputStream(fos)) { | |
File srcFile = new File(srcDir); | |
compressDirectoryToZipFile(srcFile.getParent(), srcFile.getName(), zos); | |
} catch (Exception e) { | |
throw new RuntimeException(e); | |
} | |
} | |
// 递归目录中的文件进行处理 | |
private void compressDirectoryToZipFile(String rootDir, String srcDir, ZipOutputStream zos) throws IOException { | |
File srcFile = new File(rootDir, srcDir); | |
if (srcFile.isDirectory()) { | |
File[] files = srcFile.listFiles(); | |
if(files != null) { | |
for (File file : files) { | |
if (file.isDirectory()) { | |
compressDirectoryToZipFile(rootDir, srcDir + File.separator + file.getName(), zos); | |
} else { | |
compressFileToZipFile(rootDir, srcDir + File.separator + file.getName(), zos); | |
} | |
} | |
} | |
} else { | |
compressFileToZipFile(rootDir, srcDir, zos); | |
} | |
} | |
// 将传入的文件进行压缩 | |
private void compressFileToZipFile(String rootDir, String srcFile, ZipOutputStream zos) throws IOException { | |
File file = new File(rootDir, srcFile); | |
try (FileInputStream fis = new FileInputStream(file); | |
BufferedInputStream bis = new BufferedInputStream(fis)) { | |
ZipEntry zipEntry = new ZipEntry(srcFile); | |
zos.putNextEntry(zipEntry); | |
byte[] buffer = new byte[1024]; | |
int len; | |
while ((len = bis.read(buffer)) != -1) { | |
zos.write(buffer, 0, len); | |
} | |
zos.closeEntry(); | |
} | |
} | |
// 解压缩文件 | |
private void unzipFile(String zipFileName, String descFileName) { | |
String descFileNames = descFileName; | |
if (!descFileNames.endsWith(File.separator)) { | |
descFileNames = descFileNames + File.separator; | |
} | |
try ( | |
InputStream fileStream = Files.newInputStream(Paths.get(zipFileName)); | |
ZipInputStream zipInputStream = new ZipInputStream(fileStream, Charset.forName("GBK")) | |
) { | |
// 根据 ZIP 文件创建 ZipInputStream 对象 | |
ZipEntry entry; | |
while ((entry = zipInputStream.getNextEntry()) != null) { | |
if (entry.isDirectory()) { | |
// 如果是文件夹,创建目录 | |
boolean mkdir = new File(descFileNames + entry.getName()).mkdirs(); | |
System.out.println("创建目录" + (mkdir ? "成功" : "失败") + "了"); | |
continue; | |
} | |
// 创建输出文件 | |
File outFile = new File(descFileNames + entry.getName()); | |
// 创建父目录 | |
boolean mkdir = new File(outFile.getParent()).mkdirs(); | |
System.out.println("创建父目录" + (mkdir ? "成功" : "失败") + "了"); | |
try (BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(Files.newOutputStream(outFile.toPath()))) { | |
byte[] buf = new byte[1024]; | |
int readByte; | |
while ((readByte = zipInputStream.read(buf)) != -1) { | |
bufferedOutputStream.write(buf, 0, readByte); | |
} | |
} | |
} | |
} catch (Exception e) { | |
throw new RuntimeException(e); | |
} | |
} | |
// 生成密钥 | |
public static SecretKey generateKey() { | |
try { | |
KeyGenerator keyGen = KeyGenerator.getInstance(ALGORITHM); | |
keyGen.init(KEY_SIZE, new SecureRandom()); | |
return keyGen.generateKey(); | |
} catch (NoSuchAlgorithmException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
// 保存密钥到磁盘 | |
public static void saveKey(SecretKey key, String filePath) { | |
byte[] keyBytes = key.getEncoded(); | |
try (FileOutputStream fos = new FileOutputStream(filePath)) { | |
fos.write(keyBytes); | |
System.out.println("keyBytes = " + keyBytes); | |
} catch (Exception e) { | |
throw new RuntimeException(e); | |
} | |
} | |
// 生成 IV | |
public static IvParameterSpec generateIv() { | |
byte[] iv = new byte[IV_SIZE]; | |
new SecureRandom().nextBytes(iv); | |
return new IvParameterSpec(iv); | |
} | |
// 将 IV 保存到磁盘 | |
public static void saveIv(IvParameterSpec iv, String filePath) { | |
try { | |
Files.write(new File(filePath).toPath(), iv.getIV()); | |
System.out.println("iv.getIV() = " + iv.getIV()); | |
} catch (IOException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
// 加密文件 | |
public void encryptFile(SecretKey secretKey, IvParameterSpec iv, String srcFile, String destFile) { | |
try { | |
Cipher cipher = Cipher.getInstance(TRANSFORMATION); | |
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv); | |
try (FileInputStream fis = new FileInputStream(srcFile); | |
FileOutputStream fos = new FileOutputStream(destFile); | |
CipherOutputStream cos = new CipherOutputStream(fos, cipher)) { | |
byte[] buffer = new byte[1024]; | |
int read; | |
while ((read = fis.read(buffer)) != -1) { | |
cos.write(buffer, 0, read); | |
} | |
} | |
} catch (Exception e) { | |
throw new RuntimeException(e); | |
} | |
} | |
// 从文件加载密钥 | |
public static SecretKey loadKey(String filePath) { | |
try { | |
byte[] keyBytes = Files.readAllBytes(new File(filePath).toPath()); | |
return new SecretKeySpec(keyBytes, ALGORITHM); | |
} catch (IOException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
// 从文件加载 IV | |
public static IvParameterSpec loadIv(String filePath) { | |
try { | |
byte[] ivBytes = Files.readAllBytes(new File(filePath).toPath()); | |
return new IvParameterSpec(ivBytes); | |
} catch (IOException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
// 解密文件 | |
public void decryptFile(SecretKey secretKey, IvParameterSpec iv, String srcFile, String destFile) { | |
try { | |
Cipher cipher = Cipher.getInstance(TRANSFORMATION); | |
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv); | |
try (FileInputStream fis = new FileInputStream(srcFile); | |
CipherInputStream cis = new CipherInputStream(fis, cipher); | |
FileOutputStream fos = new FileOutputStream(destFile)) { | |
byte[] buffer = new byte[1024]; | |
int read; | |
while ((read = cis.read(buffer)) != -1) { | |
fos.write(buffer, 0, read); | |
} | |
} | |
} catch (Exception e) { | |
throw new RuntimeException(e); | |
} | |
} | |
} |
效果如下:
加密文件时会在指定位置生成加密的密钥和 IV (保持加密的一致性)