0

I have created a stream that takes a plaintext input stream, and it's read method will return cypertext based on the input stream;

I've been reading that it is recommended to use the Rfc2898DeriveBytes class to generate the key, but I'm not confident on the correct usage here.

None of the current Microsoft examples around the AES class, for example, seem to talk about the recommended use using password-based key derivation such as what Rfc2898DeriveBytes provides.

Do I still have a key or do I use Rfc2898DeriveBytes to generate the key? and if so, do I need to store something additional along with the encrypted data so I can decrypt? I don't have a good mental model for Rfc2898DeriveBytes would be integrated with what I've built.

public class AesEncryptionStream : Stream { private Stream _output; public override bool CanRead => _output.CanRead; public override bool CanSeek => _output.CanSeek; public override bool CanWrite => _output.CanWrite; public override long Length => _output.Length; public override long Position { get => _output.Position; set => _output.Position = value; } public byte[] InitializationVector { get; private set; } private bool _writeIVToStream; public AesEncryptionStream(byte[] key, Stream input, bool writeIVToStream) { _writeIVToStream = writeIVToStream; _output = SetupCryptoStream(key,input); } private Stream SetupCryptoStream(byte[] key, Stream input) { var algoritm = this.CreateEncryptionAlgorithm(key.Length * 8); this.InitializationVector = this.GenerateInitializationVector(algoritm); var transform = this.GetTransform(algoritm, key); return new CryptoStream(input, transform, CryptoStreamMode.Read); } protected virtual ICryptoTransform GetTransform(SymmetricAlgorithm algorithm, byte[] key) { return algorithm.CreateEncryptor(key, this.InitializationVector); } protected virtual byte[] GenerateInitializationVector(SymmetricAlgorithm algorithm) { algorithm.GenerateIV(); return algorithm.IV; } protected SymmetricAlgorithm CreateEncryptionAlgorithm(int keySizeBits) { var encryptor = Aes.Create(); encryptor.KeySize = keySizeBits; return encryptor; } public override void Flush() { _output.Flush(); } private int _position = 0; /// <summary> /// Reads from the plaintext (decrypted) input stream, and outputs in cyphertext (ecrypted). /// If the stream was set to include the initialization vector in the stream, it will be returned first /// </summary> /// <param name="buffer"></param> /// <param name="offset"></param> /// <param name="count"></param> /// <returns></returns> public override int Read(byte[] buffer, int offset, int count) { int read = 0; if (_writeIVToStream) { //if we need to return the IV, or part of it if (_position < this.InitializationVector.Length) { var ivToCopy = Math.Min(this.InitializationVector.Length - _position, count); Buffer.BlockCopy(this.InitializationVector, _position, buffer, offset, ivToCopy); _position += ivToCopy; read = ivToCopy; } } //if we haven't read enough if (read < count) { read += _output.Read(buffer, offset + read, count - read); } return read; } public override long Seek(long offset, SeekOrigin origin) { return _output.Seek(offset, origin); } public override void SetLength(long value) { _output.SetLength(value); } public override void Write(byte[] buffer, int offset, int count) { _output.Write(buffer, offset, count); } } 
6
  • 1
    Sorry, @dr.null, your answer to that question does not incorporate a proper random salt and this is vulnerable to precomputation (aka rainbow table) attacks. Commented Jun 3, 2024 at 22:37
  • @PresidentJamesK.Polk Yes Mr. President. That's MRE that one must improve. Good point though. Commented Jun 3, 2024 at 22:56
  • 2
    This answer is a good template to go by. See if it makes sense to you. Commented Jun 3, 2024 at 22:58
  • The badly named Rfc2898DeriveBytes implements PBKDF2, i.e. the password based key derivation function 2 (it's badly named if just because the RFC also contains PBKDF1, so yeah...). You need it if you have a password. You'd need a 128 bit salt, a high iteration count, possibly a version number for your protocol and probably other measures to make sure that the password is not easy to crack. I'd always use an output stream for encryption and an input stream for decryption. I mean, you're not going to do anything special such as reading / writing objects with the ciphertext, right? Commented Jun 4, 2024 at 21:43
  • I'd also not make it seekable. Certainly not initially, the chances of that not breaking are about 0% unless you think out a strategy first. Commented Jun 4, 2024 at 21:45

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.