使用AES进行文件加密算法

前言:最近想对手机上一些文件进行加密隐藏,想自己基于jvm平台写一个(kotlin/java)但是网上的加密算法都是不公开的,所以自己利用AES的算法整出了一个文件加密解密的工具

注意:因为我电脑上的JDK是12+,所以如果移植到安卓上有出现报错,是正常现象,只需要修改 AESEncoder 文件就好了

FileEncoder.kt

import java.io.Closeable
import java.io.File
import java.io.RandomAccessFile
import java.lang.StringBuilder

/**
 * 文件加密
 * 注意!!千万不可以使用多线程来同时加密/解密多个文件
 */
class FileEncoder(
        passwd: String,
        val debug: Boolean = true,
        bufferSize: Int = (1024 * 1024 * 1)
) : Closeable
{
    private val aesEncoder = AESEncoder(passwd)
    private val buffer = ByteArray(bufferSize)
    private val spliteStr = "|"
    /**
     * 加密算法
     * [msgLen | encode_file | msg]
     */
    fun encode(src: File, e_file: File, process: (process: Float) -> Unit)
    {
        val srcLen = src.length().toFloat()
        val fis = src.inputStream()
        val accessFile = RandomAccessFile(e_file, "rw")
        // 这里在文件顶部写入8位的占位符
        var msgLen = 0L
        val headBytes = long2Bytes(msgLen)
        accessFile.write(headBytes)
        log("写入头部信息")
        val msgBuilder = StringBuilder()
        var readLen = fis.read(buffer)
        var readCount = 0L
        while (readLen > 0)
        {
            readCount += readLen
            val encodeArray = aesEncoder.encode(buffer, readLen)
            msgBuilder.append(encodeArray.size).append(spliteStr)
            accessFile.write(encodeArray)
            process.invoke(readCount / srcLen)
            readLen = fis.read(buffer)
        }
        val msg = msgBuilder.toString()
        msgBuilder.setLength(0)
        log(msg)
        // 将尾部信息转换成字节数组
        val msgByteArry = msg.toByteArray()
        // 将尾部信息字节数组,加密
        val msgEncodeByteArray = aesEncoder.encode(msgByteArry, msgByteArry.size)
        // 写入加密尾部信息
        accessFile.write(msgEncodeByteArray)
        log("写入加密尾部信息")
        // 测量出加密后的信息长度是多少
        msgLen = msgEncodeByteArray.size.toLong()
        log("新的长度:$msgLen")
        // 将光标移动到文件头,复写信息
        accessFile.seek(0)
        // 重新将长度写入进去
        accessFile.write(long2Bytes(msgLen))

        fis.close()
        accessFile.close()
    }

    /**
     * 解密算法
     */
    fun decode(src: File, d_file: File, process: (process: Float) -> Unit)
    {
        val buffer: ByteArray
        val raf = RandomAccessFile(src, "r")
        val fos = d_file.outputStream()

        val headBytes = ByteArray(8)
        val headLen = raf.read(headBytes)
        if (headLen != 8)
        {
            raf.close()
            fos.close()
            return
        }
        val msgLen = bytes2Long(headBytes)
        log("解密:$msgLen")
        val srcLen = src.length()
        val msgByteArray = ByteArray(msgLen.toInt())
        // 将光标移动到尾部,读取信息
        raf.seek(srcLen - msgLen)
        val readMsgLen = raf.read(msgByteArray)
        log("已读取信息长度:$readMsgLen")
        val decodeMsgArray = aesEncoder.decode(msgByteArray, msgByteArray.size)
        val decodeMsg = String(decodeMsgArray)
        log(decodeMsg)
        val msgs = decodeMsg.split(spliteStr).dropLastWhile { it.isEmpty() }
        val msgLong = msgs.map { it.toLong() }
        val maxBufferSize = msgLong.max() ?: 0
        buffer = ByteArray(maxBufferSize.toInt())

        log(msgLong)
        // 将光标回到读取完头部信息的位置
        raf.seek(8)
        msgLong.forEachIndexed { index, len ->
            raf.read(buffer, 0, len.toInt())
            val encodeArray = aesEncoder.decode(buffer, len.toInt())
            fos.write(encodeArray)
            process.invoke(index.toFloat() / msgLong.size)
        }
        process.invoke(1f)
        fos.flush()
        fos.close()
    }


    /**
     * 长整型转换成字节数组
     */
    private fun long2Bytes(num: Long): ByteArray
    {
        val byteNum = ByteArray(8)
        for (ix in 0..7)
        {
            val offset = 64 - (ix + 1) * 8
            byteNum[ix] = (num shr offset and 0xff).toByte()
        }
        return byteNum
    }

    /**
     * 字节数组转换成长整型
     */
    private fun bytes2Long(byteNum: ByteArray): Long
    {
        var num: Long = 0
        for (ix in 0..7)
        {
            num = num shl 8
            num = num or (byteNum[ix].toInt() and 0xff).toLong()
        }
        return num
    }

    private inline fun log(msg: Any?)
    {
        if (debug)
        {
            println(msg)
        }
    }

    override fun close()
    {
        aesEncoder.close()
    }
}

