首页 > 其他分享 >使用Aead加密支持随机读写的文件

使用Aead加密支持随机读写的文件

时间:2023-10-15 22:14:45浏览次数:35  
标签:加密 int 读写 writeLen dataLen conf Aead byte public

关联数据的认证加密Aead(authenticated encryption with associated data),是一种同时具备保密性,完整性和可认证性的加密形式,加密过程采用数据分组形式,对同一个密钥,每次加密需要使用不重复的Nonce(Number used only Once),加密后生成验证数据标签(Tag)用于解密时验证,并且可以附加一段明文数据(associated data)作为额外验证数据。

Aead加密模式的这些特征,非常适合用于随机读写文件的加密,待加密文件数据按固定大小分组,分组序号+随机初始化向量IV作为每个分组加密的Nonce,分组序号作为附加验证数据(associated data),加密文件中只需要保存块大小(BlockSize), 随机初始化向量(IV),加密数据块(Block Cipher)即可。

在块大小和初始化向量已知的情况下,任何文件位置的数据分组序号都能被计算出来,而每次加密最小单元是一个数据块,所以可以定位文件的任何位置进行读写,即文件的随机读写。

使用流读写方法实现AeadStream,写入数据:

public override void Write(byte[] src, int offset, int count)
        {
            actionPos = streamPos;
            var actionLen = streamLen;

            initBuff(count);
            int writeLen;
            while (count > 0)
            {
                writeLen = count.min(blockSize - blockOff);
                if (blockOff == 0
                    && (writeLen == blockSize // overwrite whole block
                        || writeLen >= actionLen - actionPos)) // overwrite exist block
                {
                    encryptPack(src, offset, writeLen);
                }
                else
                {
                    var dataLen = readFile(pack, pack.Length, blockIdx) - tagSize;
                    if (dataLen != (actionLen - blockIdx * blockSize).min(blockSize))
                        throw new Error(this, "DataError", 
                                    dataLen, 
                                    actionLen - blockIdx * blockSize);

                    packEnc.decrypt(pack, 0, dataLen + tagSize, blockIdx, block);
                    Buffer.BlockCopy(src, offset, block, blockOff, writeLen);

                    encryptPack(block, 0, dataLen.max(blockOff + writeLen));
                }

                offset += writeLen;
                count -= writeLen;

                actionPos += writeLen;
                actionLen = actionLen.max(actionPos);
            }
            writeBuff();

            streamPos = actionPos;
            streamLen = actionLen;
        }

读取数据:

public override int Read(byte[] dst, int offset, int count)
        {
            actionPos = streamPos;

            readBuff(count);
            int remain = count, dataLen, readLen;
            while (remain > 0)
            {
                if (actionPos >= streamLen)
                    break;
                dataLen = seekPack() - tagSize;
                readLen = remain.min(dataLen - blockOff);
                if (readLen <= 0)
                    throw new Error(this, "DataShort", remain, dataLen, blockOff);

                if (blockOff == 0 && dataLen <= remain)
                    decryptPack(dataLen, dst, offset);
                else
                {
                    decryptPack(dataLen, block);
                    Buffer.BlockCopy(block, blockOff, dst, offset, readLen);
                }

                offset += readLen;
                remain -= readLen;

                actionPos += readLen;
            }

            streamPos = actionPos;
            return count - remain;
        }

定位文件位置:

long streamPos = 0;
        public override long Position
        {
            get => streamPos;
            set
            {
                if (value < 0 || value > Length)
                    throw new IOException("pos out of range!");
                streamPos = value;
            }
        }

public override long Seek(long offset, SeekOrigin origin)
        {
            var pos = streamPos;
            switch (origin)
            {
                case SeekOrigin.Begin:
                    pos = offset;
                    break;
                case SeekOrigin.Current:
                    pos += offset;
                    break;
                case SeekOrigin.End:
                    pos = streamLen - offset;
                    break;
            }
            return Position = pos;
        }

AeadStream使用方法跟FileStream类似,都是Stream的标准读写方法。

完整代码:AeadStream.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using util.crypt;
using util.ext;

namespace util.rep.aead
{
    public class AeadStream : Stream
    {
        public const string Type = "aead";
        public const short Version = 1;
        public static byte[] KeyDomain = "file-key".utf8();

        public Stream fs;
        public AeadConf conf;

        public static long dataSize(long fileSize, AeadConf conf)
        {
            var packTotal = fileSize - headSize(conf);
            return (packTotal / conf.packSize()) * conf.BlockSize
                    + ((packTotal % conf.packSize()) - conf.tagSize()).max(0);
        }

        public static int headSize(AeadConf conf)
            => Type.Length + 2 + conf.FileIdSize + conf.nonceSize();

        byte[] fileId;
        byte[] nonce;
        public AeadStream create()
        {
            fileId = conf.FileIdSize.aesRnd();
            nonce = aeadEnc.NonceSize.aesRnd();

            var header = Type.utf8().merge(Version.bytes(), fileId, nonce);
            fs.write(header);

            return this;
        }

