文件目录加密方法

文件目录加密指的是通过加密方法加密目录名称,但保留目录结构,通过加密的目录保存加密文件,进而保持整个目录的保密性。

由于目录的特别,加密的方法需要满足以下要求:

1,加密后的密文尽可能短,从而能加密较深的目录结构

2,加密后的密文要能保持差异性,即目录名称间较小的差异,能产生较大的密文差异,从而无法通过密文还推断相近的明文特征

3,加密方法尽可能简单,最好只依赖原始目录结构

常用的加密模式如ECB,CBC等无法满足上述要求,这两种模式下,密文要求16字节对齐,使得密文通常比密文长很多,而且CBC模式需要初始化向量IV,需要专门的文件或目录来存储。

EME,CTR等加密模式能实现密文长度跟明文长度一样,但是EME模式需要初始化向量IV,需要专门的文件或目录存储该IV;而CTR模式由于异或操作的特性,使得相似的明文加密后生成相似的密文,只要破解部分文件名,便能容易的破解相似的文件名。

 

传统CBC模式虽然需要16字节对齐,但是可以通过密文偷窃算法(Cipher Text Steal)实现>16字节的明文生成一致长度的密文,同时,当密文>16字节时,通过使用16字节后的数据哈希值作为IV,可能避免专门的IV存储,哈希函数只用HMAC提高安全性。

而当明文<=16字节时,使用简单的ECB模式加密。

 

加密代码:

public byte[] encrypt(byte[] data)
        {
            if (data.Length < 16)
                return ecbPadEnc.TransformFinalBlock(data, 0, data.Length);
            else if (data.Length == 16)
                return ecbEnc.transform(data, new byte[17]);

            var cipher = new byte[data.Length + 1];
            var iv = hmac.ComputeHash(data, 16, data.Length - 16).head(16);

            // no need steal cipher
            if (data.Length % 16 == 0)
                return cbcEnc(iv, data, 0, data.Length, cipher, 0);

            // transform N-1 CBC block
            var lastPos = 16 * (data.Length / 16);
            cbcEnc(iv, data, 0, lastPos, cipher, 0);

            // steal cipher text for padding
            var lastSize = data.Length - lastPos;
            var stealPos = lastPos - 16 + lastSize;
            Buffer.BlockCopy(cipher, stealPos, data, data.Length - 16, 16 - lastSize);
            ecbEnc.TransformBlock(data, data.Length - 16, 16, cipher, cipher.Length - 17);

            return cipher;
        }

解密代码:

public byte[] decrypt(byte[] cipher)
        {
            if (cipher.Length == 16)
                return ecbPadDec.TransformFinalBlock(cipher, 0, cipher.Length);
            else if (cipher.Length == 17)
                return ecbDec.transform(cipher, 0, 16, new byte[16]);

            var data = new byte[cipher.Length - 1];
            // last steal block
            if (data.Length % 16 != 0)
            {
                ecbDec.TransformBlock(cipher, cipher.Length - 17, 16, data, data.Length - 16);
                // pay back the steal cipher
                Buffer.BlockCopy(data, data.Length - 16, cipher, cipher.Length - 17, 16);
            }
            // middle normal cbc chain
            var middleCount = data.Length / 16 - 1;
            if (middleCount > 0)
                cbcDec(cipher.head(16), cipher, 16, middleCount * 16, data, 16);
            // first block
            var iv = hmac.ComputeHash(data, 16, data.Length - 16).head(16);
            cbcDec(iv, cipher, 0, 16, data, 0);

            return data;
        }

需要注意,当明文为15和16字节时,ECB加密都生成16字节密文,产生混淆,所以实际加密时,当明文长度N>=16字节时,生成的密文长度M=N+1,密文末尾添加字节0x00,便于解密时识别明文长度。

完整代码 HmacIvCbc.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using util.ext;

namespace util.crypt
{
    public class HmacIvCbc : IDisposable
    {
        Aes aes;
        HMACSHA256 hmac;
        ICryptoTransform ecbEnc;
        ICryptoTransform ecbDec;
        ICryptoTransform ecbPadEnc;
        ICryptoTransform ecbPadDec;

        public HmacIvCbc(byte[] key)
        {
            init(key.head(key.Length / 2), key.tail(key.Length / 2));
        }

        public HmacIvCbc(byte[] encKey, byte[] macKey)
        {
            init(encKey, macKey);
        }

