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; } } }
关键修改说明
- 动态识别JPEG头部:新增
findJpegHeaderEnd方法,找到JPEG文件的0xFFDA标记,确保头部截取准确,这是图片能被识别的核心。 - 修正CFB模式的填充:把
AES/CFB/PKCS5Padding改成AES/CFB/NoPadding,避免额外填充字节破坏文件结构。 - 文件输出改为覆盖模式:去掉
FileOutputStream的第二个参数true,每次运行都会生成干净的加密文件。 - 完善文件读取逻辑:用循环读取确保整个文件的字节都被读取到,避免内容丢失。
现在运行修改后的代码,加密后的图片应该能被识别(虽然内容是加密后的乱码,但至少能被图片查看器打开显示损坏的内容,这说明头部是正常的)。
内容的提问来源于stack exchange,提问作者ag1nz
相关产品推荐
相关产品推荐

