Dot-Net
如何在底層 .NET 流之上創建雙向加密流?
我有一個 Stream 通過不安全的通道傳輸和接收數據。我有一個預先共享的秘密,通道的兩個端點都已經擁有(比如密碼)。
我想使用秘密和原始不安全流建構一個新流。我遇到的兩個問題是:
- CryptoStream 是單向的:只讀或只寫。我可以在原始 Stream 之上創建*兩個流(一個讀取流和一個寫入流),但這是不可接受的。*我是否必須編寫一個包裝流來獲得我想要的東西?(即單個讀/寫流)
- 據說 CryptoStream 以塊的形式工作,並且在塊完成之前可能不會向底層流寫入任何內容。理想情況下,我想寫入任意數量的數據並立即將其發送到底層流(加密)。
有沒有一種簡單的方法可以實現這一目標?我知道 SslStream,但它是針對私鑰/公鑰和證書量身定制的,而不是預先共享的機密。
我懷疑兩年後你會回來接受答案,但是我只是做了你所問的,我認為這是一個相當普遍的問題,所以我發布這個是為了其他可能遇到這個問題的人的利益。
我將 GregS 的資訊合併到我的實現中。對於您的特定目的,您可以將 Initialize 方法設為您的建構子,去掉 net & diffie-hellman 程式碼,並將您的預共享密鑰分配給 Aes 對象(而不是生成的密鑰)。
請注意,我使用的是 AES256,儘管它已被損害為與 AES128 具有相似的強度(如果您的密鑰由於實施不當而相關,我不知道)。如果你不相信 NIST 知道 NSA 是否在弄亂他們的規範,那麼不要使用 AES。
此外,這是一個起點!我正在解決通過 .NET 中的 NetworkStream 發送加密數據的常見問題!
在一百十二行或更少的內容中,事不宜遲:
using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Security.Cryptography; namespace FullDuplexCrypto { class CryptoNetworkStream : Stream { public CryptoNetworkStream(IPAddress address, int port) { Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp); socket.Connect(address, port); //socket.NoDelay = true; Initialize(new NetworkStream(socket, true)); } public CryptoNetworkStream(Socket socket) { Initialize(new NetworkStream(socket, true)); } private void Initialize(Stream stream) { underlyer = stream; using(ECDiffieHellmanCng dh = new ECDiffieHellmanCng()) { dh.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; dh.HashAlgorithm = CngAlgorithm.Sha256; byte[] buffer = dh.PublicKey.ToByteArray(); underlyer.Write(buffer, 0, buffer.Length); underlyer.Read(buffer, 0, buffer.Length); using(Aes aes = Aes.Create()) { aes.KeySize = 256; aes.Key = dh.DeriveKeyMaterial(CngKey.Import(buffer, CngKeyBlobFormat.EccPublicBlob)); aes.FeedbackSize = 8; aes.Mode = CipherMode.CFB; underlyer.Write(aes.IV, 0, aes.IV.Length); encrypter = new CryptoStream(underlyer, aes.CreateEncryptor(), CryptoStreamMode.Write); underlyer.Read(aes.IV, 0, aes.IV.Length); decrypter = new CryptoStream(underlyer, aes.CreateDecryptor(), CryptoStreamMode.Read); } } } private Stream underlyer; private Stream encrypter; private Stream decrypter; public override bool CanRead { get { return decrypter.CanRead; } } public override bool CanWrite { get { return encrypter.CanWrite; } } public override bool CanSeek { get { return underlyer.CanSeek; } } public override long Length { get { return underlyer.Length; } } public override long Position { get { return underlyer.Position; } set { underlyer.Position = value; } } public override void Flush() { encrypter.Flush(); } public override int Read(byte[] buffer, int offset, int count) { return decrypter.Read(buffer, offset, count); } public override void Write(byte[] buffer, int offset, int count) { encrypter.Write(buffer, offset, count); } public override long Seek(long offset, SeekOrigin origin) { return underlyer.Seek(offset, origin); } public override void SetLength(long value) { underlyer.SetLength(value); } private bool isDisposed = false; protected override void Dispose(bool isDisposing) { if(!isDisposed) { if(isDisposing) { // Release managed resources. encrypter.Dispose(); decrypter.Dispose(); underlyer.Dispose(); } // Release unmanaged resources. isDisposed = true; } base.Dispose(isDisposing); } } }