Kri*_*mar 8 c# cryptography stream
我试图使用带有AWS .NET SDk的CryptoStream失败,因为CryptoStream不支持seek.我知道内容长度已知的地方我们应该能够将这些功能添加到CryptoStream.我想知道怎么做; 任何示例代码也都很有用.
我有一个这样的方法,它与FieStream一起传递并返回一个cryptoStream.我将返回的Stream对象分配给AWS SDk PutObjectRequest对象的InputStream.
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 ;
}
Run Code Online (Sandbox Code Playgroud)
通常使用加密,输入字节和输出字节之间不存在1:1的映射,因此为了向后搜索(特别是),它必须做很多工作 - 甚至可能直接回到开始并向前移动处理数据以消耗解密流中的[n]个字节.即使它知道每个字节映射到的位置,加密的状态也依赖于它之前的数据(它不是解码器环; p),所以再次 - 它要么必须从头开始读取(和重置回初始化矢量),或者它必须跟踪位置和加密状态的快照,然后返回最近的快照,然后向前走.大量的工作和存储.
这也适用于相对于任何一端的寻求.
从当前位置向前移动不会太糟糕,但是你必须再次处理数据 - 而不仅仅是跳过基本流的位置.
没有一种很好的方法来实现这一点,大多数消费者可以使用 - 通常如果你true从那CanSeek意味着"随机访问",但在这种情况下效率不高.
作为解决方法 - 考虑将解密数据复制到MemoryStream文件或文件中; 然后您可以以随机访问方式访问完全解密的数据.
它是如此简单,只需根据流的位置(stream.Position)生成与数据大小相同的长密钥,然后使用ECB或您喜欢的任何其他加密方法,然后应用XOR。它是可搜索的,非常快的并且是一对一的加密,其输出长度与输入长度完全相同。它具有高效的内存功能,您可以在大型文件上使用它。我认为这种方法也用于现代WinZip AES加密中。您唯一要注意的是盐
对每个流使用唯一的盐,否则不进行加密。我没有做太多测试,但是如果您认为它有问题,请告诉我。
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]; //zero buffer is adequate since we have to use new salt for each stream
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);
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
static 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);
}
Run Code Online (Sandbox Code Playgroud)