        public AeadStream open()
        {
            var header = new byte[prefix];
            if (fs.readFull(header) != header.Length
                || header.utf8(0, Type.Length) != Type)
                throw new Error(this, "InvalidType");
            var ver = header.i16(Type.Length);
            if (ver > Version)
                throw new Error(this, "InvalidVersion", ver);

            fileId = header.sub(Type.Length + 2, conf.FileIdSize);
            nonce = header.tail(aeadEnc.NonceSize);
            streamLen = dataSize(fs.Length, conf);

            return this;
        }

        int? pre;
        int prefix => (int)(pre ?? (pre = headSize(conf)));
        int packSize => blockSize + tagSize;
        int blockSize => conf.BlockSize;
        int tagSize => aeadEnc.TagSize;

        AeadCrypt ae;
        AeadCrypt aeadEnc => ae ?? (ae = conf.newCrypt());
        PackCrypt pke;
        PackCrypt packEnc => pke ?? (pke = new PackCrypt(aeadEnc.setKey(conf.deriveKey(KeyDomain.merge(fileId), aeadEnc.KeySize)), nonce));

        long actionPos;

        byte[] pk;
        byte[] pack => pk ?? (pk = new byte[packSize]);
        
        byte[] blk;
        byte[] block => blk ?? (blk = new byte[blockSize]);

        long blockIdx => actionPos / blockSize;
        int blockOff => (int)(actionPos % blockSize);

        byte[] buff;
        int buffLen;
        int buffPos;
        long buffIdx;
        void initBuff(int dataLen)
        {
            buffLen = ((blockOff + dataLen - 1) / blockSize + 1) * packSize;
            if (buff == null || buff.Length < buffLen)
                buff = new byte[buffLen];
            buffPos = 0;
            buffIdx = blockIdx;
        }

        void readBuff(int dataLen)
        {
            initBuff(dataLen);
            buffLen = readFile(buff, buffLen, buffIdx);
        }

        void writeBuff()
        {
            if (buffPos <= 0)
                return;
            seekFile(buffIdx);
            fs.Write(buff, 0, buffPos);
        }

        void seekFile(long packIdx)
            => fs.Position = prefix + packIdx * packSize;

        int readFile(byte[] dst, int dstLen, long packIdx)
        {
            seekFile(packIdx);
            return fs.readFull(dst, 0, dstLen);
        }

        int seekPack()
        {
            buffPos = (int)((blockIdx - buffIdx) * packSize);
            return packSize.min(buffLen - buffPos);
        }

        void decryptPack(int dataSize, byte[] dst, int dstOff = 0)
            => packEnc.decrypt(buff, buffPos, dataSize + tagSize, blockIdx, dst, dstOff);

        void encryptPack(byte[] src, int srcOff, int srcLen)
        {
            packEnc.encrypt(src, srcOff, srcLen, blockIdx, buff, buffPos);
            buffPos += srcLen + tagSize;
        }

        public override int Read(byte[] dst, int offset, int count)
        {
            actionPos = streamPos;

            readBuff(count);
            int remain = count, dataLen, readLen;
            while (remain > 0)
            {
                if (actionPos >= streamLen)
                    break;
                dataLen = seekPack() - tagSize;
                readLen = remain.min(dataLen - blockOff);
                if (readLen <= 0)
                    throw new Error(this, "DataShort", remain, dataLen, blockOff);

                if (blockOff == 0 && dataLen <= remain)
                    decryptPack(dataLen, dst, offset);
                else
                {
                    decryptPack(dataLen, block);
                    Buffer.BlockCopy(block, blockOff, dst, offset, readLen);
                }

                offset += readLen;
                remain -= readLen;

                actionPos += readLen;
            }

            streamPos = actionPos;
            return count - remain;
        }

        public override void Write(byte[] src, int offset, int count)
        {
            actionPos = streamPos;
            var actionLen = streamLen;

            initBuff(count);
            int writeLen;
            while (count > 0)
            {
                writeLen = count.min(blockSize - blockOff);
                if (blockOff == 0
                    && (writeLen == blockSize // overwrite whole block
                        || writeLen >= actionLen - actionPos)) // overwrite exist block
                {
                    encryptPack(src, offset, writeLen);
                }
                else
                {
                    var dataLen = readFile(pack, pack.Length, blockIdx) - tagSize;
                    if (dataLen != (actionLen - blockIdx * blockSize).min(blockSize))
                        throw new Error(this, "DataError", 
                                    dataLen, 
                                    actionLen - blockIdx * blockSize);

                    packEnc.decrypt(pack, 0, dataLen + tagSize, blockIdx, block);
                    Buffer.BlockCopy(src, offset, block, blockOff, writeLen);

                    encryptPack(block, 0, dataLen.max(blockOff + writeLen));
                }

                offset += writeLen;
                count -= writeLen;

                actionPos += writeLen;
                actionLen = actionLen.max(actionPos);
            }
            writeBuff();

            streamPos = actionPos;
            streamLen = actionLen;
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            var pos = streamPos;
            switch (origin)
            {
                case SeekOrigin.Begin:
                    pos = offset;
                    break;
                case SeekOrigin.Current:
                    pos += offset;
                    break;
                case SeekOrigin.End:
                    pos = streamLen - offset;
                    break;
            }
            return Position = pos;
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }

