Java实现文件内容加解密

背景

近期在做一个对数据安全要求比较高的软件,用户要求做到对接口、文件、以及数据库部分敏感字段进行加密。由于系统中文件内容比较敏感,用户要求除了客户其他人不能查看文件具体内容,包括运维人员和开发人员。

探讨

其实文件加密并不算太复杂。无非就是在用户上传文件的时候将文件内容读出加密写入后再存到服务器,然后用户下载的时候将内容读出然后解密再写入输出流即可。

简单实现

计算机数据内容是二进制,针对二进制最简单高效的修改是进行位运算,考虑加密以及解密,可以使用异或(^)计算,具体代码如下:

public static void main(String[] args) throws Exception{
	// 加密解密秘钥
    private static final int secretKey = 0xff; 
    // 文件内容加密
    try (FileInputStream in = new FileInputStream("加密原文件.md");
         FileOutputStream out = new FileOutputStream("加密后文件.md")){
        int dataByte;
        while ((dataByte = in.read()) != -1){
            out.write(dataByte ^ secretKey);
        }
    }
    // 文件内容解密
    try (FileInputStream in = new FileInputStream("加密后文件.md");
         FileOutputStream out = new FileOutputStream("解密后文件.md")){
        int dataByte;
        while ((dataByte = in.read()) != -1){
            out.write(dataByte ^ secretKey);
        }
    }
}

JDK文件加解密

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

Java已经帮我们解决了这一问题,通过文件加密流CipherInputStreamCipherOutputStream,使用这两个加密流我们可以实现使用不同的加密算法对文件加解密。

通过CipherInputStream加解密文件

CipherInputStream加密流是对Cipher和InputStream的包装,它在读取流的时候进行加密或者解密。

    public static void encryptFile(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");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(mode, secretKeySpec);
        // 对输入流包装
        CipherInputStream cin = new CipherInputStream(in, cipher);
        byte[] dataByte = new byte[1024];
        int read;
        while ((read= cin.read(dataByte)) != -1) {
            out.write(dataByte, 0, read);
            out.flush();
        }
        out.close();
        cin.close();
        in.close();
    }

通过CipherOutputStream加解密文件

    public static void encryptFile(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");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(mode, secretKeySpec);
        // 对输出流包装
        CipherOutputStream cout = new CipherOutputStream(out, cipher);
        byte[] dataByte = new byte[1024];
        int read;
        while ((read = in.read(dataByte)) != -1) {
            cout.write(dataByte, 0, read);
            cout.flush();
        }
        cout.close();
        out.close();
        in.close();
    }

CipherInputStream加密流是对Cipher和OutputStream的包装,它在write() 方法在将数据写出到基础 OutputStream 之前先对该数据进行加密或解密。

文件加解密工具类实现

public class FileCryptoUtil {

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

    private FileCryptoUtil() {
    }

