2011-07-16 10 views
8

私は、安全でないチャンネルでデータを送受信するStreamを持っています。私は、チャネルの両方のエンドポイントに既にパスフレーズがあるという事前共有秘密を持っています。基礎となる.NETストリームの上に双方向の暗号化されたストリームを作成するには?

シークレットと元の安全でないストリームを使用して新しいストリームを構築したいと思います。私が遭遇した2つの問題は次のとおりです。

  • CryptoStreamは片方向です(読み取り専用または書き込み専用)。オリジナルのストリームの上に2つのストリーム(読み込みと書き込みストリーム)を作成できますが、これは受け入れられません。私が望むものを得るためにラッパー・ストリームを書く必要がありますか? (つまり、単一の読み取り/書き込みストリーム)

  • CryptoStreamはブロック単位で動作すると言われ、ブロックが完了するまで何も書き込むことはできません。理想的には、私は任意の量のデータを書いて、すぐに基礎となるストリーム(暗号化された)に送るようにしたいと思います。

これを達成する簡単な方法はありますか?私はSslStreamについて知っていますが、事前共有シークレットではなく、プライベート/公開鍵と証明書に合わせて調整されています。

+0

なぜ容認できない2つのストリームを作成していますか?このような状況では、私は通常、ここで起こっている2つの "セッション"に対して異なる暗号鍵を設定したいと思っています(キーとして 'HASH(パスフレーズ+ '||' +送信者名)を使用してください)。 –

+0

@Damien私はこのストリームの上にさらにストリームを重ねるので、それらのすべてを複製したくないからです。 –

+0

読み込みと書き込みをサポートする単一のストリームオブジェクトのセマンティクスが必要であるが(シークではないと思われますが)、その操作で渡された "ReadStream"または "WriteStream"書くために1ページ分のコードを取る。したがって、Cryptoクラスを使用してチャネルの両側に暗号化を追加し、この別のクラスを使用して上位クラスへの単一のストリームとして提示することができます。 –

答えて

5

私は2年後に戻って答えを受け入れることはできませんが、私はあなたが尋ねていることをやっただけですが、これはかなり一般的な問題だと思いますので、私はこれを投稿して、この質問の向こう側に。

GregSの情報を実装に取り​​入れました。具体的な目的のために、Initializeメソッドをコンストラクタにし、net & diffie-hellmanコードを取り除き、(生成されたキーの代わりに)Aesオブジェクトに事前共有キーを割り当てます。

AES126と似ているのにもかかわらず、AES256を使用していることに注意してください(あなたのキーは私の知る限りの実装不良のため関連しています)。 NSAが仕様を乱しているかどうかを知るためにNISTを信頼しない場合は、AESを使用しないでください。

また、これが出発点です! NETでNetworkStreamを介して暗号化されたデータを送信する際によく遭遇する問題を解決しようとしています。前置き百12行以下で

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()'と '.Dispose()'は良くありません。クラスのファイナライザが実行されているときに、3つの内部ストリームがまだファイナライズされていないことを保証することはできません(GCファイナライザはキューではなくヒープとして機能します)。標準のdisposeパターンに変更し、外側のストリームで明示的に呼び出された場合は、3つの内部ストリームに対して '.Dispose()'のみを呼び出す必要があります。 –

+0

確か... MSの廃棄パターンを反映するように更新されました。通常、私はストリームを管理されていないリソースとして扱いますが、明示的に行われた場合にのみストリームを廃棄することを提案しました。 – Vreenak

+1

あなたのクラスは抽象基本クラス 'Stream'を継承し、そのクラスはすでにdisposeパターンを実装しているので、' Dispose() 'や'〜CryptoNetworkStream() 'はあなたのクラスでは使用しないでください明示的に 'void Dispoese(bool)'を明示的にオーバーライドし、最後に基本クラスのバージョンを呼び出す必要があります)。私は訂正をしました。 –

2

IVを正しく送信して読み込む必要がありますが、「ブロック」制限を排除するために8ビットのフィードバックサイズを持つCFBモードでブロック暗号(AESなど)を使用できます。私はあなたが望む双方向の振る舞いを得るために独自の暗号ストリームを書かなければならないと思います。

ストリームの暗号化側では、ランダムIVを生成して最初に送信します。復号化側では、最初にストリームからIVバイトを読み込み、次にそれを使って暗号化変換を初期化し、ストリームから読み込んだ残りのバイトを暗号化変換に渡します。

Bouncycastle C# libraryを使用する場合は、SrpTlsClientクラスを使用して事前共有キーを使用してTLS/SSLに入ったすべてのセキュリティエンジニアリングと分析の利点を得ることができます。このクラスは、TLSにSRP ciphersuitesを実装します。

EDIT:SRP TLSについて

ネヴァーマインド、はBouncyCastleライブラリは唯一のプロトコルのクライアント側を持っています。残念な。

+0

+1 - > CFBとFeedbackSizeに関する情報をありがとう。 – Vreenak

関連する問題