You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

JSP网站文件上传AES加密及用户本地解密实现咨询

我来帮你梳理一下整个实现流程,从后端上传加密到用户本地解密,一步一步来:

实现JSP网站文件AES加密上传与本地解密方案

一、后端上传时的AES加密逻辑(JSP/Servlet环境)

首先咱们完善AES加密工具类,优先用AES-GCM模式——它自带完整性校验,比传统CBC模式更安全。同时要实现随机salt、iv生成,以及从密码推导密钥的逻辑,再结合Servlet处理文件上传流程。

1. 完整的AES文件加密工具类

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.security.SecureRandom;
import java.security.spec.KeySpec;
import java.util.Base64;

public class AESFileEncryption {
    // AES-GCM核心参数:256位密钥,128位校验标签
    private static final int AES_KEY_SIZE = 256;
    private static final int GCM_TAG_LENGTH = 128;
    // PBKDF2密钥推导参数:迭代次数、salt长度
    private static final int PBKDF2_ITERATIONS = 65536;
    private static final int SALT_LENGTH = 16;
    // GCM推荐IV长度(96位=12字节)
    private static final int IV_LENGTH = 12;

    // 生成随机salt
    public static byte[] generateSalt() {
        byte[] salt = new byte[SALT_LENGTH];
        new SecureRandom().nextBytes(salt);
        return salt;
    }

    // 生成随机IV
    public static byte[] generateIV() {
        byte[] iv = new byte[IV_LENGTH];
        new SecureRandom().nextBytes(iv);
        return iv;
    }

    // 从密码+salt推导AES密钥(用PBKDF2增强安全性)
    public static SecretKey generateKey(String password, byte[] salt) throws Exception {
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, PBKDF2_ITERATIONS, AES_KEY_SIZE);
        SecretKey tmp = factory.generateSecret(spec);
        return new SecretKeySpec(tmp.getEncoded(), "AES");
    }

    // 加密文件:输入原文件路径、输出加密文件路径、密码、salt、iv
    public static void encryptFile(String inputFilePath, String outputFilePath, String password, byte[] salt, byte[] iv) throws Exception {
        SecretKey key = generateKey(password, salt);
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
        cipher.init(Cipher.ENCRYPT_MODE, key, spec);

        try (FileInputStream in = new FileInputStream(inputFilePath);
             FileOutputStream out = new FileOutputStream(outputFilePath)) {
            // 分批读取原文件并加密写入
            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                byte[] encrypted = cipher.update(buffer, 0, bytesRead);
                if (encrypted != null) out.write(encrypted);
            }
            // 写入GCM校验标签
            byte[] tag = cipher.doFinal();
            out.write(tag);
        }
    }
}

2. 处理文件上传的Servlet

这个Servlet负责接收用户上传的文件、生成加密所需的密码/salt/iv、加密文件后存储,最后把密钥信息发邮件给用户:

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.File;
import java.io.IOException;
import java.util.Base64;
import java.util.UUID;

@WebServlet("/upload")
@MultipartConfig(fileSizeThreshold = 1024 * 1024 * 2, // 2MB缓存阈值
                 maxFileSize = 1024 * 1024 * 50,      // 单文件最大50MB
                 maxRequestSize = 1024 * 1024 * 100)   // 请求总大小最大100MB
public class FileUploadServlet extends HttpServlet {
    // 服务器存储加密文件的目录(需确保有读写权限)
    private static final String UPLOAD_DIR = "encrypted_files";

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String uploadPath = getServletContext().getRealPath("") + File.separator + UPLOAD_DIR;
        File uploadDir = new File(uploadPath);
        if (!uploadDir.exists()) uploadDir.mkdir();