    /**
     * 文件加密
     *
     * @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);
    }
}

参考

https://github.com/corningsun/fileEncrypt
https://www.cnblogs.com/gne-hwz/p/14736496.html

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
文件加密解密算法(Java源码) java,file,算法,加密解密,java源码 package com.crypto.encrypt; import java.security.SecureRandom; import java.io.*; import javax.crypto.spec.DESKeySpec; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.Cipher; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import javax.crypto.NoSuchPaddingException; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; import java.lang.reflect.Constructor; import java.security.spec.KeySpec; import java.lang.reflect.InvocationTargetException; public class EncryptData { private String keyfile=null; public EncryptData() { } public EncryptData(String keyfile) { this.keyfile=keyfile; } /** * 加密文件 * @param filename String 源路径 * @param filenamekey String 加密后的路径 */ public void createEncryptData(String filename,String filenamekey) throws IllegalStateException, IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException, InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException, IOException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException, IllegalStateException, IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException, InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException, IOException { //验证keyfile if(keyfile==null || keyfile.equals("")) { throw new NullPointerException("无效的key文件路径"); } encryptData(filename,filenamekey); } /** * 加密类文件 * @param filename String 原始的类文件 * @param encryptfile String 加密后的类文件 * @throws IOException * @throws InvalidKeyException * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException * @throws NoSuchPaddingException * @throws NoSuchAlgorithmException * @throws BadPaddingException * @throws IllegalBlockSizeException * @throws IllegalStateException */ private void encryptData(String filename,String encryptfile) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, IllegalStateException, ClassNotFoundException, SecurityException, NoSuchMethodException, InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException { byte data[]=Util.readFile(filename); // 执行加密操作 byte encryptedClassData[] = getencryptData(data); // 保存加密后的文件,覆盖原有的类文件。 Util.writeFile(encryptedClassData,encryptfile); } /** * 直接获得加密数据 * @param bytes byte[] * @throws IllegalStateException * @throws IllegalBlockSizeException * @throws BadPaddingException * @throws InvalidKeyException * @throws NoSuchPaddingException * @throws InvalidKeySpecException * @throws NoSuchAlgorithmException * @throws InstantiationException * @throws IllegalAccessException * @throws IllegalArgumentException * @throws InvocationTargetException * @throws NoSuchMethodException * @throws SecurityException * @throws ClassNotFoundException * @throws IOException * @return byte[] */ public byte[] createEncryptData(byte[] bytes) throws IllegalStateException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, NoSuchPaddingException, InvalidKeySpecException, NoSuchAlgorithmException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException, IOException { bytes=getencryptData(bytes); return bytes; } private byte[] getencryptData(byte[] bytes) throws IOException, ClassNotFoundException, SecurityException, NoSuchMethodException, InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, IllegalStateException { // 产生一个可信任的随机数源 SecureRandom sr = new SecureRandom(); //从密钥文件key Filename中得到密钥数据 byte[] rawKeyData = Util.readFile(keyfile); // 从原始密钥数据创建DESKeySpec对象 Class classkeyspec=Class.forName(Util.getValue("keyspec")); Constructor constructor = classkeyspec.getConstructor(new Class[]{byte[].class}); KeySpec dks = (KeySpec) constructor.newInstance(new Object[]{rawKeyData}); // 创建一个密钥工厂,然后用它把DESKeySpec转换成SecretKey对象 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(Util.getAlgorithm()); SecretKey key = keyFactory.generateSecret(dks); // Cipher对象实际完成加密操作 Cipher cipher = Cipher.getInstance(Util.getAlgorithm()); // 用密钥初始化Cipher对象 cipher.init(Cipher.ENCRYPT_MODE, key, sr); // 执行加密操作 bytes = cipher.doFinal(bytes); // 返回字节数组 return bytes; } /** * 设置key文件路径 * @param keyfile String */ public void setKeyFile(String keyfile) { this.keyfile=keyfile; } }
Java可以使用javax.crypto包中的Cipher类来实现DES算法的文件加解密。下面是一个简单的示例代码: ```java import javax.crypto.*; import javax.crypto.spec.DESKeySpec; import java.io.FileInputStream; import java.io.FileOutputStream; import java.security.Key; public class FileEncryptionExample { private static final String ALGORITHM = "DES"; public static void main(String[] args) { String key = "mysecretkey"; // 密钥 String inputFile = "input.txt"; // 输入文件 String encryptedFile = "encrypted.txt"; // 加密后的文件 String decryptedFile = "decrypted.txt"; // 解密后的文件 try { Key secretKey = generateKey(key); // 加密文件 encryptFile(inputFile, encryptedFile, secretKey); // 解密文件 decryptFile(encryptedFile, decryptedFile, secretKey); System.out.println("文件加解密完成!"); } catch (Exception e) { e.printStackTrace(); } } private static Key generateKey(String key) throws Exception { DESKeySpec keySpec = new DESKeySpec(key.getBytes()); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM); return keyFactory.generateSecret(keySpec); } private static void encryptFile(String inputFile, String outputFile, Key secretKey) throws Exception { Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, secretKey); FileInputStream inputStream = new FileInputStream(inputFile); FileOutputStream outputStream = new FileOutputStream(outputFile); CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher); byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { cipherOutputStream.write(buffer, 0, bytesRead); } cipherOutputStream.close(); inputStream.close(); } private static void decryptFile(String inputFile, String outputFile, Key secretKey) throws Exception { Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, secretKey); FileInputStream inputStream = new FileInputStream(inputFile); FileOutputStream outputStream = new FileOutputStream(outputFile); CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher); byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = cipherInputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } outputStream.close(); cipherInputStream.close(); } } ``` 在上面的示例中,首先定义了一个密钥字符串 `key`,输入文件路径 `inputFile`,加密后的文件路径 `encryptedFile`,解密后的文件路径 `decryptedFile`。 然后,通过 `generateKey` 方法生成密钥对象 `secretKey`。 接下来,使用 `Cipher` 类初始化加密/解密模式,并传入密钥对象。通过 `FileInputStream` 和 `FileOutputStream` 读取和写入文件数据。 最后,通过 `CipherOutputStream` 和 `CipherInputStream` 对数据进行加密和解密。 注意:上述示例中的密钥处理方式比较简单,实际应用中需要更加安全地生成和管理密钥。 请注意,这只是一个简单的示例,实际应用中还需要处理异常、文件不存在等情况,并进行适当的错误处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值