2011-07-16 11 views
8

Güvenli olmayan bir kanal üzerinden veri ileten ve alan bir Akım var. Önceden paylaşılan bir sırrım var, her iki uç nokta zaten var (bir parola).Temel bir .NET Akışının üstünde çift yönlü şifreli bir akış nasıl oluşturulur?

Gizli ve özgün güvenli olmayan akışı kullanarak yeni bir akış oluşturmak istiyorum. Karşılaştığım iki sorun:

  • CryptoStream tek yönlüdür: salt okunur veya salt yazılır. Orijinal Akışımın üstünde iki akış (okuma ve yazma akışı) oluşturabilirim, ancak bu kabul edilemez. İstediğimi almak için bir sarıcı akışı yazmam gerekiyor mu? (yani, tek bir okuma/yazma akışı)

  • CryptoStream'in bloklarla çalıştığı söylenir ve blok tamamlanana kadar temel akışa hiçbir şey yazamaz. İdeal olarak, herhangi bir miktarda veri yazmak istiyorum ve derhal alttaki şifreye (şifreli) gönderilmesini istiyorum.

Bunu başarmanın kolay bir yolu var mı? SslStream'i biliyorum, ancak önceden paylaşılmış sırlardan ziyade özel/açık anahtarlara ve sertifikalara uyar.

+0

neden kabul edilemez iki akım yaratmaktadır: fazla uzatmadan yüz on iki satır ya da daha az

? Böyle bir durumda, genellikle burada gerçekleşen iki "oturum" için farklı şifreleme anahtarları ayarlamak isterdim (örneğin, anahtar olarak "HASH (passphrase + '||' + gönderici adı)" anahtarını kullan) –

+0

@Damien çünkü bunun üzerinde daha fazla akıntım var ve bunların hepsini çoğaltmak istemiyorum. –

+0

, Okuma ve Yazmayı destekleyen (ancak, büyük olasılıkla aramaz) tek bir akış nesnesinin semantiğine ihtiyacınız varsa, ancak bu işlemleri, yalnızca yapımında kendisine iletilmiş bir "ReadStream" veya "WriteStream" e dönüştürür. yazmak için kod veya benzeri bir sayfa alın. Böylece, şifrelemeyi kanalın her iki tarafına eklemek için Kripto sınıflarını kullanabilir ve daha sonra bu diğer sınıfı, daha yüksek katmanlara tek bir akış olarak sunmak için kullanabilirsiniz. –

cevap

5

İki yıl sonra geri gelip cevapları kabul edeceğinizden şüpheliyim, ancak ne soruyorsun diye sordum, ve bunun oldukça yaygın bir sorun olduğunu düşünüyorum, bu yüzden bunu başkalarının yararı için gönderiyorum bu soru boyunca.

GregS'in bilgilerini uygulamaya dahil ettim. Belirli bir amaç için, Initialize yöntemini yapıcınız yapar, net & diffie-hellman kodunu çıkarır ve önceden paylaşılmış anahtarınızı Aes nesnesine atar (oluşturulan anahtar yerine).

AES258'e (AES128'e benzer güçte olma tehlikesiyle karşı karşıya olmasına rağmen (AES256 kullanıyorum) (anahtarlarınız yanlış uygulama nedeniyle ilgiliyse, benimkileri bilmediğim için). NSA'nın kendi özellikleriyle uğraşıp karıştırılmadığını öğrenmek için NIST'e güvenmiyorsanız, AES'i KULLANMAYIN.

Ayrıca, bu bir başlangıç ​​noktasıdır! .NET'te bir NetworkStream üzerinden şifrelenmiş veri gönderme sık karşılaşılan sorun üzerinde çalışıyorum!

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); 
     } 
    } 
} 
+0

"CryptoNetworkStream()" ve ".Dispose()" işlevi iyi değil. Sınıfınızın finalleştiricisi çalıştırıldığında 3 iç akışın henüz tamamlanmamış olacağını garanti edemezsiniz (GC Finalizer sıra dışı bir yığın olarak çalışır). Standart atma düzenine değiştirilmeli ve yalnızca dış akışta çağrılırsa, sadece 3 iç akış üzerinde '.Dispose()' işlevini çağırmalısınız. –

+0

Elbette ... MS'in atma modelini yansıtacak şekilde güncellendi. Normal olarak akışları yönetilmeyen kaynaklar olarak ele alırdım, ancak açık bir şekilde yapıldığı takdirde bunları bertaraf etmeyi önerdin. – Vreenak

+1

Yine de doğru değildi, çünkü sınıfınız 'Temel Akışı' temel sınıfından miras kalıyor ve bu sınıf zaten imha modelini uyguladığı için, ne 'Dispose() ne de' CryptoNetworkStream() 'sınıfınızda kullanılmamalıdır. Ayrıca, geçersizliği geçersiz kılma (bool) 'u geçersiz kılmanız ve temel sınıfın sonundaki sürümünü çağırmanız gerekir. Düzeltmeleri yaptım. –

2

IV'leri doğru bir şekilde iletmeniz ve okumanız gerekir, ancak "bloklu" sınırlamayı ortadan kaldırmak için 8 bitlik geri besleme boyutuyla CFB modunda bir blok şifresini, örneğin AES'yi kullanabilirsiniz. İstediğiniz iki yönlü davranışı elde etmek için kendi kripto akışınızı yazmanız gerektiğini düşünüyorum.

Akışın şifreleme tarafında rastgele bir IV oluşturulur ve önce iletilir. Şifre çözme tarafında, önce akıştan IV baytları okuyun, ardından şifreli dönüştürmeyi başlatın ve daha sonra kripto dönüşümü aracılığıyla akıştan okunan kalan baytları iletin.

Bouncycastle C# library ürününü kullanmak isterseniz, SrpTlsClient sınıfını kullanarak önceden paylaşılan anahtarlar kullanarak TLS/SSL'ye giren tüm güvenlik mühendisliği ve analizinin avantajlarından yararlanabilirsiniz. Bu sınıf TLS'de SRP ciphersuites'u uygular.

DÜZENLEME:

, BouncyCastle kütüphane sadece protokolün istemci tarafı vardır Boşver SRP TLS hakkında

. Çok kötü.

+0

+1 -> CFB ve FeedbackSize ile ilgili bilgiler için teşekkürler. – Vreenak

İlgili konular