Java文件压缩&加解密

435 阅读3分钟

Android文件压缩、解压 & 加解密,对于客户端端来说有时对于一些敏感信息,还是有必要进行一下安全防护滴。东西很简单,纯粹是简单记录一下,方便自己使用O(∩_∩)O哈哈~

文件压缩代码:

public static void main(String[] args) {
        try {
//             zipCompress("D:\java_projects\test", "D:\java_projects\test.zip");
//             zipUnCompress("D:\java_projects\test.zip", "D:\java_projects\test2");
//            FileCryptoUtil.encryptFile(new FileInputStream("D:\java_projects\test.zip"),
//                    new FileOutputStream("D:\java_projects\test_encrypt.zip"),"8765432187654321");
            FileCryptoUtil.decryptedFile(new FileInputStream("D:\java_projects\test_encrypt.zip"),
                    new FileOutputStream("D:\java_projects\test2.zip"), "8765432187654321");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    public static void zipCompress(String input, String output) throws IOException {
        // 创建zip输出流
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(output));
        // 创建缓冲输出流
        BufferedOutputStream bos = new BufferedOutputStream(zos);
        String[] paths = input.split(",");
        for (String path : paths) {
            File file = new File(path);
            compress(zos, bos, file, null);
        }
        bos.close();
    }

    public static void compress(ZipOutputStream zos,
                                BufferedOutputStream bos,
                                File input,
                                String name) throws IOException {
        if (name == null) {
            // 如果未指定压缩包内的文件(夹)名称,默认使用文件(夹)名
            name = input.getName();
        }
        if (input.isDirectory()) {
            // 当前读取到的是文件夹,获取文件夹下的文件
            File[] listFiles = input.listFiles();
            if (listFiles == null || listFiles.length == 0) {
                // 空文件夹
                zos.putNextEntry(new ZipEntry(name + File.separator));
            } else {
                // 递归写入当前文件夹下的文件
                for (File file : listFiles) {
                    compress(zos, bos, file, name + File.separator + file.getName());
                }
            }
        } else {
            // 当前读取到的是文件,读取文件输入流,写入输出流
            zos.putNextEntry(new ZipEntry(name));
            FileInputStream fis = new FileInputStream(input);
            BufferedInputStream bis = new BufferedInputStream(fis);

            byte[] bytes = new byte[1024];
            int len;
            while ((len = bis.read(bytes)) != -1) {
                bos.write(bytes, 0, len);
            }
            bis.close();
            // 写入完当前文件,立刻flush!! 否则会出现文件内容错位,刷新到到其他文件的bug。
            bos.flush();
        }
    }

    public static void zipUnCompress(String inputZip, String destFilePath) throws IOException {
        File srcZip = new File(inputZip);
        if (!srcZip.exists()) {
            throw new FileNotFoundException("未找到压缩包 " + inputZip);
        }
        try (ZipInputStream zis = new ZipInputStream(new FileInputStream(srcZip))) {
            ZipEntry zipEntry;
            while ((zipEntry = zis.getNextEntry()) != null) {
                if (!zipEntry.isDirectory()) {
                    File file = new File(destFilePath, zipEntry.getName());
                    if (!file.exists()) {
                        // 创建此文件的上级目录
                        boolean parDir = new File(file.getParent()).mkdirs();
                    }
                    try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file))) {
                        byte[] bytes = new byte[1024];
                        int len;
                        while ((len = zis.read(bytes)) != -1) {
                            bos.write(bytes, 0, len);
                        }
                    }
                }
            }
        }
    }

文件加解密代码:

private static final int IV_LENGTH = 16;
private static final int KEY_LENGTH = 16;
private static final int KEY_HASH_LENGTH = 32;


/**
 * 文件加密
 *
 * @param fis    原始文件读取流
 * @param fos    加密文件输出流
 * @param encKey 加密密钥
 * @throws IOException
 * @throws InvalidKeyException
 * @throws NoSuchAlgorithmException
 * @throws NoSuchPaddingException
 * @throws InvalidAlgorithmParameterException
 */