        public override bool CanRead => fs.CanRead;
        public override bool CanSeek => fs.CanSeek;
        public override bool CanWrite => fs.CanWrite;
        long streamLen = 0;
        public override long Length => streamLen;
        long streamPos = 0;
        public override long Position
        {
            get => streamPos;
            set
            {
                if (value < 0 || value > Length)
                    throw new IOException("pos out of range!");
                streamPos = value;
            }
        }
        public override void Flush() => fs.Flush();
        public override void Close()
        {
            fs?.Close();
            fs = null;
        }
    }
}
View Code

Github链接:

https://github.com/bsmith-zhao/vfs/blob/main/util/rep/aead/AeadStream.cs

其他参考:

https://github.com/bsmith-zhao/vfs/blob/main/util/rep/aead/AeadConf.cs

https://github.com/bsmith-zhao/vfs/tree/main/util/crypt

https://github.com/bsmith-zhao/vfs/tree/main/util/crypt/sodium

标签:加密,int,读写,writeLen,dataLen,conf,Aead,byte,public
From: https://www.cnblogs.com/bsmith/p/17766307.html

相关文章

  • ACCESS 混淆加密解密
    考虑到这样一个场景,程序只给用户使用到一定期限,如果用户没有新的KEY,将不能再使用程序.所以才有了下面这个想法.考虑不到位的地方,希望大家指正一.数据表内有两个字段,A存储着过期日期,B字段存储着用户登陆日期,这里要重点说一下,如果用户打开程序时,电脑上的日期大于B字段的日......
  • 文件目录加密方法
    文件目录加密指的是通过加密方法加密目录名称,但保留目录结构,通过加密的目录保存加密文件,进而保持整个目录的保密性。由于目录的特别,加密的方法需要满足以下要求:1,加密后的密文尽可能短,从而能加密较深的目录结构2,加密后的密文要能保持差异性,即目录名称间较小的差异,能产生较大的密......
  • access MD5加密
    PrivateConstBITS_TO_A_BYTE=8PrivateConstBYTES_TO_A_WORD=4PrivateConstBITS_TO_A_WORD=32Privatem_lOnBits(30)Privatem_l2Power(30)PrivateFunctionLShift(lValue,iShiftBits)IfiShiftBits=0ThenLShift=lValueExitFunction......
  • python项目加密
     https://blog.csdn.net/weixin_38649779/article/details/131911893 sourcedefenderhttps://dev.to/richard_scott/encryption-for-protecting-python-source-code-4ckg https://zhuanlan.zhihu.com/p/54297880https://github.com/echoechoin/spython/   https://......
  • https是如何实现加密http消息的?
    1、利于服务端证书公钥加密、服务端私钥解密得到会话密钥(使用证书使服务端也会话密钥)2、使用会话密钥-属于对称加密,用的是同一个密钥3、对称加密消息  ......
  • Springboot 加密方案探讨
    背景toB的本地化java应用程序,通常是部署在客户机器上,为了保护知识产权,我们需要将核心代码(例如Lience,Billing,Pay等)进行加密或混淆,防止使用jadx等工具轻易反编译。同时,为了更深层的保护程序,也要防止三方依赖细节被窥探;业界方案ProGuard简介:开源社区有名的免费混淆工具,......
  • postman如何使用md5 、base64加密传参
    使用CryptoJS库什么是CryptoJS?CryptoJS是一个纯JavaScript实现的加密库,提供了很多常见的加密算法和加密模式,例如AES、DES、TripleDES、MD5、SHA-1、SHA-256等。它支持的加密方式很全面,使用简便,而且在前端中使用也非常方便postman在Pre-requestScript中先进行加密,然后设置全局......
  • JS加密/解密之webpack打包代码逆向
    Webpack是一个强大的打包工具,能够将多个文件打包成一个或多个最终的文件。然而,将已经经过打包的代码还原回原始源代码并不是一件直接的事情,因为webpack打包的过程通常会对代码进行压缩、混淆和优化,丢失了部分变量名和代码结构的信息。因此,完全还原打包前的源码可能并不现实。但......
  • Exception in thread "main" java.security.InvalidKeyException: Wrong key size问题
    问题描述在Java里面使用DES加密算法,然后就爆出这个错误:问题解决换用了另外一种加密解密的函数:SecretKeySpec;即将原来的这种:换成了这种:我是觉得使用DES加密算法时,它一直显示key的字节长度不对,就想着换一种表述方式,又看到了别的友友的经验分享,就换成这样试了试(直接放进mai......
  • 在Eclipse将Java代码打包为jar用于jmeter BeanShell(HMAC_SHA1)加密
    Eclipse代码importjavax.crypto.Mac;importjavax.crypto.SecretKey;importjavax.crypto.spec.SecretKeySpec;importjava.nio.charset.StandardCharsets;importjava.security.InvalidKeyException;importjava.security.NoSuchAlgorithmException;publicclassHMAC_data......