文件目录加密指的是通过加密方法加密目录名称,但保留目录结构,通过加密的目录保存加密文件,进而保持整个目录的保密性。
由于目录的特别,加密的方法需要满足以下要求:
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
标签:加密,16,方法,iv,Length,cipher,文件目录,byte,data From: https://www.cnblogs.com/bsmith/p/17766157.html