本文要介绍的是基于 Java 语言实现 AES256 加解密文件功能,主要流程包括
- 读取文件明文数据,通过 AES256 加密算法进行加密,将加密后的数据写回文件
-
读取文件密文数据,通过 AES256 解密算法进行解密,将解密后的数据写回文件
AES256 算法简介
AES(高级加密标准,Advanced Encryption Standard),对称加密算法,不同于 RSA 等非对称加密,其只使用一个密钥参与加密和解密。
密钥
AES256 中的 256
代表的是密钥的长度为 256位,此外还存在 AES128、AES192,AES256 的安全性最高,AES128性能最高,本质原因是它们的加密处理轮数不同。
填充
AES 算法在对明文加密的时候,并不是直接对明文数据进行加密,而是将明文拆分成一个一个独立的明文段,每一段长度为 128bit。然后这些明文段经过 AES 加密器处理,生成密文段,将密文段合并到一起,得到加密结果。
因为明文段是按照 128bit 长度进行拆分,就会存在长度不足 128bit 的情况,所以需要对不足的明文段进行填充。
Nopadding
不做任何填充,但是要求铭文必须是 16字节 的整数倍。
PKCS5Padding
不足的明文段,在末尾补足相应数量的字符,且每个字节的值等于缺少的字符数。
ISO10126Padding
不足的明文段,在末尾补足相应数量的字符,最后一个字符值等于缺少的字符数,其他字符值填充随机数。
模式
AES 的工作模式,分为 ECB、CBC、CTR、CFB、OFB。本文使用 CBC 模式,该模式会使用一个初始向量 IV
,加密的时候,第一个明文段会首先和初始向量 IV 做异或操作,然后再经过密钥加密,然后第一个密文块又会作为第二个明文段的加密向量来异或,依次类推下去,这样相同的明文段加密出来的密文块就是不同的,因此更加安全。
文件加密
本文主要介绍媒体文件的加密,所以不需要对文件的全部数据进行加密,仅仅加密头部部分数据即可,因为没有头部数据,这些媒体文件也是无法成功进行解码。使用部分数据进行加密,从而提高加密性能。
public static int encryptFile(File file, SecretKey secretKey, int encryptLength) {
try {
// 以 byte 的形式读取,不改变文件数据的编码格式
byte[] bytes = Files.readAllBytes(file.toPath());
// 仅加密 encryptLength 长度的数据
byte[] substring = new byte[encryptLength];
System.arraycopy(bytes, 0, substring, 0, encryptLength);
// 加密
byte[] encrypt = encrypt(substring, secretKey);
// 使用密文替换老数据
byte[] newContent = new byte[encrypt.length + bytes.length - encryptLength];
System.arraycopy(encrypt, 0, newContent, 0, encrypt.length);
System.arraycopy(bytes, encryptLength, newContent, encrypt.length, bytes.length - encryptLength);
// 覆盖写入文件
Files.write(file.toPath(), newContent);
return encrypt.length;
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
加密
private static byte[] encrypt(byte[] content, SecretKey secretKey) {
byte[] str = null;
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
str = cipher.doFinal(content);
} catch (Exception e) {
e.printStackTrace();
}
return str;
}
文件解密
因为数据加密后的长度与明文是不一致的,而文件加密只是部分加密,所以需要记录下密文的长度,从而读取密文,完成解密
public static void decryptFile(File file, SecretKey secretKey, int decryptLength) {
try {
// 以 byte 的形式读取,不改变文件数据的编码格式
byte[] bytes = Files.readAllBytes(file.toPath());
// 截取密文数据
byte[] substring = new byte[decryptLength];
System.arraycopy(bytes, 0, substring, 0, decryptLength);
// 解密
byte[] decrypt = decrypt(substring, secretKey);
// 使用明文替换加密数据
byte[] newContent = new byte[decrypt.length + bytes.length - decryptLength];
System.arraycopy(decrypt, 0, newContent, 0, decrypt.length);
System.arraycopy(bytes, decryptLength, newContent, decrypt.length, bytes.length - decryptLength);
// 覆盖写入文件
Files.write(file.toPath(), newContent);
} catch (Exception e) {
e.printStackTrace();
}
}
解密
private static byte[] decrypt(byte[] bytes, SecretKey secretKey) {
byte[] decryptStr = null;
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
decryptStr = cipher.doFinal(bytes);
} catch (Exception e) {
e.printStackTrace();
}
return decryptStr;
}
源码
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.file.Files;
/**
* AES 加解密
*/
public class AESEncrypt {
private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
private static final byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/**
* 生成 SecretKey
* @param secret
* @param salt
* @return
*/
public static SecretKey generateSecretKey(String secret, String salt) {
SecretKey secretKey = null;
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
PBEKeySpec keySpec = new PBEKeySpec(secret.toCharArray(), salt.getBytes(), 65536, 256);
secretKey = new SecretKeySpec(factory.generateSecret(keySpec).getEncoded(), "AES");
} catch (Exception e) {
e.printStackTrace();
}
return secretKey;
}
/**
* 加密
* @param content
* @param secretKey
* @return
*/
private static byte[] encrypt(byte[] content, SecretKey secretKey) {
byte[] str = null;
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
str = cipher.doFinal(content);
} catch (Exception e) {
e.printStackTrace();
}
return str;
}
/**
* 解密
* @param bytes
* @param secretKey
* @return
*/
private static byte[] decrypt(byte[] bytes, SecretKey secretKey) {
byte[] decryptStr = null;
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
decryptStr = cipher.doFinal(bytes);
} catch (Exception e) {
e.printStackTrace();
}
return decryptStr;
}
/**
* 文件加密
* @param file
* @param secretKey
*/
public static int encryptFile(File file, SecretKey secretKey, int encryptLength) {
try {
// 以 byte 的形式读取,不改变文件数据的编码格式
byte[] bytes = Files.readAllBytes(file.toPath());
// 仅加密 encryptLength 长度的数据
byte[] substring = new byte[encryptLength];
System.arraycopy(bytes, 0, substring, 0, encryptLength);
// 加密
byte[] encrypt = encrypt(substring, secretKey);
// 使用密文替换老数据
byte[] newContent = new byte[encrypt.length + bytes.length - encryptLength];
System.arraycopy(encrypt, 0, newContent, 0, encrypt.length);
System.arraycopy(bytes, encryptLength, newContent, encrypt.length, bytes.length - encryptLength);
// 覆盖写入文件
Files.write(file.toPath(), newContent);
return encrypt.length;
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
/**
* 文件解密
* @param file
* @param secretKey
* @param decryptLength
*/
public static void decryptFile(File file, SecretKey secretKey, int decryptLength) {
try {
// 以 byte 的形式读取,不改变文件数据的编码格式
byte[] bytes = Files.readAllBytes(file.toPath());
// 截取密文数据
byte[] substring = new byte[decryptLength];
System.arraycopy(bytes, 0, substring, 0, decryptLength);
// 解密
byte[] decrypt = decrypt(substring, secretKey);
// 使用明文替换加密数据
byte[] newContent = new byte[decrypt.length + bytes.length - decryptLength];
System.arraycopy(decrypt, 0, newContent, 0, decrypt.length);
System.arraycopy(bytes, decryptLength, newContent, decrypt.length, bytes.length - decryptLength);
// 覆盖写入文件
Files.write(file.toPath(), newContent);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
// generate secret key
SecretKey secretKey = generateSecretKey("password", "salt");
File file = new File(args[0]);
long encryptStart = System.currentTimeMillis();
int encryptLength = encryptFile(file, secretKey, 128);
long encryptEnd = System.currentTimeMillis();
System.out.printf("Encrypt %s cost %d%n", args[0], (encryptEnd - encryptStart));
decryptFile(file, secretKey, encryptLength);
System.out.printf("Decrypt %s cost %d%n", args[0], (System.currentTimeMillis() - encryptEnd));
} catch (Exception e) {
e.printStackTrace();
}
}
}