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

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。

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容