public static void encryptFile(FileInputStream fis, FileOutputStream fos, String encKey) throws IOException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException {
    // 获取16位加密密钥
    byte[] encKeyBytes = getEncKeyBytes(encKey);
    // 记录输入的加密密码的消息摘要,32位
    final byte[] encKeySha256 = sha256(encKeyBytes);
    fos.write(encKeySha256);
    // 获取系统时间作为IV
    byte[] ivBytes = getRandomIv();
    // 记录IV,16位
    fos.write(ivBytes);
    // 获取加密算法
    Cipher cipher = getCipher(encKeyBytes, ivBytes, Cipher.ENCRYPT_MODE);
    // 构造加密流并输出
    try (CipherInputStream cis = new CipherInputStream(fis, cipher)) {
        byte[] buffer = new byte[1024];
        int n;
        while ((n = cis.read(buffer)) != -1) {
            fos.write(buffer, 0, n);
        }
    }
}

/**
 * 文件解密
 *
 * @param fis    加密文件输入流
 * @param fos    解密文件输出流
 * @param encKey 解密密钥
 * @throws IOException
 * @throws InvalidKeyException
 * @throws NoSuchAlgorithmException
 * @throws NoSuchPaddingException
 * @throws InvalidAlgorithmParameterException
 */
public static void decryptedFile(FileInputStream fis, FileOutputStream fos, String encKey) throws IOException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException {
    final byte[] encKeyBytes = getEncKeyBytes(encKey);
    byte[] encKeySha256 = new byte[KEY_HASH_LENGTH];
    // 读记录的文件加密密码的消息摘要,并判断是否匹配
    if (fis.read(encKeySha256) != KEY_HASH_LENGTH || !Arrays.equals(sha256(encKeyBytes), encKeySha256)) {
        throw new IllegalArgumentException("解密失败,解密密钥不匹配");
    }
    // 获取IV值
    byte[] ivBytes = new byte[IV_LENGTH];
    final int read = fis.read(ivBytes);
    if (read != IV_LENGTH) {
        throw new IllegalArgumentException("读取IV向量失败,长度不够");
    }
    // 获取解密算法
    Cipher cipher = getCipher(encKeyBytes, ivBytes, Cipher.DECRYPT_MODE);
    // 构造解密流并输出
    try (CipherInputStream cis = new CipherInputStream(fis, cipher)) {
        byte[] buffer = new byte[1024];
        int n;
        while ((n = cis.read(buffer)) != -1) {
            fos.write(buffer, 0, n);
        }
    }
}

/**
 * 获取系统时间作为IV
 *
 * @return
 */
private static byte[] getRandomIv() {
    byte[] ivBytes = new byte[IV_LENGTH];
    Random random = new Random(System.currentTimeMillis());
    random.nextBytes(ivBytes);
    return ivBytes;
}

/**
 * 提取16位加密密钥
 * @param encKey 加密密钥,长度不能小于16,加解密时要一致
 * @return
 */
private static byte[] getEncKeyBytes(String encKey) {
    if (encKey == null || encKey.length() < KEY_LENGTH) {
        throw new IllegalArgumentException("encKey illegal");
    }
    return encKey.substring(0, KEY_LENGTH).getBytes(StandardCharsets.UTF_8);
}

/**
 * 构造加密/解密算法
 * <p>
 * AES/CFB/PKCS5Padding 密码反馈模式
 * @param encKeyBytes 加密密钥
 * @param ivBytes     加密向量
 * @param encryptMode 加密/解密
 * @return
 * @throws NoSuchAlgorithmException
 * @throws NoSuchPaddingException
 * @throws InvalidKeyException
 * @throws InvalidAlgorithmParameterException
 */
private static Cipher getCipher(byte[] encKeyBytes, byte[] ivBytes, int encryptMode) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
    Cipher cipher = Cipher.getInstance("AES/CFB/PKCS5Padding");
    SecretKeySpec secretKeySpec = new SecretKeySpec(encKeyBytes, "AES");
    IvParameterSpec iv = new IvParameterSpec(ivBytes);
    cipher.init(encryptMode, secretKeySpec, iv);
    return cipher;
}

/**
 * sha256摘要算法
 *
 * @param bytes 摘要原文
 * @return 摘要结果
 * @throws NoSuchAlgorithmException
 */
private static byte[] sha256(byte[] bytes) throws NoSuchAlgorithmException {
    MessageDigest digest = MessageDigest.getInstance("SHA-256");
    return digest.digest(bytes);
}