java AES文件流加密

java AES文件流加密

文章转载自:https://www.cnblogs.com/gne-hwz/p/14736496.html

java工具类-对称加密算法AES 加密文件流

工具类出自jdk中jce.jar

文件流加密涉及到大文件加密过程,不能直接使用Cipher.doFinal(byte[] bytes)方法进行直接加密,超大文件会导致内存溢出。

解决方法:

可以使用 Cipher.update(byte[] bytes) 方法进行文件流部分加密数据,当整个文件流数据都加密完后,使用 Cipher.doFinal()方法来生成填充内容,保证最后一段内容也是完整128位数据块

所以会使用CipherInputStream 或者 CipherOutputStream进行文件加解密,使用上面中了一个就可以了。或者也可以混着用

原理:
CipherInputStream :

对输入流进行封装
CipherInputStream.read()读取字节流时调用的cipher.update()方法进行流部分加密, 当加密到最后一段时,会调用 doFinal() 方法。

CipherOutputStream:

对输出流进行封装,当要写入固定字节数据时,先加密,再写出
CipherOutputStream.write() 中调用 cipher.update() 方法进行字节数组加密后写出
在CipherOutputStream.close()中调用cipher.doFinal()方法 填充最后一段内容

示例代码:
public static void aesEncryptFile(String sourceFilePath, String destFilePath, String key) throws Exception {
        aesFile(sourceFilePath, destFilePath, key, Cipher.ENCRYPT_MODE);
    }
    public static void aesDecryptFile(String sourceFilePath, String destFilePath, String key) throws Exception {
        aesFile(sourceFilePath, destFilePath, key, Cipher.DECRYPT_MODE);
    }

    public static void aesEncryptFileForInput(String sourceFilePath, String destFilePath, String key) throws Exception {
        aesFileForInput(sourceFilePath, destFilePath, key, Cipher.ENCRYPT_MODE);
    }
    public static void aesDecryptFileForInput(String sourceFilePath, String destFilePath, String key) throws Exception {
        aesFileForInput(sourceFilePath, destFilePath, key, Cipher.DECRYPT_MODE);
    }

    /**
     * 通过文件输入流加密文件并输出到指定路径
     * CipherOutputStream进行加密数据
     */
    public static void aesFile(String sourceFilePath, String destFilePath, String key, int mode) throws Exception {
        File sourceFile = new File(sourceFilePath);
        File destFile = new File(destFilePath);
        if (sourceFile.exists() && sourceFile.isFile()) {
            throw new IllegalArgumentException("加密源文件不存在");
        }
        if (!destFile.getParentFile().exists()) {
            destFile.getParentFile().mkdirs();
        }
        destFile.createNewFile();
        InputStream in = new FileInputStream(sourceFile);
        OutputStream out = new FileOutputStream(destFile);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES/ECB/PKCS5Padding");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(mode, secretKeySpec);
        // 对输出流包装
        CipherOutputStream cout = new CipherOutputStream(out, cipher);
        byte[] cache = new byte[1024];
        int nRead = 0;
        while ((nRead = in.read(cache)) != -1) {
            cout.write(cache, 0, nRead);
            cout.flush();
        }
        cout.close();
        out.close();
        in.close();
    }

    /**
     * 通过文件输入流加密文件并输出到指定路径
     * CipherInputStream进行加密数据
     */
    public static void aesFileForInput(String sourceFilePath, String destFilePath, String key, int mode) throws Exception {
        File sourceFile = new File(sourceFilePath);
        File destFile = new File(destFilePath);
        if (sourceFile.exists() && sourceFile.isFile()) {
            throw new IllegalArgumentException("加密源文件不存在");
        }
        if (!destFile.getParentFile().exists()) {
            destFile.getParentFile().mkdirs();
        }
        destFile.createNewFile();
        InputStream in = new FileInputStream(sourceFile);
        OutputStream out = new FileOutputStream(destFile);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES/ECB/PKCS5Padding");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(mode, secretKeySpec);
        // 对输入流包装
        CipherInputStream cin = new CipherInputStream(in, cipher);

        byte[] cache = new byte[1024];
        int nRead = 0;
        while ((nRead = cin.read(cache)) != -1) {
            out.write(cache, 0, nRead);
            out.flush();
        }
        out.close();
        cin.close();
        in.close();
    }

	/**
	 * AES加密方法,此处使用AES-128-ECB加密模式,key需要为16位
	 * @param sKey
	 * @return
	 */
	public static void encryptStream(ByteArrayOutputStream source, OutputStream out, String sKey) {
		long start = System.currentTimeMillis();
		try {
			// 判断Key是否正确
			if (sKey == null || sKey.length() != CommonIntConst.INT_16) {
				throw new RuntimeException("Key为空或长度不为16位");
			}
			byte[] raw = sKey.getBytes("utf-8");
			SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
			// "算法/模式/补码方式"
			Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
			cipher.init(Cipher.ENCRYPT_MODE, skeySpec);

			CipherOutputStream cout = new CipherOutputStream(out, cipher);
			cout.write(source.toByteArray());
			cout.close();
			source.close();
		} catch (Exception e) {
			LOG.error("AES加密失败", e);
		}
	}

	/**
	 * AES解密方法,此处使用AES-128-ECB加密模式,key需要为16位
	 * @param sKey
	 * @return
	 */
	public static void decryptStream(InputStream in, OutputStream out, String sKey) {

		long start = System.currentTimeMillis();
		try {
			// 判断Key是否正确
			if (sKey == null || sKey.length() != CommonIntConst.INT_16) {
				throw new RuntimeException("Key为空或长度不为16位");
			}
			byte[] raw = sKey.getBytes("utf-8");
			SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
			Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
			cipher.init(Cipher.DECRYPT_MODE, skeySpec);
			// 先用base64解密
			LOG.info("AES解密耗时:" + (System.currentTimeMillis() - start) + "ms");

			CipherOutputStream cout = new CipherOutputStream(out, cipher);
			byte[] cache = new byte[1024];
			int nRead = 0;
			while ((nRead = in.read(cache)) != -1) {
				cout.write(cache, 0, nRead);
				cout.flush();
			}
			cout.close();
			in.close();
		} catch (Exception e) {
			LOG.error("AES解密失败", e);
		}
	}

你可能感兴趣的:(#,java工具类,java,开发语言,后端)