.net - 如何在底层 .NET 流之上创建双向加密流?

标签 .net security cryptography

我有一个 Stream,它通过不安全的 channel 传输和接收数据。我有一个预先共享的 secret , channel 的两个端点都已经有了(比如密码)。

我想使用 secret 和原始不安全流构建一个新流。我遇到的两个问题是:

  • CryptoStream 是一种方式:只读或只写。我可以在原始 Stream 之上创建两个流(一个读取流和一个写入流),但这是 Not Acceptable 。我是否必须编写一个包装流才能得到我想要的? (即单个读/写流)
  • 据说 CryptoStream 在块中工作,并且在块完成之前可能不会向底层流写入任何内容。理想情况下,我想写入任意数量的数据并立即将其发送到底层流(加密)。

  • 有没有一种简单的方法来实现这一目标?我知道 SslStream,但它是为私钥/公钥和证书量身定制的,而不是预先共享的 secret 。

    最佳答案

    我怀疑你两年后会回来接受答案,但是我只是做了你所问的,我认为这是一个相当普遍的问题,所以我发布这个是为了其他可能遇到这个问题的人的利益。

    我将 GregS 的信息纳入了我的实现中。对于您的特定目的,您可以将 Initialize 方法作为您的构造函数,去除 net & diffie-hellman 代码,并将您的预共享 key 分配给 Aes 对象(而不是生成的 key )。

    请注意,我正在使用 AES256,尽管它已被妥协为与 AES128 具有相似的强度(如果您的 key 由于实现不当而相关,我不知道)。如果你不相信 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);
            }
        }
    }
    

    关于.net - 如何在底层 .NET 流之上创建双向加密流?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6717356/

    相关文章:

    .net - 如何使用批处理文件构建解决方案

    windows - 如何在不重启的情况下使用 SEE_MASK_NOZONECHECKS 运行 msi

    asp.net - 可以 asp :Button with Visible=False be submitted by a malicious user?

    java - 使用 PKIStatus 值验证 RFC 3161 时间戳响应

    c# - 使用循环限制并发异步请求

    .net - MessageInspector消息: "This message cannot support the operation because it has been copied."

    c# - 计时器委托(delegate)周围的内存泄漏

    c# - 在多个 .NET 应用程序中引用相同的连接字符串

    iOS SecKeyRef(公钥)发送到服务器

    java - 基于身份的加密和开源