You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java实现AES加密图片后无法查看问题求助

解决AES加密图片后无法打开的问题

嘿,作为编程新手能想到保留图片头部来让加密后的文件仍能被识别,这个思路真的很赞!不过你遇到的无法打开的问题,主要有几个关键的小问题,咱们一个个来解决:

问题分析

  • 硬编码的头部长度不准确:你固定截取了55字节作为JPEG头部,但JPEG的头部长度并不是固定的——它是从文件开头一直到0xFFDA(扫描起始标记)的位置。硬写55字节要么截少了(头部不完整),要么截多了(把部分图片内容算进头部,导致加密的内容缺失),这都会让图片无法被正常识别。
  • 流模式加密错误使用了填充:CFB是AES的流加密模式,这类模式不需要PKCS5Padding,你用AES/CFB/PKCS5Padding会额外添加填充字节,导致加密后的内容长度变长,破坏了图片文件的结构。
  • 文件输出使用了追加模式:FileOutputStream的第二个参数传true会以追加模式写入,如果多次运行代码,文件里会累积多份加密内容,直接导致文件损坏。
  • 文件读取不完整:fileInputStream.read(bFile)不能保证一次性读取完整个文件的所有字节,尤其是大图片,会导致部分原始内容丢失。

修正后的代码

下面是调整后的代码,我标注了关键修改点:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class ImageEncryptor {
    private static SecretKeySpec secretKey;
    private static byte[] key;
    static String encodekey = "770A8A65DA156D24EE2A093277530142";
    static String IV = "AAAAAAAAAAAAAAAA";

    public static void main(String[] args) {
        encrypt(new File("/Users/uni/IdeaProjects/ExtendedExperimentalAPI/src/image.jpeg"));
    }

    public static void encrypt(File file) {
        setKey(encodekey);
        String fileName = file.getName();
        byte[] fileBytes = fileToBytes(file);
        if (fileBytes == null || fileBytes.length == 0) {
            System.out.println("读取文件失败");
            return;
        }

        // 动态获取JPEG头部到0xFFDA标记的位置
        int headerEndIndex = findJpegHeaderEnd(fileBytes);
        if (headerEndIndex == -1) {
            System.out.println("不是有效的JPEG文件");
            return;
        }
        byte[] header = Arrays.copyOfRange(fileBytes, 0, headerEndIndex);
        byte[] toEncrypt = Arrays.copyOfRange(fileBytes, headerEndIndex, fileBytes.length);

        Cipher cipher;
        try {
            // ECB模式加密
            cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            byte[] cipherDataECB = cipher.doFinal(toEncrypt);
            // 去掉append模式,覆盖写入
            try (FileOutputStream fos = new FileOutputStream("ECB_" + fileName)) {
                fos.write(header);
                fos.write(cipherDataECB);
            }

            // CBC模式加密
            cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(IV.getBytes("UTF-8")));
            byte[] cipherDataCBC = cipher.doFinal(toEncrypt);
            try (FileOutputStream fos = new FileOutputStream("CBC_" + fileName)) {
                fos.write(header);
                fos.write(cipherDataCBC);
            }

            // CFB模式使用NoPadding
            cipher = Cipher.getInstance("AES/CFB/NoPadding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(IV.getBytes("UTF-8")));
            byte[] cipherDataCFB = cipher.doFinal(toEncrypt);
            try (FileOutputStream fos = new FileOutputStream("CFB_" + fileName)) {
                fos.write(header);
                fos.write(cipherDataCFB);
            }

        } catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException | NoSuchPaddingException
                 | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | IOException e) {
            e.printStackTrace();
        }
    }

    // 查找JPEG头部结束位置(0xFFDA标记)
    private static int findJpegHeaderEnd(byte[] fileBytes) {
        for (int i = 0; i < fileBytes.length - 1; i++) {
            if (fileBytes[i] == (byte) 0xFF && fileBytes[i + 1] == (byte) 0xDA) {
                return i + 2; // 返回标记之后的位置,作为内容起始点
            }
        }
        return -1;
    }

    static void setKey(String myKey) {
        MessageDigest sha = null;
        try {
            key = myKey.getBytes("UTF-8");
            sha = MessageDigest.getInstance("SHA-1");
            key = sha.digest(key);
            key = Arrays.copyOf(key, 16); // 取前128位作为AES密钥
            secretKey = new SecretKeySpec(key, "AES");
        } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    // 确保文件完整读取
    static byte[] fileToBytes(File file) {
        try (FileInputStream fileInputStream = new FileInputStream(file)) {
            byte[] bFile = new byte[(int) file.length()];
            int bytesRead;
            int totalRead = 0;
            while ((bytesRead = fileInputStream.read(bFile, totalRead, bFile.length - totalRead)) != -1) {
                totalRead += bytesRead;
            }
            return bFile;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
}

关键修改说明

  1. 动态识别JPEG头部:新增findJpegHeaderEnd方法,找到JPEG文件的0xFFDA标记,确保头部截取准确,这是图片能被识别的核心。
  2. 修正CFB模式的填充:把AES/CFB/PKCS5Padding改成AES/CFB/NoPadding,避免额外填充字节破坏文件结构。
  3. 文件输出改为覆盖模式:去掉FileOutputStream的第二个参数true,每次运行都会生成干净的加密文件。
  4. 完善文件读取逻辑:用循环读取确保整个文件的字节都被读取到,避免内容丢失。

现在运行修改后的代码,加密后的图片应该能被识别(虽然内容是加密后的乱码,但至少能被图片查看器打开显示损坏的内容,这说明头部是正常的)。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:42:43