C# 如何向CryptoStream添加搜索和定位功能
我试图在AWS.NET SDk中使用CryptoStream,但失败了,因为CryptoStream不支持seek。我在某个地方读到,内容长度已知,我们应该能够将这些功能添加到CryptoStream。我想知道如何做到这一点;任何示例代码都将非常有用 我有一个类似这样的方法,它通过FieStream传递并返回一个cryptoStream。我将返回的Stream对象分配给AWS SDk PutObjectRequest对象的InputStreamC# 如何向CryptoStream添加搜索和定位功能,c#,stream,cryptography,C#,Stream,Cryptography,我试图在AWS.NET SDk中使用CryptoStream,但失败了,因为CryptoStream不支持seek。我在某个地方读到,内容长度已知,我们应该能够将这些功能添加到CryptoStream。我想知道如何做到这一点;任何示例代码都将非常有用 我有一个类似这样的方法,它通过FieStream传递并返回一个cryptoStream。我将返回的Stream对象分配给AWS SDk PutObjectRequest对象的InputStream public static Stream GetE
public static Stream GetEncryptStream(Stream existingStream,
SymmetricAlgorithm cryptoServiceProvider,
string encryptionKey, string encryptionIV)
{
Stream existingStream = this.dataStream;
cryptoServiceProvider.Key = ASCIIEncoding.ASCII.GetBytes(encryptionKey);
cryptoServiceProvider.IV = ASCIIEncoding.ASCII.GetBytes(encryptionIV);
CryptoStream cryptoStream = new CryptoStream(existingStream,
cryptoServiceProvider.CreateEncryptor(), CryptoStreamMode.Read);
return cryptoStream ;
}
通常,使用加密时,输入字节和输出字节之间没有1:1的映射,因此为了向后(特别是)查找,它必须做大量的工作——甚至可能回到开始,向前处理数据,以消耗解密流中的[n]个字节。即使它知道每个字节映射到何处,加密的状态也取决于它之前的数据(它不是解码器环;p),因此,它必须从头开始读取(并重置回初始化向量),或者必须跟踪位置和加密状态的快照,然后返回最近的快照,然后向前走。大量的工作和存储 这也适用于相对两端的搜索 从当前位置向前移动不会太糟糕,但是您必须再次处理数据,而不仅仅是跳转基本流的位置 没有一种好的方法可以实现这一点,大多数消费者都可以使用——通常情况下,如果您从
CanSeek
获得true
,这意味着“随机访问”,但在这种情况下,这是不有效的
作为解决方案——考虑将解密后的数据复制到<代码>内存流< /代码>或文件中;然后,您可以以随机访问方式访问完全解密的数据。
作为标记Gravell答案的扩展,密码的可查找性取决于您使用的密码。大多数操作模式是不可查找的,因为每个密文块在某种程度上取决于前一个密文块。欧洲央行是可行的,但使用它几乎是一个普遍的坏主意。CTR模式是另一种可以随机访问的模式,CBC也是但是,所有这些模式都有其自身的漏洞,因此在选择之前,您应该仔细阅读并仔细考虑(最好咨询专家)。非常简单,只需根据流的位置(stream.position)生成与数据大小相同的长密钥即可使用ECB或任何其他您喜欢的加密方法,然后应用XOR。它是可查找的、非常快速的1对1加密,输出长度与输入长度完全相同。它的内存效率很高,您可以在大型文件上使用它。我认为现代WinZip AES加密中也使用了这种方法。你唯一需要注意的是盐 对每个流使用唯一的salt,否则没有加密
public class SeekableAesStream : Stream
{
private Stream baseStream;
private AesManaged aes;
private ICryptoTransform encryptor;
public bool autoDisposeBaseStream { get; set; } = true;
/// <param name="salt">//** WARNING **: MUST be unique for each stream otherwise there is NO security</param>
public SeekableAesStream(Stream baseStream, string password, byte[] salt)
{
this.baseStream = baseStream;
using (var key = new PasswordDeriveBytes(password, salt))
{
aes = new AesManaged();
aes.KeySize = 128;
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None;
aes.Key = key.GetBytes(aes.KeySize / 8);
aes.IV = new byte[16]; //useless for ECB
encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
}
}
private void cipher(byte[] buffer, int offset, int count, long streamPos)
{
//find block number
var blockSizeInByte = aes.BlockSize / 8;
var blockNumber = (streamPos / blockSizeInByte) + 1;
var keyPos = streamPos % blockSizeInByte;
//buffer
var outBuffer = new byte[blockSizeInByte];
var nonce = new byte[blockSizeInByte];
var init = false;
for (int i = offset; i < count; i++)
{
//encrypt the nonce to form next xor buffer (unique key)
if (!init || (keyPos % blockSizeInByte) == 0)
{
BitConverter.GetBytes(blockNumber).CopyTo(nonce, 0);
encryptor.TransformBlock(nonce, 0, nonce.Length, outBuffer, 0);
if (init) keyPos = 0;
init = true;
blockNumber++;
}
buffer[i] ^= outBuffer[keyPos]; //simple XOR with generated unique key
keyPos++;
}
}
public override bool CanRead { get { return baseStream.CanRead; } }
public override bool CanSeek { get { return baseStream.CanSeek; } }
public override bool CanWrite { get { return baseStream.CanWrite; } }
public override long Length { get { return baseStream.Length; } }
public override long Position { get { return baseStream.Position; } set { baseStream.Position = value; } }
public override void Flush() { baseStream.Flush(); }
public override void SetLength(long value) { baseStream.SetLength(value); }
public override long Seek(long offset, SeekOrigin origin) { return baseStream.Seek(offset, origin); }
public override int Read(byte[] buffer, int offset, int count)
{
var streamPos = Position;
var ret = baseStream.Read(buffer, offset, count);
cipher(buffer, offset, count, streamPos);
return ret;
}
public override void Write(byte[] buffer, int offset, int count)
{
cipher(buffer, offset, count, Position);
baseStream.Write(buffer, offset, count);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
encryptor?.Dispose();
aes?.Dispose();
if (autoDisposeBaseStream)
baseStream?.Dispose();
}
base.Dispose(disposing);
}
}
公共类seekableasstream:Stream
{
私有流基流;
私人管理的aes;
专用加密机;
公共bool autodisposebastream{get;set;}=true;
/////**警告**:每个流必须是唯一的,否则没有安全性
public-seekableastream(流基流,字符串密码,字节[]salt)
{
this.baseStream=baseStream;
使用(var key=newpasswordderivebytes(password,salt))
{
aes=新aes管理();
aes.KeySize=128;
aes.Mode=CipherMode.ECB;
aes.Padding=PaddingMode.None;
aes.Key=Key.GetBytes(aes.KeySize/8);
aes.IV=新字节[16];//对ECB无效
encryptor=aes.CreateEncryptor(aes.Key,aes.IV);
}
}
专用无效密码(字节[]缓冲区、整数偏移量、整数计数、长streamPos)
{
//查找块号
var blockSizeInByte=aes.BlockSize/8;
变量blockNumber=(streamPos/blockSizeInByte)+1;
var keyPos=streamPos%blockSizeInByte;
//缓冲区
var exputffer=新字节[blockSizeInByte];
var nonce=新字节[blockSizeInByte];
var init=false;
for(int i=偏移量;istatic void test()
{
var buf = new byte[255];
for (byte i = 0; i < buf.Length; i++)
buf[i] = i;
//encrypting
var uniqueSalt = new byte[16]; //** WARNING **: MUST be unique for each stream otherwise there is NO security
var baseStream = new MemoryStream();
var cryptor = new SeekableAesStream(baseStream, "password", uniqueSalt);
cryptor.Write(buf, 0, buf.Length);
//decrypting at position 200
cryptor.Position = 200;
var decryptedBuffer = new byte[50];
cryptor.Read(decryptedBuffer, 0, 50);
}