一个文件加密和解密的库开发历程

hello,大家好,这篇文章离我上一篇文章的发布过去还不够一个小时,我又来了。

最近又掏出了500块钱的机械键盘,继续开始我的码农生活。

文件加密解密,本质上就是把文件流读取出来,进行一系列的加密或者解密处理,然后把处理后的字节流重新写进文件,这样就生成加密或者解密后的文件。

首先,我们来看看这个库的结构:

image.png

1.最核心的部分,就是我们的各种加密解密的方式基本操作,如AES加密,DES加密,异或加密等等,当然还有非对称加密,不过暂时还未加上。

下面就拿个AES的帮助类(AESHelper)进行示例,其他的DES,异或加密一大堆,右转度娘。

public class AESHelper {
    /**
     * 偏移变量,固定占16位字节
     */
    private final static String IV_PARAMETER = "1234567812345678";
    private static final String CIPHER_ALGORITHM_CBC = "AES/CBC/PKCS5Padding";
    //ECB模式计算快,但是没有那么安全
    private static final String CIPHER_ALGORITHM_ECB = "AES/ECB/PKCS5Padding";
    /**
     * 默认编码
     */
    private static final String CHARSET = "utf-8";

    /**
     * 创建密钥
     **/
    private static SecretKeySpec createKey(String password) {
        byte[] data = null;
        if (password == null) {
            password = "";
        }
        StringBuffer sb = new StringBuffer(32);
        sb.append(password);
        while (sb.length() < 32) {
            sb.append("0");
        }
        if (sb.length() > 32) {
            sb.setLength(32);
        }

        try {
            data = sb.toString().getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return new SecretKeySpec(data, "AES");
    }

    /**
     * 加密字节数据
     **/
    public static byte[] encrypt(byte[] content, String password, boolean isSaftMode) {
        try {
            if (isSaftMode) {
                return encryptSaft(content, password);
            } else {
                return encryptFast(content, password);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解密字节数组
     **/
    public static byte[] decrypt(byte[] content, String password, boolean isSaftMode) {
        try {
            if (isSaftMode) {
                return decryptSaft(content, password);
            } else {
                return decryptFast(content, password);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 加密字节数据(更安全)
     **/
    public static byte[] encryptSaft(byte[] content, String password) {
        try {
            SecretKeySpec key = createKey(password);
            System.out.println(key);
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_CBC);
            IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes(CHARSET));
            cipher.init(Cipher.ENCRYPT_MODE, key, iv);
            byte[] result = cipher.doFinal(content);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解密字节数组(更安全)
     **/
    public static byte[] decryptSaft(byte[] content, String password) {
        try {
            SecretKeySpec key = createKey(password);
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_CBC);
            IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes(CHARSET));
            cipher.init(Cipher.DECRYPT_MODE, key, iv);
            byte[] result = cipher.doFinal(content);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 加密字节数据(速度快)
     **/
    public static byte[] encryptFast(byte[] content, String password) {
        try {
            SecretKeySpec key = createKey(password);
            System.out.println(key);
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_ECB);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] result = cipher.doFinal(content);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解密字节数组(速度快)
     **/
    public static byte[] decryptFast(byte[] content, String password) {
        try {
            SecretKeySpec key = createKey(password);
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_ECB);
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] result = cipher.doFinal(content);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 加密(结果为16进制字符串)
     **/
    public static String encryptFast(String content, String password) {
        byte[] data = null;
        try {
            data = content.getBytes("UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        data = encryptFast(data, password);
        String result = byte2hex(data);
        return result;
    }


    /**
     * 解密16进制的字符串为字符串
     **/
    public static String decryptFast(String content, String password) {
        byte[] data = null;
        try {
            data = hex2byte(content);
        } catch (Exception e) {
            e.printStackTrace();
        }
        data = decryptFast(data, password);
        if (data == null)
            return null;
        String result = null;
        try {
            result = new String(data, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 字节数组转成16进制字符串
     **/
    public static String byte2hex(byte[] b) { // 一个字节的数,
        StringBuffer sb = new StringBuffer(b.length * 2);
        String tmp = "";
        for (int n = 0; n < b.length; n++) {
            // 整数转成十六进制表示
            tmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
            if (tmp.length() == 1) {
                sb.append("0");
            }
            sb.append(tmp);
        }
        return sb.toString().toUpperCase(); // 转成大写
    }

    /**
     * 将hex字符串转换成字节数组
     **/
    private static byte[] hex2byte(String inputString) {
        if (inputString == null || inputString.length() < 2) {
            return new byte[0];
        }
        inputString = inputString.toLowerCase();
        int l = inputString.length() / 2;
        byte[] result = new byte[l];
        for (int i = 0; i < l; ++i) {
            String tmp = inputString.substring(2 * i, 2 * i + 2);
            result[i] = (byte) (Integer.parseInt(tmp, 16) & 0xFF);
        }
        return result;
    }

}

2.我们需要定义一个文件加密解密操作的抽象接口(IFileEncrytionControl)

public interface IFileEncrytionControl {
    /**
     * 加密文件,全参数
     *
     * @param sourceFileTargetPath 源文件的路径
     * @param targetFolderName     加密后文件夹的路径
     * @param targetFileName       加密后的文件名
     * @param key                  key
     * @param startPosition        开始加密的文件位置
     * @param length               需要加密部分的长度
     * @throws Exception 抛出异常
     */
    void getBaseEncryptFile(String sourceFileTargetPath, String targetFolderName, String targetFileName,
                            String key, int startPosition, int length) throws Exception;

    /**
     * 解密文件,全参数
     *
     * @param sourceFileTargetPath 源文件的路径
     * @param targetFolderName     解密后文件夹的路径
     * @param targetFileName       解密后的文件名
     * @param key                  key
     * @param startPosition        开始解密的文件位置(与加密位置保持一致)
     * @param length               需要解密部分的长度
     * @throws Exception 抛出异常
     */
    void getBaseDecryptFile(String sourceFileTargetPath, String targetFolderName, String targetFileName,
                            String key, int startPosition, int length) throws Exception;

    /**
     * 加密文件(全文件加密)
     *
     * @param sourceFileTargetPath 源文件的路径
     * @param targetFolderName     加密后文件夹的路径
     * @param targetFileName       加密后的文件名
     * @param key                  key
     * @throws Exception 抛出异常
     */
    void getEncryptFile(String sourceFileTargetPath, String targetFolderName, String targetFileName, String key) throws Exception;

    /**
     * 解密文件(全文件解密)
     *
     * @param sourceFileTargetPath 源文件的路径
     * @param targetFolderName     解密后文件夹的路径
     * @param targetFileName       解密后的文件名
     * @param key                  key
     * @throws Exception 抛出异常
     */
    void getDecryptFile(String sourceFileTargetPath, String targetFolderName, String targetFileName, String key) throws Exception;

    /**
     * 加密文件(自动生成key,并写进加密文件)
     *
     * @param sourceFileTargetPath 源文件的路径
     * @param targetFolderName     加密后文件夹的路径
     * @param targetFileName       加密后的文件名
     * @throws Exception 抛出异常
     */
    void getEncryptFileSelfKey(String sourceFileTargetPath, String targetFolderName, String targetFileName) throws Exception;

    /**
     * 解密文件(从已加密的文件读取key)
     *
     * @param sourceFileTargetPath 源文件的路径
     * @param targetFolderName     解密后文件夹的路径
     * @param targetFileName       解密后的文件名
     * @throws Exception 抛出异常
     */
    void getDecryptFileSelfKey(String sourceFileTargetPath, String targetFolderName, String targetFileName) throws Exception;

    /**
     * 加密文件(随机生成key和加密位置的信息,并写进加密文件)
     *
     * @param sourceFileTargetPath 源文件的路径
     * @param targetFolderName     加密后文件夹的路径
     * @param targetFileName       加密后的文件名
     * @throws Exception 抛出异常
     */
    void getEncryptFileSelfKeyAndLength(String sourceFileTargetPath, String targetFolderName, String targetFileName) throws Exception;

    /**
     * 解密文件(在文件中获取key和加密位置的信息)
     *
     * @param sourceFileTargetPath 源文件的路径
     * @param targetFolderName     解密后文件夹的路径
     * @param targetFileName       解密后的文件名
     * @throws Exception 抛出异常
     */
    void getDecryptFileSelfKeyAndLength(String sourceFileTargetPath, String targetFolderName, String targetFileName) throws Exception;

    /**
     * 加密文件(自动生成key)
     *
     * @param sourceFileTargetPath 源文件的路径
     * @param targetFolderName     加密后文件夹的路径
     * @param targetFileName       加密后的文件名
     * @param startPosition        开始加密的文件位置
     * @param length               需要加密部分的长度
     * @throws Exception 抛出异常
     */
    void getEncryptFileSelfKeySomePart(String sourceFileTargetPath, String targetFolderName, String targetFileName,
                                       int startPosition, int length) throws Exception;
}

3.定义一个抽象的基类类,同时实现上面的抽象操作接口(BaseEncryptionManager)

public abstract class BaseEncryptionManager implements IFileEncrytionControl {
    //加密每块的大小
    public static int FILE_BLOCK_BYTE_SIZE = 4096;

    /**
     * 加密/解密文件,全参数
     *
     * @param sourceFileTargetPath 源文件的路径
     * @param targetFolderName     加密后文件夹的路径
     * @param targetFileName       加密后的文件名
     * @param key                  的key
     * @param startPosition        开始加密的文件位置
     * @param length               需要加密部分的长度
     * @param isEncrypt            true表示加密,false表示解密
     * @throws Exception 抛出异常
     */
    protected void getBaseEncryptFileByType(String sourceFileTargetPath, String targetFolderName, String targetFileName,
                                            String key, int startPosition, int length, boolean isEncrypt) throws Exception {
        File sourceFile = new File(sourceFileTargetPath);
        if (sourceFile == null || !sourceFile.exists()) {
            throw new Exception("源文件不存在");
        }

        FileInputStream inputStream = null;
        FileOutputStream outputStream = null;
        try {
            File targetFolder = new File(targetFolderName);
            if (!targetFolder.exists()) {
                targetFolder.mkdirs();
            }
            File targetFile = new File(targetFolderName + "/" + targetFileName);
            if (targetFile.exists()) {
                targetFile.delete();
            } else {
                targetFile.createNewFile();
            }
            inputStream = new FileInputStream(sourceFile);
            outputStream = new FileOutputStream(targetFile);
            int blockSize = 0;
            if (isEncrypt) {
                //插入文件头并获取blocksize
                blockSize = writeFileHead(outputStream);
                int[] result = writeStartPoiAndLength(startPosition, length, outputStream, sourceFile.length());
                startPosition = result[0];
                length = result[1];
                key = writeKey(key, outputStream);
                writePreDatas(startPosition, inputStream, outputStream, blockSize);
                getBaseEncrytCore(key, startPosition, length, blockSize, inputStream, outputStream, getSaftMode());
            } else {
                //获取文件头并获取blocksize
                blockSize = getFileHead(inputStream);
                startPosition = getStartPosition(startPosition, inputStream);
                length = getLength(length, inputStream);
                key = getKeyString(key, inputStream);
                writePreDatas(startPosition, inputStream, outputStream, blockSize);
                getBaseDecrytCore(key, startPosition, length, blockSize, inputStream, outputStream, getSaftMode());
            }
            writeSurplusDatas(blockSize, inputStream, outputStream);
        } catch (Exception e) {
            throw e;
        } finally {
            //释放资源
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                inputStream = null;
            }
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                outputStream = null;
            }
        }
    }

    /**
     * 获取文件信息头
     *
     * @param inputStream
     * @return
     * @throws Exception
     */
    private int getFileHead(FileInputStream inputStream) throws Exception {
        //获取头信息
        //获取加密类型
        byte[] typeByte = new byte[1];
        inputStream.read(typeByte);
        int type = LwNetWorkTool.readInt(typeByte, 0, 1);
        if (type != getEncryptType()) {
            throw new Exception("解密类型不正确,视频类型为:" + type);
        }
        //从加密文件的前两个字节获取到加密块的大小
        byte[] blockByte = new byte[2];
        inputStream.read(blockByte);
        int blockByteSize = LwNetWorkTool.readInt(blockByte, 0, 2);
        return blockByteSize;
    }


    /**
     * 解密时获取key
     *
     * @param key
     * @param inputStream
     * @return
     * @throws Exception
     */
    private String getKeyString(String key, FileInputStream inputStream) throws Exception {
        if (StringUtil.isEmpty(key)) {
            // 如果为空,说明要去文件头获取
            byte[] passHeads = new byte[16];
            inputStream.read(passHeads);
            key = new String(passHeads, "UTF-8");
        }
        return key;
    }

    /**
     * 解密时获取长度
     *
     * @param length
     * @param inputStream
     * @return
     * @throws Exception
     */
    private int getLength(int length, FileInputStream inputStream) throws Exception {
        //去文件获取第4个字节
        byte[] lBytes = new byte[4];
        inputStream.read(lBytes);
        if (length == UNKNOW_LENGTH) {
            length = LwNetWorkTool.readInt(lBytes, 0, 4);
        }
        return length;
    }

    /**
     * 解密时获取开始位置
     *
     * @param startPosition
     * @param inputStream
     * @return
     * @throws Exception
     */
    private int getStartPosition(int startPosition, FileInputStream inputStream) throws Exception {
        //去文件获取第3位字节
        byte[] sBytes = new byte[2];
        inputStream.read(sBytes);
        if (startPosition == UNKNOW_START_POSITION) {
            startPosition = LwNetWorkTool.readInt(sBytes, 0, 2);
        }
        return startPosition;
    }

    /**
     * 写入加密内容前的数据
     *
     * @param startPosition
     * @param inputStream
     * @param outputStream
     * @param blockSize
     * @throws Exception
     */
    private void writePreDatas(int startPosition, FileInputStream inputStream, FileOutputStream outputStream, int blockSize) throws Exception {
        //如果大于0,就需要把视频文件的前面部分数据复制出来
        if (startPosition > 0) {
            byte[] beforebs = null;
            int temp = -1;
            if (startPosition < blockSize) {
                beforebs = new byte[startPosition];
                inputStream.read(beforebs);
                outputStream.write(beforebs, 0, beforebs.length);
            } else {
                int totalPage = startPosition / blockSize;
                int page = 0;
                beforebs = new byte[blockSize];
                while ((temp = inputStream.read(beforebs)) != -1) {
                    outputStream.write(beforebs, 0, temp);
                    if (page == totalPage) {
                        break;
                    }
                    page++;
                }
            }
        }
    }

    /**
     * 加密文件时候写入文件头信息
     *
     * @param outputStream
     * @return
     * @throws Exception
     */
    private int writeFileHead(FileOutputStream outputStream) throws Exception {
        //写入头信息
        int type = getEncryptType();
        byte[] typeByte = new byte[1];
        LwNetWorkTool.writeLong(typeByte, type, 0, 1);
        outputStream.write(type);
        //初始化每一块加密的大小
        int blockByteSize = FILE_BLOCK_BYTE_SIZE;
        byte[] blockByte = new byte[2];
        LwNetWorkTool.writeLong(blockByte, blockByteSize, 0, 2);
        //将每一块的加密数据大小写进加密文件的第一和第二个字节
        outputStream.write(blockByte);
        return blockByteSize;
    }

    /**
     * 加密时写入开始的位置和长度
     *
     * @param startPosition
     * @param outputStream
     * @param fileLength
     * @return
     * @throws Exception
     */
    private int[] writeStartPoiAndLength(int startPosition, int length, FileOutputStream outputStream, long fileLength) throws Exception {
        int[] result = new int[2];
        if (startPosition == UNKNOW_START_POSITION) {
            //写入文件第3个字节
            startPosition = RandomHelper.getRamdomNum(0, 64);
        }
        if (startPosition > fileLength) {
            startPosition = 0;
        }
        if (length == UNKNOW_LENGTH) {
            //写入文件第4个字节
            length = RandomHelper.getRamdomNum(16, 128);
        }
        if (length > fileLength) {
            length = (int) fileLength;
        }

        if (startPosition + length > fileLength) {
            startPosition = 0;
            length = (int) fileLength;
        }
        byte[] sByte = new byte[2];
        LwNetWorkTool.writeLong(sByte, startPosition, 0, 2);
        outputStream.write(sByte);
        byte[] lByte = new byte[4];
        LwNetWorkTool.writeLong(lByte, length, 0, 4);
        outputStream.write(lByte);
        result[0] = startPosition;
        result[1] = length;
        return result;
    }

    /**
     * 写入加密后的剩余数量
     *
     * @param blockByteSize
     * @param inputStream
     * @param outputStream
     * @throws Exception
     */
    private void writeSurplusDatas(int blockByteSize, FileInputStream inputStream, FileOutputStream outputStream) throws Exception {
        //将剩余数据写入
        byte[] bs = new byte[blockByteSize];
        int temp = -1;
        while ((temp = inputStream.read(bs)) != -1) {
            outputStream.write(bs, 0, temp);
        }
        outputStream.flush();
    }

    /**
     * 加密文件,全参数
     *
     * @param sourceFileTargetPath 源文件的路径
     * @param targetFolderName     加密后文件夹的路径
     * @param targetFileName       加密后的文件名
     * @param key                  的key
     * @param startPosition        开始加密的文件位置
     * @param length               需要加密部分的长度
     * @throws Exception 抛出异常
     */
    @Override
    public void getBaseEncryptFile(String sourceFileTargetPath, String targetFolderName, String targetFileName, String key, int startPosition, int length) throws Exception {
        getBaseEncryptFileByType(sourceFileTargetPath, targetFolderName, targetFileName, key, startPosition, length, true);
    }

    /**
     * 解密文件,全参数
     *
     * @param sourceFileTargetPath 源文件的路径
     * @param targetFolderName     解密后文件夹的路径
     * @param targetFileName       解密后的文件名
     * @param key                  的key
     * @param startPosition        开始解密的文件位置(与加密位置保持一致)
     * @param length               需要解密部分的长度
     * @throws Exception 抛出异常
     */
    @Override
    public void getBaseDecryptFile(String sourceFileTargetPath, String targetFolderName, String targetFileName, String key, int startPosition, int length) throws Exception {
        getBaseEncryptFileByType(sourceFileTargetPath, targetFolderName, targetFileName, key, startPosition, length, false);
    }

    /**
     * 加密文件(全文件加密)
     *
     * @param sourceFileTargetPath 源文件的路径
     * @param targetFolderName     加密后文件夹的路径
     * @param targetFileName       加密后的文件名
     * @param key                  的key
     * @throws Exception 抛出异常
     */
    @Override
    public void getEncryptFile(String sourceFileTargetPath, String targetFolderName, String targetFileName, String key) throws Exception {
        File sourceFile = new File(sourceFileTargetPath);
        if (sourceFile == null || !sourceFile.exists()) {
            throw new Exception("源文件不存在");
        }
        getBaseEncryptFile(sourceFileTargetPath, targetFolderName, targetFileName, key, 0, (int) sourceFile.length());
    }

    /**
     * 解密文件(全文件解密)
     *
     * @param sourceFileTargetPath 源文件的路径
     * @param targetFolderName     解密后文件夹的路径
     * @param targetFileName       解密后的文件名
     * @param key                  的key
     * @throws Exception 抛出异常
     */
    @Override
    public void getDecryptFile(String sourceFileTargetPath, String targetFolderName, String targetFileName, String key) throws Exception {
        File sourceFile = new File(sourceFileTargetPath);
        if (sourceFile == null || !sourceFile.exists()) {
            throw new Exception("源文件不存在");
        }
        getBaseDecryptFile(sourceFileTargetPath, targetFolderName, targetFileName, key, 0, UNKNOW_LENGTH);
    }

    /**
     * 加密文件(自动生成key,并写进加密文件)
     *
     * @param sourceFileTargetPath 源文件的路径
     * @param targetFolderName     加密后文件夹的路径
     * @param targetFileName       加密后的文件名
     * @throws Exception 抛出异常
     */
    @Override
    public void getEncryptFileSelfKey(String sourceFileTargetPath, String targetFolderName, String targetFileName) throws Exception {
        File sourceFile = new File(sourceFileTargetPath);
        if (sourceFile == null || !sourceFile.exists()) {
            throw new Exception("源文件不存在");
        }
        getBaseEncryptFile(sourceFileTargetPath, targetFolderName, targetFileName, "", 0, (int) sourceFile.length());
    }

    /**
     * 解密文件(从已加密的文件读取key)
     *
     * @param sourceFileTargetPath 源文件的路径
     * @param targetFolderName     解密后文件夹的路径
     * @param targetFileName       解密后的文件名
     * @throws Exception 抛出异常
     */
    @Override
    public void getDecryptFileSelfKey(String sourceFileTargetPath, String targetFolderName, String targetFileName) throws Exception {
        File sourceFile = new File(sourceFileTargetPath);
        if (sourceFile == null || !sourceFile.exists()) {
            throw new Exception("源文件不存在");
        }
        getBaseDecryptFile(sourceFileTargetPath, targetFolderName, targetFileName, "", 0, UNKNOW_LENGTH);
    }

    /**
     * 加密文件(随机生成key和加密位置的信息,并写进加密文件)
     *
     * @param sourceFileTargetPath 源文件的路径
     * @param targetFolderName     加密后文件夹的路径
     * @param targetFileName       加密后的文件名
     * @throws Exception 抛出异常
     */
    @Override
    public void getEncryptFileSelfKeyAndLength(String sourceFileTargetPath, String targetFolderName, String targetFileName) throws Exception {
        File sourceFile = new File(sourceFileTargetPath);
        if (sourceFile == null || !sourceFile.exists()) {
            throw new Exception("源文件不存在");
        }
        getBaseEncryptFile(sourceFileTargetPath, targetFolderName, targetFileName, "", UNKNOW_START_POSITION, UNKNOW_LENGTH);
    }

    /**
     * 解密文件(在文件中获取key和加密位置的信息)
     *
     * @param sourceFileTargetPath 源文件的路径
     * @param targetFolderName     解密后文件夹的路径
     * @param targetFileName       解密后的文件名
     * @throws Exception 抛出异常
     */
    @Override
    public void getDecryptFileSelfKeyAndLength(String sourceFileTargetPath, String targetFolderName, String targetFileName) throws Exception {
        File sourceFile = new File(sourceFileTargetPath);
        if (sourceFile == null || !sourceFile.exists()) {
            throw new Exception("源文件不存在");
        }
        getBaseDecryptFile(sourceFileTargetPath, targetFolderName, targetFileName, "", UNKNOW_START_POSITION, UNKNOW_LENGTH);
    }

    /**
     * 加密文件(自动生成key)
     *
     * @param sourceFileTargetPath 源文件的路径
     * @param targetFolderName     加密后文件夹的路径
     * @param targetFileName       加密后的文件名
     * @param startPosition        开始加密的文件位置
     * @param length               需要加密部分的长度
     * @throws Exception 抛出异常
     */
    @Override
    public void getEncryptFileSelfKeySomePart(String sourceFileTargetPath, String targetFolderName, String targetFileName,
                                              int startPosition, int length) throws Exception {
        getBaseEncryptFile(sourceFileTargetPath, targetFolderName, targetFileName, "", startPosition, length);
    }

//    /**
//     * 解密文件(获取加密文件内的key)
//     *
//     * @param sourceFileTargetPath 源文件的路径
//     * @param targetFolderName     解密后文件夹的路径
//     * @param targetFileName       解密后的文件名
//     * @param startPosition        开始解密的文件位置
//     * @param length               需要解密部分的长度
//     * @throws Exception 抛出异常
//     */
//    @Override
//    public void getDecryptFileSelfKeySomePart(String sourceFileTargetPath, String targetFolderName, String targetFileName,
//                                              int startPosition, int length) throws Exception {
//        getBaseDecryptFile(sourceFileTargetPath, targetFolderName, targetFileName, "", startPosition, length);
//    }

    protected abstract void getBaseEncrytCore(String key, int startPosition, int length, int blockByteSize,
                                              FileInputStream inputStream, FileOutputStream outputStream, boolean saftMode) throws Exception;

    protected abstract void getBaseDecrytCore(String key, int startPosition, int length, int blockByteSize,
                                              FileInputStream inputStream, FileOutputStream outputStream, boolean saftMode) throws Exception;

    protected abstract int getEncryptType();

    protected abstract String writeKey(String key, OutputStream outputStream) throws Exception;

    /**
     * 设置是否是安全模式,true表示加密安全优先,false表示速度优先
     *
     * @return
     */
    protected abstract boolean getSaftMode();
}

4.同时我们需要定义一下类型的常量

public class BaseConstans {
    public static final int AES_ENCRYPTION = 0;
    public static final int XOR_ENCRYPTION = 1;
    public static final int DES_ENCRYPTION = 2;

    public static final int UNKNOW_START_POSITION = -1;
    public static final int UNKNOW_LENGTH = -1;
}

5.(核心)通过继承BaseEncryptionManager类,来实现数据的加密以及解密的核心算法,下面仅仅举个AES加密解密的例子:

public class FileAesEncryptionUtil extends BaseEncryptionManager {
    public static FileAesEncryptionUtil util = null;

    public static FileAesEncryptionUtil getInstance() {
        if (util == null) {
            synchronized (FileAesEncryptionUtil.class) {
                if (util == null) {
                    util = new FileAesEncryptionUtil();
                }
            }
        }
        return util;
    }

    /**
     * 加密核心部分
     *
     * @param key
     * @param startPosition
     * @param length
     * @param inputStream
     * @param outputStream
     * @throws Exception
     */
    @Override
    protected void getBaseEncrytCore(String key, int startPosition, int length, int blockByteSize,
                                     FileInputStream inputStream, FileOutputStream outputStream, boolean saftMode) throws Exception {
        try {
            if (length < blockByteSize) {
                //加密部分数据
                //如果要加密的长度小于每块的长度,直接加密
                byte[] passDatas = new byte[length];
                inputStream.read(passDatas, 0, passDatas.length);
                byte[] encrypt = AESHelper.encrypt(passDatas, key, saftMode);
                outputStream.write(encrypt);
            } else {
                //如果是大文件就需要分包
                //加密写入
                //如果数据量大
                byte[] passDatas = new byte[blockByteSize];
                int totalPage = length / passDatas.length;
                int page = 0;
                int temp = -1;
                byte[] encrypt = null;
                while ((temp = inputStream.read(passDatas)) != -1) {
                    if (temp != passDatas.length) {
                        byte[] lastData = new byte[temp];
                        System.arraycopy(passDatas, 0, lastData, 0, temp);
                        encrypt = AESHelper.encrypt(lastData, key, saftMode);
                    } else {
                        encrypt = AESHelper.encrypt(passDatas, key, saftMode);
                    }
                    outputStream.write(encrypt);
                    if (page == totalPage) {
                        break;
                    }
                    page++;
                }
            }
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * 解密核心部分
     *
     * @param key
     * @param startPosition
     * @param length
     * @param inputStream
     * @param outputStream
     * @throws Exception
     */
    @Override
    protected void getBaseDecrytCore(String key, int startPosition, int length, int blockByteSize,
                                     FileInputStream inputStream, FileOutputStream outputStream, boolean saftMode) throws Exception {
        try {
            //length需要是16的倍数
            if (length % 16 != 0) {
                int count = (length / 16) + 1;
                length = count * 16;
            } else {
                length = length + 16;
            }

            if (length < blockByteSize) {
                //解密部分数据
                //如果要解密的长度小于每块的长度,直接解密
                byte[] passDatas = new byte[length];
                inputStream.read(passDatas, 0, passDatas.length);
                byte[] decrypt = AESHelper.decrypt(passDatas, key, saftMode);
                outputStream.write(decrypt);
            } else {
                //如果数据量大
                //由于加密后会增加16字节,所以需要+16来进行计算
                byte[] passDatas = new byte[blockByteSize + 16];
                int totalPage = length / passDatas.length;
                int page = 0;
                int temp = -1;
                byte[] decrypt = null;
                while ((temp = inputStream.read(passDatas)) != -1) {
                    if (temp != passDatas.length) {
                        byte[] lastData = new byte[temp];
                        System.arraycopy(passDatas, 0, lastData, 0, temp);
                        decrypt = AESHelper.decrypt(lastData, key, saftMode);
                    } else {
                        decrypt = AESHelper.decrypt(passDatas, key, saftMode);
                    }
                    outputStream.write(decrypt);
                    if (page == totalPage) {
                        break;
                    }
                    page++;
                }
            }
        } catch (Exception e) {
            throw e;
        }
    }

    @Override
    protected int getEncryptType() {
        return AES_ENCRYPTION;
    }

    @Override
    protected String writeKey(String key, OutputStream outputStream) throws Exception {
        if (StringUtil.isEmpty(key)) {
            // 如果为空,说明要随机生成并写在文件头
            key = RandomHelper.getNumSmallCharRandom(16);
            byte[] passHeads = key.getBytes();
            outputStream.write(passHeads, 0, passHeads.length);
        }
        return key;
    }

    @Override
    protected boolean getSaftMode() {
        return EncryptionModeManager.IS_SAFT_ENCRYPT_MODE;
    }
}

该库可以把key写进视频文件里,也可以外部传入key;可以加密解密文件的全部数据,也可以只加密解密部分数据,节约时间。

写完这个库,发现代码真的是要一点一点的进行优化。一开始的写这个库的时候,全部的方法和功能,都写在了FileAesEncryptionUtil一个类里面,不利于扩展其他的加密方式。然后我就得想办法抽离出各种加密方法相同的操作形成接口,每添加一种加密方式,就实现这个操作的接口。这样的一顿操作下来,这个库的扩展性就提高了。就在我觉得可以收工的时候,实际情况却是遇到了其他的问题,当我加入了DES加密以及异或加密之后,发现这几个类的好多方法步骤都是相同的,复用性还是达不到。没办法,我只能继续把相同的部分抽离出来,放到一个基类里面,继承这个基类的子类只需要实现数据的加密和数据的解密部分就足够了,其他的文件处理、流处理、文件头处理,都交给基类的方法来做。这样的话,扩展会更加方便,也会更加快捷的插入更多的加密方式。
除此之外,文件加密解密处理还得分片读取,否则一次性把全部字节数组都读出来,这样就会造成安卓经典问题OOM。

代码一把梭会一时爽,但是后期还是得各种整合重构,代码价值才会提高。

你可能感兴趣的:(一个文件加密和解密的库开发历程)