        void init(byte[] encKey, byte[] macKey)
        {
            aes = Aes.Create();
            aes.Key = encKey;

            hmac = new HMACSHA256(macKey);
            ecbEnc = getEncoder(CipherMode.ECB);
            ecbPadEnc = getEncoder(CipherMode.ECB, null, PaddingMode.PKCS7);
            ecbDec = getDecoder(CipherMode.ECB);
            ecbPadDec = getDecoder(CipherMode.ECB, null, PaddingMode.PKCS7);
        }

        ICryptoTransform getEncoder(CipherMode mode, byte[] iv = null, PaddingMode pad = PaddingMode.None)
        {
            aes.Mode = mode;
            aes.Padding = pad;
            if (null != iv)
                aes.IV = iv;
            return aes.CreateEncryptor();
        }

        byte[] cbcEnc(byte[] iv, byte[] src, int srcPos, int count, byte[] dst, int dstPos)
        {
            using (var enc = getEncoder(CipherMode.CBC, iv))
            {
                enc.TransformBlock(src, srcPos, count, dst, dstPos);
            }
            return dst;
        }

        ICryptoTransform getDecoder(CipherMode mode, byte[] iv = null, PaddingMode pad = PaddingMode.None)
        {
            aes.Mode = mode;
            aes.Padding = pad;
            if (null != iv)
                aes.IV = iv;
            return aes.CreateDecryptor();
        }

        byte[] cbcDec(byte[] iv, byte[] src, int srcPos, int count, byte[] dst, int dstPos)
        {
            using (var enc = getDecoder(CipherMode.CBC, iv))
            {
                enc.TransformBlock(src, srcPos, count, dst, dstPos);
            }
            return dst;
        }

        public byte[] encrypt(byte[] data)
        {
            if (data.Length < 16)
                return ecbPadEnc.TransformFinalBlock(data, 0, data.Length);
            else if (data.Length == 16)
                return ecbEnc.transform(data, new byte[17]);

            var cipher = new byte[data.Length + 1];
            var iv = hmac.ComputeHash(data, 16, data.Length - 16).head(16);

            // no need steal cipher
            if (data.Length % 16 == 0)
                return cbcEnc(iv, data, 0, data.Length, cipher, 0);

            // transform N-1 CBC block
            var lastPos = 16 * (data.Length / 16);
            cbcEnc(iv, data, 0, lastPos, cipher, 0);

            // steal cipher text for padding
            var lastSize = data.Length - lastPos;
            var stealPos = lastPos - 16 + lastSize;
            Buffer.BlockCopy(cipher, stealPos, data, data.Length - 16, 16 - lastSize);
            ecbEnc.TransformBlock(data, data.Length - 16, 16, cipher, cipher.Length - 17);

            return cipher;
        }

        public byte[] decrypt(byte[] cipher)
        {
            if (cipher.Length == 16)
                return ecbPadDec.TransformFinalBlock(cipher, 0, cipher.Length);
            else if (cipher.Length == 17)
                return ecbDec.transform(cipher, 0, 16, new byte[16]);

            var data = new byte[cipher.Length - 1];
            // last steal block
            if (data.Length % 16 != 0)
            {
                ecbDec.TransformBlock(cipher, cipher.Length - 17, 16, data, data.Length - 16);
                // pay back the steal cipher
                Buffer.BlockCopy(data, data.Length - 16, cipher, cipher.Length - 17, 16);
            }
            // middle normal cbc chain
            var middleCount = data.Length / 16 - 1;
            if (middleCount > 0)
                cbcDec(cipher.head(16), cipher, 16, middleCount * 16, data, 16);
            // first block
            var iv = hmac.ComputeHash(data, 16, data.Length - 16).head(16);
            cbcDec(iv, cipher, 0, 16, data, 0);

            return data;
        }

        public void Dispose()
        {
            ecbEnc?.Dispose();
            ecbDec?.Dispose();
            ecbPadEnc?.Dispose();
            ecbPadDec?.Dispose();
            aes?.Dispose();
            hmac?.Dispose();
        }
    }
}
View Code

Github链接:

https://github.com/bsmith-zhao/vfs/blob/main/util/crypt/HmacIvCbc.cs

posted @ 2023-10-15 20:45  bsmith  阅读(424)  评论(0)    收藏  举报