在日常开发中,我们经常需要对敏感数据或文件进行加密以保护其安全性。本文将介绍如何使用Node.js的crypto模块实现文件加密和解密的功能。
const fs = require('fs')
const path = require("path")
1. 加密文件
function encryptFile(sourceFilePath, targetEncryptedPath, password) {
return new Promise((resolve, reject) => {
const input = fs.createReadStream(sourceFilePath);
const output = fs.createWriteStream(targetEncryptedPath);
const algorithm = 'aes-256-cbc';
// 从密码中生成加密密钥和初始化向量
const key = crypto.scryptSync(password, 'salt', 32);
const iv = Buffer.alloc(16, 0);
const cipher = crypto.createCipheriv(algorithm, key, iv);
input.pipe(cipher).pipe(output);
output.on('finish', () => {
resolve();
});
cipher.on('error', (err) => {
reject(err);
});
});
}
在加密函数中,我们首先创建了一个读取流(input
)和一个写入流(output
),用于分别读取源文件和写入加密后的文件。然后,我们选择了一种对称加密算法(aes-256-cbc
)来加密文件内容。
通过密码生成加密密钥和初始化向量,我们使用crypto.createCipheriv
方法创建了一个加密器(cipher
)。接着,我们通过管道将读取流连接到加密器上,再将加密器通过管道连接到写入流上。这样,读取流中的数据会经过加密器进行加密,并写入到目标文件中。
最后,我们通过监听写入流的finish
事件来表示加密过程完成,并通过Promise的resolve方法返回结果。
2. 解密文件
function decryptFile(sourceEncryptedPath, targetDecryptedPath, password) {
return new Promise((resolve, reject) => {
const input = fs.createReadStream(sourceEncryptedPath);
const output = fs.createWriteStream(targetDecryptedPath);
const algorithm = 'aes-256-cbc';
// 从密码中生成加密密钥和初始化向量
const key = crypto.scryptSync(password, 'salt', 32);
const iv = Buffer.alloc(16, 0);
const decipher = crypto.createDecipheriv(algorithm, key, iv);
input.pipe(decipher).pipe(output);
output.on('finish', () => {
resolve();
});
decipher.on('error', (err) => {
reject(err);
});
});
}
解密函数的实现与加密函数类似。我们同样创建了一个读取流和一个写入流,用于分别读取加密文件和写入解密后的文件。然后,我们使用crypto.createDecipheriv
方法创建了一个解密器(decipher
),并将解密器通过管道连接到写入流上。
3. 使用示例
const sourceFilePath = '/path/to/source/file.zip';
const targetEncryptedPath = '/path/to/target/encrypted/file.zip';
const targetDecryptedPath = '/path/to/target/decrypted/file.zip';
const password = 'secretpassword';
encryptFile(sourceFilePath, targetEncryptedPath, password)
.then(() => {
console.log('文件加密完成');
return decryptFile(targetEncryptedPath, targetDecryptedPath, password);
})
.then(() => {
console.log('文件解密完成');
})
.catch((err) => {
console.error('加密或解密过程出错:', err);
});
在使用示例中,我们定义了源文件路径(sourceFilePath
)、目标加密文件路径(targetEncryptedPath
)、目标解密文件路径(targetDecryptedPath
)和密码(password
)。然后,我们调用encryptFile
函数来加密源文件,并在加密完成后调用decryptFile
函数来解密已加密的文件。
通过上述代码,我们可以实现对文件的加密和解密操作,保护敏感数据的安全性。唯一的缺点就是加密后如果不通过代码解密,使用压缩软件不能直接打开。
下面是使用java代码来对该zip文件进行加密解密原理相同,
1. 导入所需的依赖
首先,我们需要导入以下依赖,以使用SCrypt和AES算法相关的类和方法。
import org.bouncycastle.crypto.generators.SCrypt;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
2. 解密zip压缩包
下面是解密zip压缩包的方法实现。
public static void decryptFile(MultipartFile sourceEncryptedFile, String targetDecryptedPath, String password) {
try {
// scrypt参数
byte[] salt = "salt".getBytes(StandardCharsets.UTF_8);
int costParameter = 16384; // CPU cost parameter (N)
int blockSize = 8; // Block size parameter (r)
int parallelizationParameter = 1; // Parallelization parameter (p)
int keyLength = 32; // 密钥长度
// 使用scrypt生成密钥
byte[] keyBytes = SCrypt.generate(password.getBytes(StandardCharsets.UTF_8), salt, costParameter, blockSize, parallelizationParameter, keyLength);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
// 初始化向量
byte[] iv = new byte[16]; // 默认全部是0
// 将 MultipartFile 转换为临时文件
File sourceEncryptedTempFile = File.createTempFile("source_encrypted", ".zip");
sourceEncryptedFile.transferTo(sourceEncryptedTempFile);
// 解密过程
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(iv));
try (FileInputStream fileInputStream = new FileInputStream(sourceEncryptedTempFile);
FileOutputStream fileOutputStream = new FileOutputStream(targetDecryptedPath)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fileInputStream.read(buffer)) != -1) {
fileOutputStream.write(cipher.update(buffer, 0, bytesRead));
}
fileOutputStream.write(cipher.doFinal());
}
// 删除临时文件
sourceEncryptedTempFile.delete();
} catch (Exception e) {
e.printStackTrace();
}
}
在解密方法中,我们首先定义了scrypt参数,包括salt、costParameter、blockSize、parallelizationParameter和keyLength。使用这些参数,我们通过调用SCrypt.generate
方法生成加密密钥。
然后,我们初始化向量iv
为16个字节的0。接下来,我们将MultipartFile类型的加密文件转换为临时文件,并将其传输到临时文件中。
在解密过程中,我们使用AES算法的CBC模式和PKCS5Padding填充方式创建一个Cipher对象。通过调用cipher.init
方法,我们将Cipher对象设置为解密模式,并传入加密密钥和初始化向量。
随后,我们使用FileInputStream读取加密文件,将解密后的数据写入到目标解密路径的FileOutputStream中。最后,我们通过调用cipher.doFinal
方法来处理最后的数据块。
完成解密操作后,我们删除临时文件。
3. 加密zip压缩包
下面是加密zip压缩包的方法实现。
public static void encryptFile(String sourceFilePath, String targetEncryptedPath, String password, HttpServletResponse response) {
try {
// scrypt参数
byte[] salt = "salt".getBytes(StandardCharsets.UTF_8);
int costParameter = 16384; // CPU cost parameter (N)
int blockSize = 8; // Block size parameter (r)
int parallelizationParameter = 1; // Parallelization parameter (p)
int keyLength = 32; // 密钥长度
// 使用scrypt生成密钥
byte[] keyBytes = SCrypt.generate(password.getBytes(StandardCharsets.UTF_8), salt, costParameter, blockSize, parallelizationParameter, keyLength);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
// 初始化向量
byte[] iv = new byte[16]; // 默认全部是0
// 加密过程
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(iv));
// 设置响应头
response.setContentType("application/zip"); // 定义输出类型
response.setHeader("Access-Control-Allow-Origin", "*");
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Disposition", "attachment; filename=encrypted.zip");
// 获取输出流
ServletOutputStream out = response.getOutputStream();
try (FileInputStream fileInputStream = new FileInputStream(sourceFilePath)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fileInputStream.read(buffer)) != -1) {
byte[] encryptedBytes = cipher.update(buffer, 0, bytesRead);
out.write(encryptedBytes);
}
byte[] finalEncryptedBytes = cipher.doFinal();
out.write(finalEncryptedBytes);
}
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
在加密方法中,我们同样使用相同的scrypt参数和密钥生成方式来生成加密密钥。
然后,我们初始化向量iv
为16个字节的0。接下来,我们使用AES算法的CBC模式和PKCS5Padding填充方式创建一个Cipher对象。通过调用cipher.init
方法,我们将Cipher对象设置为加密模式,并传入加密密钥和初始化向量。
随后,我们设置响应头信息,包括内容类型、允许跨域访问、字符编码和附件文件名。
通过获取ServletOutputStream对象,我们可以将加密后的数据直接写入到HTTP响应中。
在加密过程中,我们使用FileInputStream读取源文件,并将加密后的数据通过调用out.write
方法写入到输出流中。最后,我们通过调用cipher.doFinal
方法来处理最后的数据块。
完成加密操作后,我们刷新输出流并关闭它。
4. 使用示例
下面是使用示例代码,演示了如何调用加密和解密方法。
MultipartFile sourceEncryptedFile = ...; // 从请求中获取加密文件
String targetDecryptedPath = ...; // 目标解密路径
String password = ...; // 密码
decryptFile(sourceEncryptedFile, targetDecryptedPath, password); // 解密操作
String sourceFilePath = ...; // 源文件路径
String targetEncryptedPath = ...; // 目标加密路径
String password = ...; // 密码
HttpServletResponse response = ...; // HTTP响应对象
encryptFile(sourceFilePath, targetEncryptedPath, password, response); // 加密操作