        try {
            // 获取上传文件和用户邮箱
            Part filePart = request.getPart("file");
            String fileName = filePart.getSubmittedFileName();
            String userEmail = request.getParameter("email");

            // 临时存储原文件
            String tempFilePath = uploadPath + File.separator + "temp_" + fileName;
            filePart.write(tempFilePath);

            // 生成加密所需的随机信息
            String password = UUID.randomUUID().toString().substring(0, 8); // 生成8位随机密码
            byte[] salt = AESFileEncryption.generateSalt();
            byte[] iv = AESFileEncryption.generateIV();

            // 生成加密后文件名(避免重复)
            String encryptedFileName = UUID.randomUUID().toString() + "_encrypted";
            String encryptedFilePath = uploadPath + File.separator + encryptedFileName;

            // 执行加密并删除临时原文件
            AESFileEncryption.encryptFile(tempFilePath, encryptedFilePath, password, salt, iv);
            new File(tempFilePath).delete();

            // 发送加密信息到用户邮箱
            sendEncryptionEmail(userEmail, password, Base64.getEncoder().encodeToString(salt), Base64.getEncoder().encodeToString(iv), encryptedFileName);

            // 返回成功提示
            response.getWriter().println("文件上传加密成功!密码、Salt和IV已发送至你的邮箱,请妥善保存!");

        } catch (Exception e) {
            e.printStackTrace();
            response.getWriter().println("上传加密失败:" + e.getMessage());
        }
    }

    // 邮件发送方法(需配置你的SMTP服务器信息)
    private void sendEncryptionEmail(String toEmail, String password, String saltBase64, String ivBase64, String encryptedFileName) throws Exception {
        String fromEmail = "你的邮箱@example.com";
        String emailPwd = "你的邮箱授权码";
        String smtpHost = "smtp.example.com";
        int smtpPort = 587;

        javax.mail.Session session = javax.mail.Session.getInstance(System.getProperties(),
                new javax.mail.Authenticator() {
                    protected javax.mail.PasswordAuthentication getPasswordAuthentication() {
                        return new javax.mail.PasswordAuthentication(fromEmail, emailPwd);
                    }
                });

        try (javax.mail.Message message = new javax.mail.internet.MimeMessage(session)) {
            message.setFrom(new javax.mail.internet.InternetAddress(fromEmail));
            message.setRecipients(javax.mail.Message.RecipientType.TO, javax.mail.internet.InternetAddress.parse(toEmail));
            message.setSubject("你的文件加密密钥信息");
            String content = "您好,你的文件已完成加密上传!\n\n" +
                    "解密所需信息:\n" +
                    "密码:" + password + "\n" +
                    "Salt(Base64格式):" + saltBase64 + "\n" +
                    "IV(Base64格式):" + ivBase64 + "\n" +
                    "加密文件下载链接:http://你的域名.com/download?file=" + encryptedFileName;
            message.setText(content);
            javax.mail.Transport.send(message);
        }
    }
}

3. 上传表单JSP页面

写一个简单的前端页面让用户上传文件:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>文件加密上传</title>
</head>
<body>
    <h2>上传文件并加密</h2>
    <form action="upload" method="post" enctype="multipart/form-data">
        <div>
            <label>接收密钥的邮箱:</label>
            <input type="email" name="email" required>
        </div>
        <div style="margin: 15px 0;">
            <label>选择要上传的文件:</label>
            <input type="file" name="file" required>
        </div>
        <button type="submit">上传并加密</button>
    </form>
</body>
</html>

二、用户本地的AES解密逻辑

用户下载加密文件后,需要用本地工具解密。咱们写一个Java类,用户可以直接运行(或打包成jar包),输入邮件里的密钥信息即可解密。

本地解密工具类

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.security.spec.KeySpec;
import java.util.Base64;

public class AESFileDecryption {
    private static final int AES_KEY_SIZE = 256;
    private static final int GCM_TAG_LENGTH = 128;
    private static final int PBKDF2_ITERATIONS = 65536;

    // 从密码+Base64格式的salt推导密钥
    public static SecretKey generateKey(String password, String saltBase64) throws Exception {
        byte[] salt = Base64.getDecoder().decode(saltBase64);
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, PBKDF2_ITERATIONS, AES_KEY_SIZE);
        SecretKey tmp = factory.generateSecret(spec);
        return new SecretKeySpec(tmp.getEncoded(), "AES");
    }

    // 解密文件:输入加密文件路径、输出原文件路径、密码、saltBase64、ivBase64
    public static void decryptFile(String encryptedFilePath, String outputFilePath, String password, String saltBase64, String ivBase64) throws Exception {
        SecretKey key = generateKey(password, saltBase64);
        byte[] iv = Base64.getDecoder().decode(ivBase64);
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
        cipher.init(Cipher.DECRYPT_MODE, key, spec);

        try (FileInputStream in = new FileInputStream(encryptedFilePath);
             FileOutputStream out = new FileOutputStream(outputFilePath)) {
            byte[] encryptedData = in.readAllBytes();
            byte[] decryptedData = cipher.doFinal(encryptedData);
            out.write(decryptedData);
        }
    }

    // 命令行运行入口:java AESFileDecryption <加密文件路径> <输出文件路径> <密码> <Salt(Base64)> <IV(Base64)>
    public static void main(String[] args) {
        if (args.length != 5) {
            System.out.println("使用方法:java AESFileDecryption <加密文件路径> <输出文件路径> <密码> <Salt(Base64)> <IV(Base64)>");
            return;
        }
        try {
            decryptFile(args[0], args[1], args[2], args[3], args[4]);
            System.out.println("解密成功!原文件已保存至:" + args[1]);
        } catch (Exception e) {
            System.err.println("解密失败:" + e.getMessage());
            e.printStackTrace();
        }
    }
}

三、关键注意事项

  • 安全性
    • 永远不要在服务器存储密码、salt、iv,这些信息只发送给用户,确保只有用户能解密文件。
    • 密码要随机生成,建议长度不少于8位,包含混合字符。
    • 每次上传都要生成新的salt和iv,绝对不能固定。
  • 环境配置
    • JDK8及以下版本需要安装JCE无限强度权限政策文件,才能使用256位AES密钥;JDK9+默认支持。
    • 确保服务器上传目录有读写权限,加密文件做好备份。
  • 用户体验
    • 可以把解密工具打包成可执行jar包,用户无需安装JDK就能运行。
    • 邮件里附上解密工具下载链接和详细操作步骤。

内容的提问来源于stack exchange,提问作者Shubhu Gholu

火山引擎 最新活动