AESEncoder.kt


import java.io.Closeable
import java.security.SecureRandom
import java.util.*
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.spec.SecretKeySpec


/*
 * AES对称加密和解密
 */
class AESEncoder(encodeRules: String) : Closeable
{
    private val encoder = Base64.getEncoder()
    private val decoder = Base64.getDecoder()
    private var encodeCipher: Cipher
    private var decoderCipher: Cipher

    private val bufferHolder = LinkedHashMap<Int, ByteArray>()

    init
    {
        //1.构造密钥生成器,指定为AES算法,不区分大小写
        val keygen = KeyGenerator.getInstance("AES")
        //2.根据ecnodeRules规则初始化密钥生成器
        //生成一个128位的随机源,根据传入的字节数组
        keygen.init(128, SecureRandom(encodeRules.toByteArray()))
        //3.产生原始对称密钥
        val original_key = keygen.generateKey()
        //4.获得原始对称密钥的字节数组
        val raw = original_key.encoded
        //5.根据字节数组生成AES密钥
        val key = SecretKeySpec(raw, "AES")

        //6.根据指定算法AES自成密码器
        encodeCipher = Cipher.getInstance("AES")
        decoderCipher = Cipher.getInstance("AES")

        //7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEY
        encodeCipher.init(Cipher.ENCRYPT_MODE, key)
        //7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作,第二个参数为使用的KEY
        decoderCipher.init(Cipher.DECRYPT_MODE, key)
    }

    /*
     * 加密
     * 1.构造密钥生成器
     * 2.根据ecnodeRules规则初始化密钥生成器
     * 3.产生密钥
     * 4.创建和初始化密码器
     * 5.内容加密
     * 6.返回字符串
     */
    fun encode(content: ByteArray, len: Int): ByteArray
    {

        var buffer = bufferHolder[len]
        if (buffer == null)
        {
            buffer = ByteArray(len)
            bufferHolder[len] = buffer
        }
        System.arraycopy(content, 0, buffer, 0, len)
        //8.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
        val byte_encode = buffer
        //9.根据密码器的初始化方式--加密:将数据加密
        val byte_AES = encodeCipher.doFinal(byte_encode)
        //10.将加密后的数据转换为字符串
        //这里用Base64Encoder中会找不到包
        //解决办法:
        //在项目的Build path中先移除JRE System Library,再添加库JRE System Library,重新编译后就一切正常了。
        //11.将字符串返回
        return encoder.encode(byte_AES)
    }

    /*
     * 解密
     * 解密过程:
     * 1.同加密1-4步
     * 2.将加密后的字符串反纺成byte[]数组
     * 3.将加密内容解密
     */
    fun decode(content: ByteArray, len: Int): ByteArray
    {
        var buffer = bufferHolder[len]
        if (buffer == null)
        {
            buffer = ByteArray(len)
            bufferHolder[len] = buffer
        }
        System.arraycopy(content, 0, buffer, 0, len)
        //8.将加密并编码后的内容解码成字节数组
        val byte_content = decoder.decode(buffer)
        /*
         * 解密
         */
        val byte_decode = decoderCipher.doFinal(byte_content)
        return byte_decode
    }
    
    override fun close()
    {
        bufferHolder.clear()
    }
}

效果

右边是加密后,左边是解密后

右边是加密后的文件,左边是解密后的文件

end