C#文件加密和解密

由于项目需要,要对文件进行加密操作,所以对这个功能做了一点学习。不难,需要注意的地方也不多:

  1. 加密算法和加密后的长度问题(这里用的AES);
  2. 加密大文件使用流的方式一点一点加密(防止一次性吃掉很多内存);
  3. 加密过程的回调;
  4. 文件结构;

截图

加密: 
加密

解密: 
解密

选择文件: 
选择文件

内存及CPU的使用: 
内存及CPU的使用

一、算法

首先,文件要加密就一定要解密,所以一定要找个能解密的算法,这个不用多说……其实加密解密算法也算是C#里自带了,代码如下:

/// <summary>
/// 加密
/// </summary>
/// <param name="array">要加密的 byte[] 数组</param>
/// <param name="key"></param>
/// <returns></returns>
public static byte[] Encrypt(byte[] array, string key)
{
    key = FmtPassword(key);
    byte[] keyArray = Encoding.UTF8.GetBytes(key);

    RijndaelManaged rDel = new RijndaelManaged();
    rDel.Key = keyArray;
    rDel.Mode = CipherMode.ECB;
    rDel.Padding = PaddingMode.PKCS7;

    ICryptoTransform cTransform = rDel.CreateEncryptor();
    byte[] resultArray = cTransform.TransformFinalBlock(array, 0, array.Length);

    return resultArray;
} 

/// <summary>
/// 解密
/// </summary>
/// <param name="array">要解密的 byte[] 数组</param>
/// <param name="key"></param>
/// <returns></returns>
public static byte[] Decrypt(byte[] array, string key)
{
    key = FmtPassword(key);
    byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);

    RijndaelManaged rDel = new RijndaelManaged();
    rDel.Key = keyArray;
    rDel.Mode = CipherMode.ECB;
    rDel.Padding = PaddingMode.PKCS7;

    ICryptoTransform cTransform = rDel.CreateDecryptor();
    byte[] resultArray = cTransform.TransformFinalBlock(array, 0, array.Length);

    return resultArray;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

这里要注意一点,AES加密的话,密码是需要32位的,不然会提示错误:DES和AES加密:指定键的大小对于此算法无效 
所以需要自己把不够长度的密码填充一下(超出长度的也需要处理一下),避免出现这种错误

二、流

文件加密的功能网上有很多,我发现有很多直接把文件全部读取出来,然后加密整个文件。这种方法在处理大文件会消耗非常大的内存,所以需要注意不能一次性把文件全加密。

注1: 关于加密的部分,可以只加密文件的一部分数据,这样可以保证加密速度。

注2: 使用流读取部分,循环加密的时候,AES加密后的内容长度会增加,比如要加密一个 byte[1024] 大小的内容,加密后会变成 byte[1024+16] ,这一点可以自己试一试。

三、进度

进度方面是很有必要的,在加密大文件的时候,如果光等着没有进度也显得很尴尬,所以做一个委托,来把进度回调出来。

// 定义
public delegate void ProgressHandler(object sender, ProgressEventArgs e);

// 使用
progress?.Invoke(sender, new ProgressEventArgs(fsRead.Position, fsRead.Length));
  • 1
  • 2
  • 3
  • 4
  • 5

四、文件结构

加密文件后的文件结构是要自己重新做定义的,使自己的程序能读懂,并且能成功解密即可。这里贴一个文件加密的方法: 
里面有很多我自己写的方法,下面会给链接地址

/// <summary>
/// 文件加密
/// </summary>
/// <param name="srcFile">源文件</param>
/// <param name="dstFile">目标文件</param>
/// <param name="password">加密密码</param>
/// <param name="progress">回调进度</param>
/// <param name="overwrite">是否覆盖已有目标文件</param>
/// <returns>
/// >0:操作成功(操作共计秒数)
/// -11:要加密的文件不存在
/// -12:加密后的目标文件已存在
/// -404:未知错误,操作失败
/// </returns>
public static int Encrypt(string srcFile, string dstFile, string password, ProgressDelegate.ProgressHandler progress = null, object sender = null, bool overwrite = true)
{
    DateTime beginTime = DateTime.Now;
    if (!File.Exists(srcFile)) return -11; //要加密的文件不存在
    if (File.Exists(dstFile) && !overwrite) return -12;//加密后的目标文件已存在

    string fmtPwd = AesTool.FmtPassword(password);
    string pwdMd5 = MD5Tool.Encrypt(MD5Tool.Encrypt(fmtPwd));

    string md5 = FileTool.GetMD5(srcFile);
    using (FileStream fsRead = new FileStream(srcFile, FileMode.Open))
    {
        using (FileStream fsWrite = new FileStream(dstFile, FileMode.Create))
        {
            try
            {
                //写入文件类型标识和版本号
                byte[] filetypeandversion = Encoding.Default.GetBytes(FileType + FileVersion);
                fsWrite.Write(filetypeandversion, 0, filetypeandversion.Length);

                //文件头部数据定义
                List<byte[]> headdata = new List<byte[]>()
                    {
                        Encoding.Default.GetBytes(FileType),
                        Encoding.Default.GetBytes(md5),
                        Encoding.Default.GetBytes(AesTool.Encrypt(fmtPwd,AesTool.DefaultPassword)),
                        Encoding.Default.GetBytes(pwdMd5),
                        Encoding.Default.GetBytes(DateTime.Now.ToString())
                    };
                //写入头部信息个数
                byte[] count = BitConverter.GetBytes(headdata.Count);
                fsWrite.Write(count, 0, count.Length);
                //写入各部分长度
                for (int i = 0; i < headdata.Count; i++)
                {
                    byte[] length = BitConverter.GetBytes(headdata[i].Length);
                    fsWrite.Write(length, 0, length.Length);
                }
                //写入各部分数据
                for (int i = 0; i < headdata.Count; i++)
                {
                    fsWrite.Write(headdata[i], 0, headdata[i].Length);
                }

                //写入文件源数据
                int readCount = 0;
                byte[] buffer = new byte[FileBuffer];
                while ((readCount = fsRead.Read(buffer, 0, buffer.Length)) > 0)
                {
                    if (readCount != buffer.Length)
                    {
                        byte[] temp = new byte[readCount];
                        Buffer.BlockCopy(buffer, 0, temp, 0, readCount);
                        byte[] enbyte = AesTool.Encrypt(temp, fmtPwd);
                        fsWrite.Write(enbyte, 0, enbyte.Length);
                    }
                    else
                    {
                        byte[] enbyte = AesTool.Encrypt(buffer, fmtPwd);
                        fsWrite.Write(enbyte, 0, enbyte.Length);
                    }
                    progress?.Invoke(sender, new ProgressEventArgs(fsRead.Position, fsRead.Length));
                }
                return (int)Math.Ceiling((DateTime.Now - beginTime).TotalSeconds);//操作成功
            }
            catch (Exception e) { }
        }
        //加密失败后,删除加密的文件
        try { File.Delete(dstFile); } catch (Exception e) { }
    }
    return -404;//未知错误,操作失败
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86

五、源码

下载 
或查看 GitHub 中Oreo.FileMan

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值