Changed to unix line endings
authorCarlos Guzmán Álvarez <carlos@mono-cvs.ximian.com>
Tue, 17 Feb 2004 16:13:36 +0000 (16:13 -0000)
committerCarlos Guzmán Álvarez <carlos@mono-cvs.ximian.com>
Tue, 17 Feb 2004 16:13:36 +0000 (16:13 -0000)
svn path=/trunk/mcs/; revision=23170

mcs/class/Mono.Security/Mono.Security.Protocol.Tls/SslClientStream.cs

index 5ce83543fe437b8a79cb1ecc8c989bc48c45f566..89e1f91ecad36902ec165557acbf30bb8b164c4f 100644 (file)
-/* Transport Security Layer (TLS)\r
- * Copyright (c) 2003-2004 Carlos Guzman Alvarez\r
- * \r
- * Permission is hereby granted, free of charge, to any person \r
- * obtaining a copy of this software and associated documentation \r
- * files (the "Software"), to deal in the Software without restriction, \r
- * including without limitation the rights to use, copy, modify, merge, \r
- * publish, distribute, sublicense, and/or sell copies of the Software, \r
- * and to permit persons to whom the Software is furnished to do so, \r
- * subject to the following conditions:\r
- * \r
- * The above copyright notice and this permission notice shall be included \r
- * in all copies or substantial portions of the Software.\r
- * \r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, \r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES \r
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT \r
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, \r
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER \r
- * DEALINGS IN THE SOFTWARE.\r
- */\r
-\r
-using System;\r
-using System.Collections;\r
-using System.IO;\r
-using System.Net;\r
-using System.Net.Sockets;\r
-using System.Security.Cryptography;\r
-using System.Security.Cryptography.X509Certificates;\r
-using System.Threading;\r
-\r
-using Mono.Security.Protocol.Tls.Alerts;\r
-using Mono.Security.Protocol.Tls.Handshake;\r
-using Mono.Security.Protocol.Tls.Handshake.Client;\r
-\r
-namespace Mono.Security.Protocol.Tls\r
-{\r
-       #region Delegates\r
-\r
-       public delegate bool CertificateValidationCallback(\r
-               X509Certificate certificate, \r
-               int[]                   certificateErrors);\r
-       \r
-       public delegate X509Certificate CertificateSelectionCallback(\r
-               X509CertificateCollection       clientCertificates, \r
-               X509Certificate                         serverCertificate, \r
-               string                                          targetHost, \r
-               X509CertificateCollection       serverRequestedCertificates);\r
-       \r
-       public delegate AsymmetricAlgorithm PrivateKeySelectionCallback(\r
-               X509Certificate clientCertificate, \r
-               string                  targetHost);\r
-\r
-       #endregion\r
-\r
-       public class SslClientStream : Stream, IDisposable\r
-       {\r
-               #region Events\r
-\r
-               public event TlsWarningAlertEventHandler WarningAlert;\r
-\r
-               #endregion\r
-\r
-               #region Internal Events\r
-               \r
-               internal event CertificateValidationCallback    ServerCertValidation;\r
-               internal event CertificateSelectionCallback             ClientCertSelection;\r
-               internal event PrivateKeySelectionCallback              PrivateKeySelection;\r
-               \r
-               #endregion\r
-\r
-               #region Fields\r
-\r
-               private CertificateValidationCallback   serverCertValidationDelegate;\r
-               private CertificateSelectionCallback    clientCertSelectionDelegate;\r
-               private PrivateKeySelectionCallback             privateKeySelectionDelegate;\r
-               private Stream                                                  innerStream;\r
-               private BufferedStream                                  inputBuffer;\r
-               private TlsContext                                              context;\r
-               private bool                                                    ownsStream;\r
-               private bool                                                    disposed;\r
-               private string                                                  read;\r
-               private string                                                  write;\r
-\r
-               #endregion\r
-\r
-               #region Properties\r
-\r
-               public override bool CanRead\r
-               {\r
-                       get { return this.innerStream.CanRead; }\r
-               }\r
-\r
-               public override bool CanSeek\r
-               {\r
-                       get { return false; }\r
-               }\r
-\r
-               public override bool CanWrite\r
-               {\r
-                       get { return this.innerStream.CanWrite; }\r
-               }\r
-\r
-               public override long Length\r
-               {\r
-                       get { throw new NotSupportedException(); }\r
-               }\r
-\r
-               public override long Position\r
-               {\r
-                       get { throw new NotSupportedException(); }\r
-                       set { throw new NotSupportedException(); }\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region Security Properties\r
-\r
-               public bool CheckCertRevocationStatus \r
-               {\r
-                       get { throw new NotImplementedException(); }\r
-                       set { throw new NotImplementedException(); }\r
-               }\r
-\r
-               public CipherAlgorithmType CipherAlgorithm \r
-               {\r
-                       get { return this.context.Cipher.CipherAlgorithmType;}\r
-               }\r
-               \r
-               public int CipherStrength \r
-               {\r
-                       get { return this.context.Cipher.EffectiveKeyBits;}\r
-               }\r
-               \r
-               public X509CertificateCollection ClientCertificates \r
-               {\r
-                       get { return this.context.ClientSettings.Certificates;}\r
-               }\r
-               \r
-               public HashAlgorithmType HashAlgorithm \r
-               {\r
-                       get { return this.context.Cipher.HashAlgorithmType; }\r
-               }\r
-               \r
-               public int HashStrength\r
-               {\r
-                       get { return this.context.Cipher.HashSize * 8; }\r
-               }\r
-               \r
-               public int KeyExchangeStrength \r
-               {\r
-                       get \r
-                       { \r
-                               return this.context.ServerSettings.Certificates[0].RSA.KeySize;\r
-                       }\r
-               }\r
-               \r
-               public ExchangeAlgorithmType KeyExchangeAlgorithm \r
-               {\r
-                       get { return this.context.Cipher.ExchangeAlgorithmType; }\r
-               }\r
-               \r
-               public SecurityProtocolType SecurityProtocol \r
-               {\r
-                       get { return this.context.Protocol; }\r
-               }\r
-               \r
-               public X509Certificate SelectedClientCertificate \r
-               {\r
-                       get { throw new NotImplementedException(); }\r
-               }\r
-\r
-               public X509Certificate ServerCertificate \r
-               {\r
-                       get { throw new NotImplementedException(); }\r
-               } \r
-\r
-               #endregion\r
-\r
-               #region Callback Properties\r
-\r
-               public CertificateValidationCallback ServerCertValidationDelegate\r
-               {\r
-                       get { return this.serverCertValidationDelegate; }\r
-                       set \r
-                       { \r
-                               if (this.ServerCertValidation != null)\r
-                               {\r
-                                       this.ServerCertValidation -= this.serverCertValidationDelegate;\r
-                               }\r
-                               this.serverCertValidationDelegate       = value;\r
-                               this.ServerCertValidation                       += this.serverCertValidationDelegate;\r
-                       }\r
-               }\r
-\r
-               public CertificateSelectionCallback ClientCertSelectionDelegate \r
-               {\r
-                       get { return this.clientCertSelectionDelegate; }\r
-                       set \r
-                       { \r
-                               if (this.ClientCertSelection != null)\r
-                               {\r
-                                       this.ClientCertSelection -= this.clientCertSelectionDelegate;\r
-                               }\r
-                               this.clientCertSelectionDelegate        = value;\r
-                               this.ClientCertSelection                        += this.clientCertSelectionDelegate;\r
-                       }\r
-               }\r
-\r
-               public PrivateKeySelectionCallback PrivateKeyCertSelectionDelegate \r
-               {\r
-                       get { return this.privateKeySelectionDelegate; }\r
-                       set \r
-                       { \r
-                               if (this.PrivateKeySelection != null)\r
-                               {\r
-                                       this.PrivateKeySelection -= this.privateKeySelectionDelegate;\r
-                               }\r
-                               this.privateKeySelectionDelegate        = value;\r
-                               this.PrivateKeySelection                        += this.privateKeySelectionDelegate;\r
-                       }\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region Constructors\r
-               \r
-               public SslClientStream(\r
-                       Stream  stream, \r
-                       string  targetHost, \r
-                       bool    ownsStream) \r
-                       : this(\r
-                               stream, targetHost, ownsStream, \r
-                               SecurityProtocolType.Default, null)\r
-               {\r
-               }\r
-               \r
-               public SslClientStream(\r
-                       Stream                          stream, \r
-                       string                          targetHost, \r
-                       X509Certificate         clientCertificate) \r
-                       : this(\r
-                               stream, targetHost, false, SecurityProtocolType.Default, \r
-                               new X509CertificateCollection(new X509Certificate[]{clientCertificate}))\r
-               {\r
-               }\r
-\r
-               public SslClientStream(\r
-                       Stream                                          stream,\r
-                       string                                          targetHost, \r
-                       X509CertificateCollection clientCertificates) : \r
-                       this(\r
-                               stream, targetHost, false, SecurityProtocolType.Default, \r
-                               clientCertificates)\r
-               {\r
-               }\r
-\r
-               public SslClientStream(\r
-                       Stream                                  stream,\r
-                       string                                  targetHost,\r
-                       bool                                    ownsStream,\r
-                       SecurityProtocolType    securityProtocolType) \r
-                       : this(\r
-                               stream, targetHost, ownsStream, securityProtocolType,\r
-                               new X509CertificateCollection())\r
-               {\r
-               }\r
-\r
-               public SslClientStream(\r
-                       Stream                                          stream,\r
-                       string                                          targetHost,\r
-                       bool                                            ownsStream,\r
-                       SecurityProtocolType            securityProtocolType,\r
-                       X509CertificateCollection       clientCertificates)\r
-               {\r
-                       if (stream == null)\r
-                       {\r
-                               throw new ArgumentNullException("stream is null.");\r
-                       }\r
-                       if (!stream.CanRead || !stream.CanWrite)\r
-                       {\r
-                               throw new ArgumentNullException("stream is not both readable and writable.");\r
-                       }\r
-                       if (targetHost == null || targetHost.Length == 0)\r
-                       {\r
-                               throw new ArgumentNullException("targetHost is null or an empty string.");\r
-                       }\r
-\r
-                       this.context            = new TlsContext(\r
-                               this,\r
-                               securityProtocolType, \r
-                               targetHost, \r
-                               clientCertificates);\r
-                       this.inputBuffer        = new BufferedStream(new MemoryStream());\r
-                       this.innerStream        = stream;\r
-                       this.ownsStream         = ownsStream;\r
-                       this.read                       = String.Empty;\r
-                       this.write                      = String.Empty;\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region Finalizer\r
-\r
-               ~SslClientStream()\r
-               {\r
-                       this.Dispose(false);\r
-               }\r
-\r
-               #endregion\r
-\r
-        #region IDisposable Methods\r
-\r
-               void IDisposable.Dispose()\r
-               {\r
-                       this.Dispose(true);\r
-                       GC.SuppressFinalize(this);\r
-               }\r
-\r
-               protected virtual void Dispose(bool disposing)\r
-               {\r
-                       if (!this.disposed)\r
-                       {\r
-                               if (disposing)\r
-                               {\r
-                                       if (this.innerStream != null)\r
-                                       {\r
-                                               if (this.context.HandshakeFinished)\r
-                                               {\r
-                                                       // Write close notify\r
-                                                       TlsCloseNotifyAlert alert = new TlsCloseNotifyAlert(this.context);\r
-                                                       this.SendAlert(alert);\r
-                                               }\r
-\r
-                                               if (this.ownsStream)\r
-                                               {\r
-                                                       // Close inner stream\r
-                                                       this.innerStream.Close();\r
-                                               }\r
-                                       }\r
-                                       this.ownsStream         = false;\r
-                                       this.innerStream        = null;\r
-                                       if (this.ClientCertSelection != null)\r
-                                       {\r
-                                               this.ClientCertSelection -= this.clientCertSelectionDelegate;\r
-                                       }\r
-                                       if (this.ServerCertValidation != null)\r
-                                       {\r
-                                               this.ServerCertValidation -= this.serverCertValidationDelegate;\r
-                                       }\r
-                                       this.serverCertValidationDelegate       = null;\r
-                                       this.clientCertSelectionDelegate        = null;\r
-                               }\r
-\r
-                               this.disposed = true;\r
-                       }\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region Methods\r
-\r
-               public override IAsyncResult BeginRead(\r
-                       byte[]                  buffer,\r
-                       int                             offset,\r
-                       int                             count,\r
-                       AsyncCallback   callback,\r
-                       object                  state)\r
-               {\r
-                       this.checkDisposed();\r
-                       \r
-                       if (buffer == null)\r
-                       {\r
-                               throw new ArgumentNullException("buffer is a null reference.");\r
-                       }\r
-                       if (offset < 0)\r
-                       {\r
-                               throw new ArgumentOutOfRangeException("offset is less than 0.");\r
-                       }\r
-                       if (offset > buffer.Length)\r
-                       {\r
-                               throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");\r
-                       }\r
-                       if (count < 0)\r
-                       {\r
-                               throw new ArgumentOutOfRangeException("count is less than 0.");\r
-                       }\r
-                       if (count > (buffer.Length - offset))\r
-                       {\r
-                               throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");\r
-                       }\r
-\r
-                       if (!this.context.HandshakeFinished)\r
-                       {\r
-                               this.doHandshake();     // Handshake negotiation\r
-                       }\r
-\r
-                       if (!Monitor.TryEnter(this.read))\r
-                       {\r
-                               throw new InvalidOperationException("A read operation is already in progress.");\r
-                       }\r
-\r
-                       IAsyncResult asyncResult;\r
-\r
-                       try\r
-                       {\r
-                               System.Threading.Monitor.Enter(this.read);\r
-\r
-                               // If actual buffer is full readed reset it\r
-                               if (this.inputBuffer.Position == this.inputBuffer.Length &&\r
-                                       this.inputBuffer.Length > 0)\r
-                               {\r
-                                       this.resetBuffer();\r
-                               }\r
-\r
-                               // Check if we have space in the middle buffer\r
-                               // if not Read next TLS record and update the inputBuffer\r
-                               while ((this.inputBuffer.Length - this.inputBuffer.Position) < count)\r
-                               {\r
-                                       // Read next record and write it into the inputBuffer\r
-                                       long    position        = this.inputBuffer.Position;                                    \r
-                                       byte[]  record          = this.receiveRecord();\r
-                                       \r
-                                       if (record != null && record.Length > 0)\r
-                                       {\r
-                                               // Write new data to the inputBuffer\r
-                                               this.inputBuffer.Seek(0, SeekOrigin.End);\r
-                                               this.inputBuffer.Write(record, 0, record.Length);\r
-\r
-                                               // Restore buffer position\r
-                                               this.inputBuffer.Seek(position, SeekOrigin.Begin);\r
-                                       }\r
-                                       else\r
-                                       {\r
-                                               if (record == null)\r
-                                               {\r
-                                                       break;\r
-                                               }\r
-                                       }\r
-\r
-                                       // TODO: Review if we need to check the Length\r
-                                       // property of the innerStream for other types\r
-                                       // of streams, to check that there are data available\r
-                                       // for read\r
-                                       if (this.innerStream is NetworkStream &&\r
-                                               !((NetworkStream)this.innerStream).DataAvailable)\r
-                                       {\r
-                                               break;\r
-                                       }\r
-                               }\r
-\r
-                               asyncResult = this.inputBuffer.BeginRead(\r
-                                       buffer, offset, count, callback, state);\r
-                       }\r
-                       catch (TlsException ex)\r
-                       {\r
-                               throw new IOException("The authentication or decryption has failed.", ex);\r
-                       }\r
-                       catch (Exception ex)\r
-                       {\r
-                               throw new IOException("IO exception during read.", ex);\r
-                       }\r
-                       finally\r
-                       {\r
-                               System.Threading.Monitor.Exit(this.read);\r
-                       }\r
-\r
-                       return asyncResult;\r
-               }\r
-\r
-               public override IAsyncResult BeginWrite(\r
-                       byte[]                  buffer,\r
-                       int                             offset,\r
-                       int                             count,\r
-                       AsyncCallback   callback,\r
-                       object                  state)\r
-               {\r
-                       this.checkDisposed();\r
-\r
-                       if (buffer == null)\r
-                       {\r
-                               throw new ArgumentNullException("buffer is a null reference.");\r
-                       }\r
-                       if (offset < 0)\r
-                       {\r
-                               throw new ArgumentOutOfRangeException("offset is less than 0.");\r
-                       }\r
-                       if (offset > buffer.Length)\r
-                       {\r
-                               throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");\r
-                       }\r
-                       if (count < 0)\r
-                       {\r
-                               throw new ArgumentOutOfRangeException("count is less than 0.");\r
-                       }\r
-                       if (count > (buffer.Length - offset))\r
-                       {\r
-                               throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");\r
-                       }\r
-\r
-                       if (!this.context.HandshakeFinished)\r
-                       {\r
-                               // Start handshake negotiation\r
-                               this.doHandshake();\r
-                       }\r
-\r
-                       if (!Monitor.TryEnter(this.write))\r
-                       {\r
-                               throw new InvalidOperationException("A write operation is already in progress.");\r
-                       }\r
-\r
-                       IAsyncResult asyncResult;\r
-\r
-                       try\r
-                       {\r
-                               Monitor.Enter(this.write);\r
-\r
-                               // Send the buffer as a TLS record\r
-                               byte[] record = this.encodeRecord(\r
-                                       TlsContentType.ApplicationData, buffer, offset, count);\r
-                               \r
-                               asyncResult = this.innerStream.BeginWrite(\r
-                                       record, 0, record.Length, callback, state);\r
-                       }\r
-                       catch (TlsException ex)\r
-                       {\r
-                               throw new IOException("The authentication or decryption has failed.", ex);\r
-                       }\r
-                       catch (Exception ex)\r
-                       {\r
-                               throw new IOException("IO exception during Write.", ex);\r
-                       }\r
-                       finally\r
-                       {\r
-                               Monitor.Exit(this.write);\r
-                       }                       \r
-\r
-                       return asyncResult;\r
-               }\r
-\r
-               public override int EndRead(IAsyncResult asyncResult)\r
-               {\r
-                       this.checkDisposed();\r
-\r
-                       if (asyncResult == null)\r
-                       {\r
-                               throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginRead.");\r
-                       }\r
-\r
-                       return this.inputBuffer.EndRead(asyncResult);\r
-               }\r
-\r
-               public override void EndWrite(IAsyncResult asyncResult)\r
-               {\r
-                       this.checkDisposed();\r
-\r
-                       if (asyncResult == null)\r
-                       {\r
-                               throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginRead.");\r
-                       }\r
-\r
-                       this.innerStream.EndWrite (asyncResult);\r
-               }\r
-\r
-               public override void Close()\r
-               {\r
-                       ((IDisposable)this).Dispose();\r
-               }\r
-\r
-               public override void Flush()\r
-               {\r
-                       this.checkDisposed();\r
-\r
-                       this.innerStream.Flush();\r
-               }\r
-\r
-               public int Read(byte[] buffer)\r
-               {\r
-                       return this.Read(buffer, 0, buffer.Length);\r
-               }\r
-\r
-               public override int Read(byte[] buffer, int offset, int count)\r
-               {\r
-                       IAsyncResult res = this.BeginRead(buffer, offset, count, null, null);\r
-\r
-                       return this.EndRead(res);\r
-               }\r
-\r
-               public override long Seek(long offset, SeekOrigin origin)\r
-               {\r
-                       throw new NotSupportedException();\r
-               }\r
-               \r
-               public override void SetLength(long value)\r
-               {\r
-                       throw new NotSupportedException();\r
-               }\r
-\r
-               public void Write(byte[] buffer)\r
-               {\r
-                       this.Write(buffer, 0, buffer.Length);\r
-               }\r
-\r
-               public override void Write(byte[] buffer, int offset, int count)\r
-               {\r
-                       IAsyncResult res = this.BeginWrite (buffer, offset, count, null, null);\r
-\r
-                       this.EndWrite(res);\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region Reveive Record Methods\r
-\r
-               private byte[] receiveRecord()\r
-               {\r
-                       if (this.context.ConnectionEnd)\r
-                       {\r
-                               throw this.context.CreateException("The session is finished and it's no longer valid.");\r
-                       }\r
-                       \r
-                       // Try to read the Record Content Type\r
-                       int type = this.innerStream.ReadByte();\r
-\r
-                       // There are no more data for read\r
-                       if (type == -1)\r
-                       {\r
-                               return null;\r
-                       }\r
-\r
-                       TlsContentType                  contentType     = (TlsContentType)type;\r
-                       SecurityProtocolType    protocol        = (SecurityProtocolType)this.ReadShort();\r
-                       short                                   length          = this.ReadShort();\r
-                       \r
-                       // Read Record data\r
-                       int             received        = 0;\r
-                       byte[]  buffer          = new byte[length];\r
-                       while (received != length)\r
-                       {\r
-                               received += this.innerStream.Read(\r
-                                       buffer, received, buffer.Length - received);\r
-                       }\r
-\r
-                       TlsStream message = new TlsStream(buffer);\r
-               \r
-                       // Check that the message has a valid protocol version\r
-                       if (protocol != this.context.Protocol)\r
-                       {\r
-                               throw this.context.CreateException("Invalid protocol version on message received from server");\r
-                       }\r
-\r
-                       // Decrypt message contents if needed\r
-                       if (contentType == TlsContentType.Alert && length == 2)\r
-                       {\r
-                       }\r
-                       else\r
-                       {\r
-                               if (this.context.IsActual &&\r
-                                       contentType != TlsContentType.ChangeCipherSpec)\r
-                               {\r
-                                       message = this.decryptRecordFragment(\r
-                                               contentType, \r
-                                               protocol,\r
-                                               message.ToArray());\r
-                               }\r
-                       }\r
-\r
-                       byte[] result = message.ToArray();\r
-\r
-                       // Process record\r
-                       switch (contentType)\r
-                       {\r
-                               case TlsContentType.Alert:\r
-                                       this.processAlert((TlsAlertLevel)message.ReadByte(),\r
-                                               (TlsAlertDescription)message.ReadByte());\r
-                                       break;\r
-\r
-                               case TlsContentType.ChangeCipherSpec:\r
-                                       // Reset sequence numbers\r
-                                       this.context.ReadSequenceNumber = 0;\r
-                                       break;\r
-\r
-                               case TlsContentType.ApplicationData:\r
-                                       break;\r
-\r
-                               case TlsContentType.Handshake:\r
-                                       while (!message.EOF)\r
-                                       {\r
-                                               this.processHandshakeMessage(message);\r
-                                       }\r
-                                       // Update handshakes of current messages\r
-                                       this.context.HandshakeMessages.Write(message.ToArray());\r
-                                       break;\r
-\r
-                               default:\r
-                                       throw this.context.CreateException("Unknown record received from server.");\r
-                       }\r
-\r
-                       return result;\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region Send Record Methods\r
-\r
-               internal void SendAlert(TlsAlert alert)\r
-               {                       \r
-                       // Write record\r
-                       this.sendRecord(TlsContentType.Alert, alert.ToArray());\r
-\r
-                       // Update session\r
-                       alert.Update();\r
-\r
-                       // Reset message contents\r
-                       alert.Reset();\r
-               }\r
-\r
-               private void sendChangeCipherSpec()\r
-               {\r
-                       // Send Change Cipher Spec message\r
-                       this.sendRecord(TlsContentType.ChangeCipherSpec, new byte[] {1});\r
-\r
-                       // Reset sequence numbers\r
-                       this.context.WriteSequenceNumber = 0;\r
-\r
-                       // Make the pending state to be the current state\r
-                       this.context.IsActual = true;\r
-\r
-                       // Send Finished message\r
-                       this.sendRecord(TlsHandshakeType.Finished);                     \r
-               }\r
-\r
-               private void sendRecord(TlsHandshakeType type)\r
-               {\r
-                       TlsHandshakeMessage msg = this.createClientHandshakeMessage(type);\r
-                       \r
-                       // Write record\r
-                       this.sendRecord(msg.ContentType, msg.EncodeMessage());\r
-\r
-                       // Update session\r
-                       msg.Update();\r
-\r
-                       // Reset message contents\r
-                       msg.Reset();\r
-               }\r
-\r
-               private void sendRecord(TlsContentType contentType, byte[] recordData)\r
-               {\r
-                       if (this.context.ConnectionEnd)\r
-                       {\r
-                               throw this.context.CreateException("The session is finished and it's no longer valid.");\r
-                       }\r
-\r
-                       byte[] record = this.encodeRecord(contentType, recordData);\r
-\r
-                       this.innerStream.Write(record, 0, record.Length);\r
-               }\r
-\r
-               private byte[] encodeRecord(TlsContentType contentType, byte[] recordData)\r
-               {\r
-                       return this.encodeRecord(\r
-                               contentType,\r
-                               recordData,\r
-                               0,\r
-                               recordData.Length);\r
-               }\r
-\r
-               private byte[] encodeRecord(\r
-                       TlsContentType  contentType, \r
-                       byte[]                  recordData,\r
-                       int                             offset,\r
-                       int                             count)\r
-               {\r
-                       if (this.context.ConnectionEnd)\r
-                       {\r
-                               throw this.context.CreateException("The session is finished and it's no longer valid.");\r
-                       }\r
-\r
-                       TlsStream record = new TlsStream();\r
-\r
-                       int     position = offset;\r
-\r
-                       while (position < ( offset + count ))\r
-                       {\r
-                               short   fragmentLength = 0;\r
-                               byte[]  fragment;\r
-\r
-                               if ((count - position) > TlsContext.MAX_FRAGMENT_SIZE)\r
-                               {\r
-                                       fragmentLength = TlsContext.MAX_FRAGMENT_SIZE;\r
-                               }\r
-                               else\r
-                               {\r
-                                       fragmentLength = (short)(count - position);\r
-                               }\r
-\r
-                               // Fill the fragment data\r
-                               fragment = new byte[fragmentLength];\r
-                               Buffer.BlockCopy(recordData, position, fragment, 0, fragmentLength);\r
-\r
-                               if (this.context.IsActual)\r
-                               {\r
-                                       // Encrypt fragment\r
-                                       fragment = this.encryptRecordFragment(contentType, fragment);\r
-                               }\r
-\r
-                               // Write tls message\r
-                               record.Write((byte)contentType);\r
-                               record.Write((short)this.context.Protocol);\r
-                               record.Write((short)fragment.Length);\r
-                               record.Write(fragment);\r
-\r
-                               // Update buffer position\r
-                               position += fragmentLength;\r
-                       }\r
-\r
-                       return record.ToArray();\r
-               }\r
-               \r
-               #endregion\r
-\r
-               #region Cryptography Methods\r
-\r
-               private byte[] encryptRecordFragment(\r
-                       TlsContentType  contentType, \r
-                       byte[]                  fragment)\r
-               {\r
-                       // Calculate message MAC\r
-                       byte[] mac      = this.context.Cipher.ComputeClientRecordMAC(contentType, fragment);\r
-\r
-                       // Encrypt the message\r
-                       byte[] ecr = this.context.Cipher.EncryptRecord(fragment, mac);\r
-\r
-                       // Set new IV\r
-                       if (this.context.Cipher.CipherMode == CipherMode.CBC)\r
-                       {\r
-                               byte[] iv = new byte[this.context.Cipher.IvSize];\r
-                               System.Array.Copy(ecr, ecr.Length - iv.Length, iv, 0, iv.Length);\r
-                               this.context.Cipher.UpdateClientCipherIV(iv);\r
-                       }\r
-\r
-                       // Update sequence number\r
-                       this.context.WriteSequenceNumber++;\r
-\r
-                       return ecr;\r
-               }\r
-\r
-               private TlsStream decryptRecordFragment(\r
-                       TlsContentType                  contentType, \r
-                       SecurityProtocolType    protocol,\r
-                       byte[]                                  fragment)\r
-               {\r
-                       byte[]  dcrFragment     = null;\r
-                       byte[]  dcrMAC          = null;\r
-\r
-                       // Decrypt message\r
-                       this.context.Cipher.DecryptRecord(fragment, ref dcrFragment, ref dcrMAC);\r
-\r
-                       // Set new IV\r
-                       if (this.context.Cipher.CipherMode == CipherMode.CBC)\r
-                       {\r
-                               byte[] iv = new byte[this.context.Cipher.IvSize];\r
-                               System.Array.Copy(fragment, fragment.Length - iv.Length, iv, 0, iv.Length);\r
-                               this.context.Cipher.UpdateServerCipherIV(iv);\r
-                       }\r
-                       \r
-                       // Check MAC code\r
-                       byte[] mac = this.context.Cipher.ComputeServerRecordMAC(contentType, dcrFragment);\r
-\r
-                       // Check that the mac is correct\r
-                       if (mac.Length != dcrMAC.Length)\r
-                       {\r
-                               throw new TlsException("Invalid MAC received from server.");\r
-                       }\r
-                       for (int i = 0; i < mac.Length; i++)\r
-                       {\r
-                               if (mac[i] != dcrMAC[i])\r
-                               {\r
-                                       throw new TlsException("Invalid MAC received from server.");\r
-                               }\r
-                       }\r
-\r
-                       // Update sequence number\r
-                       this.context.ReadSequenceNumber++;\r
-\r
-                       return new TlsStream(dcrFragment);\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region Handshake Processing Methods\r
-\r
-               private void processHandshakeMessage(TlsStream handMsg)\r
-               {\r
-                       TlsHandshakeType        handshakeType   = (TlsHandshakeType)handMsg.ReadByte();\r
-                       TlsHandshakeMessage     message                 = null;\r
-\r
-                       // Read message length\r
-                       int length = handMsg.ReadInt24();\r
-\r
-                       // Read message data\r
-                       byte[] data = new byte[length];\r
-                       handMsg.Read(data, 0, length);\r
-\r
-                       // Create and process the server message\r
-                       message = this.createServerHandshakeMessage(handshakeType, data);\r
-\r
-                       // Update session\r
-                       if (message != null)\r
-                       {\r
-                               message.Update();\r
-                       }\r
-               }\r
-\r
-               private void processAlert(\r
-                       TlsAlertLevel           alertLevel, \r
-                       TlsAlertDescription alertDesc)\r
-               {\r
-                       switch (alertLevel)\r
-                       {\r
-                               case TlsAlertLevel.Fatal:\r
-                                       throw this.context.CreateException(alertLevel, alertDesc);                                      \r
-\r
-                               case TlsAlertLevel.Warning:\r
-                               default:\r
-                               switch (alertDesc)\r
-                               {\r
-                                       case TlsAlertDescription.CloseNotify:\r
-                                               this.context.ConnectionEnd = true;\r
-                                               break;\r
-\r
-                                       default:\r
-                                               this.RaiseWarningAlert(alertLevel, alertDesc);\r
-                                               break;\r
-                               }\r
-                               break;\r
-                       }\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region Misc Methods\r
-\r
-               private void resetBuffer()\r
-               {\r
-                       this.inputBuffer.SetLength(0);\r
-                       this.inputBuffer.Position = 0;\r
-               }\r
-\r
-               private short ReadShort()\r
-               {\r
-                       byte[] b = new byte[2];\r
-                       this.innerStream.Read(b, 0, b.Length);\r
-\r
-                       short val = BitConverter.ToInt16(b, 0);\r
-\r
-                       return System.Net.IPAddress.HostToNetworkOrder(val);\r
-               }\r
-\r
-               private void checkDisposed()\r
-               {\r
-                       if (this.disposed)\r
-                       {\r
-                               throw new ObjectDisposedException("The SslClientStream is closed.");\r
-                       }\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region Handsake Methods\r
-\r
-               /*\r
-                       Client                                                                                  Server\r
-\r
-                       ClientHello                 -------->\r
-                                                                                                                       ServerHello\r
-                                                                                                                       Certificate*\r
-                                                                                                                       ServerKeyExchange*\r
-                                                                                                                       CertificateRequest*\r
-                                                                               <--------                       ServerHelloDone\r
-                       Certificate*\r
-                       ClientKeyExchange\r
-                       CertificateVerify*\r
-                       [ChangeCipherSpec]\r
-                       Finished                    -------->\r
-                                                                                                                       [ChangeCipherSpec]\r
-                                                                               <--------           Finished\r
-                       Application Data            <------->                   Application Data\r
-\r
-                                       Fig. 1 - Message flow for a full handshake              \r
-               */\r
-\r
-               private void doHandshake()\r
-               {\r
-                       // Obtain supported cipher suite collection\r
-                       this.context.SupportedCiphers = TlsCipherSuiteFactory.GetSupportedCiphers(context.Protocol);\r
-\r
-                       // Send client hello\r
-                       this.sendRecord(TlsHandshakeType.ClientHello);\r
-\r
-                       // Read server response\r
-                       while (!this.context.HelloDone)\r
-                       {\r
-                               // Read next record\r
-                               this.receiveRecord();\r
-                       }\r
-                       \r
-                       // Send client certificate if requested\r
-                       if (this.context.ServerSettings.CertificateRequest)\r
-                       {\r
-                               this.sendRecord(TlsHandshakeType.Certificate);\r
-                       }\r
-\r
-                       // Send Client Key Exchange\r
-                       this.sendRecord(TlsHandshakeType.ClientKeyExchange);\r
-\r
-                       // Now initialize session cipher with the generated keys\r
-                       this.context.Cipher.InitializeCipher();\r
-\r
-                       // Send certificate verify if requested\r
-                       if (this.context.ServerSettings.CertificateRequest)\r
-                       {\r
-                               this.sendRecord(TlsHandshakeType.CertificateVerify);\r
-                       }\r
-\r
-                       // Send Cipher Spec protocol\r
-                       this.sendChangeCipherSpec();                    \r
-                       \r
-                       // Read record until server finished is received\r
-                       while (!this.context.HandshakeFinished)\r
-                       {\r
-                               // If all goes well this will process messages:\r
-                               //              Change Cipher Spec\r
-                               //              Server finished\r
-                               this.receiveRecord();\r
-                       }\r
-\r
-                       // Clear Key Info\r
-                       this.context.ClearKeyInfo();\r
-               }\r
-               \r
-               private TlsHandshakeMessage createClientHandshakeMessage(TlsHandshakeType type)\r
-               {\r
-                       switch (type)\r
-                       {\r
-                               case TlsHandshakeType.ClientHello:\r
-                                       return new TlsClientHello(this.context);\r
-\r
-                               case TlsHandshakeType.Certificate:\r
-                                       return new TlsClientCertificate(this.context);\r
-\r
-                               case TlsHandshakeType.ClientKeyExchange:\r
-                                       return new TlsClientKeyExchange(this.context);\r
-\r
-                               case TlsHandshakeType.CertificateVerify:\r
-                                       return new TlsClientCertificateVerify(this.context);\r
-\r
-                               case TlsHandshakeType.Finished:\r
-                                       return new TlsClientFinished(this.context);\r
-\r
-                               default:\r
-                                       throw new InvalidOperationException("Unknown client handshake message type: " + type.ToString() );\r
-                       }\r
-               }\r
-\r
-               private TlsHandshakeMessage createServerHandshakeMessage(TlsHandshakeType type, byte[] buffer)\r
-               {\r
-                       switch (type)\r
-                       {\r
-                               case TlsHandshakeType.HelloRequest:\r
-                                       this.sendRecord(TlsHandshakeType.ClientHello);\r
-                                       return null;\r
-\r
-                               case TlsHandshakeType.ServerHello:\r
-                                       return new TlsServerHello(this.context, buffer);\r
-\r
-                               case TlsHandshakeType.Certificate:\r
-                                       return new TlsServerCertificate(this.context, buffer);\r
-\r
-                               case TlsHandshakeType.ServerKeyExchange:\r
-                                       return new TlsServerKeyExchange(this.context, buffer);\r
-\r
-                               case TlsHandshakeType.CertificateRequest:\r
-                                       return new TlsServerCertificateRequest(this.context, buffer);\r
-\r
-                               case TlsHandshakeType.ServerHelloDone:\r
-                                       return new TlsServerHelloDone(this.context, buffer);\r
-\r
-                               case TlsHandshakeType.Finished:\r
-                                       return new TlsServerFinished(this.context, buffer);\r
-\r
-                               default:\r
-                                       throw this.context.CreateException("Unknown server handshake message received ({0})", type.ToString());\r
-                       }\r
-               }\r
-\r
-               #endregion\r
-\r
-               #region Event Methods\r
-\r
-               internal void RaiseWarningAlert(\r
-                       TlsAlertLevel           level, \r
-                       TlsAlertDescription description)\r
-               {\r
-                       if (WarningAlert != null)\r
-                       {\r
-                               WarningAlert(this, new TlsWarningAlertEventArgs(level, description));\r
-                       }\r
-               }\r
-\r
-               internal bool RaiseServerCertificateValidation(\r
-                       X509Certificate certificate, \r
-                       int[]                   certificateErrors)\r
-               {\r
-                       if (this.ServerCertValidation != null)\r
-                       {\r
-                               return this.ServerCertValidation(certificate, certificateErrors);\r
-                       }\r
-\r
-                       return false;\r
-               }\r
-\r
-               internal X509Certificate RaiseClientCertificateSelection(\r
-                       X509CertificateCollection       clientCertificates, \r
-                       X509Certificate                         serverCertificate, \r
-                       string                                          targetHost, \r
-                       X509CertificateCollection       serverRequestedCertificates)\r
-               {\r
-                       if (this.ClientCertSelection != null)\r
-                       {\r
-                               return this.ClientCertSelection(\r
-                                       clientCertificates,\r
-                                       serverCertificate,\r
-                                       targetHost,\r
-                                       serverRequestedCertificates);\r
-                       }\r
-\r
-                       return null;\r
-               }\r
-\r
-               internal AsymmetricAlgorithm RaisePrivateKeySelection(\r
-                       X509Certificate clientCertificate, \r
-                       string                  targetHost)\r
-               {\r
-                       if (this.PrivateKeySelection != null)\r
-                       {\r
-                               return this.PrivateKeySelection(\r
-                                       clientCertificate,\r
-                                       targetHost);\r
-                       }\r
-\r
-                       return null;\r
-               }\r
-\r
-               #endregion\r
-       }\r
-}\r
+/* Transport Security Layer (TLS)
+ * Copyright (c) 2003-2004 Carlos Guzman Alvarez
+ * 
+ * Permission is hereby granted, free of charge, to any person 
+ * obtaining a copy of this software and associated documentation 
+ * files (the "Software"), to deal in the Software without restriction, 
+ * including without limitation the rights to use, copy, modify, merge, 
+ * publish, distribute, sublicense, and/or sell copies of the Software, 
+ * and to permit persons to whom the Software is furnished to do so, 
+ * subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included 
+ * in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+using System;
+using System.Collections;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+
+using Mono.Security.Protocol.Tls.Alerts;
+using Mono.Security.Protocol.Tls.Handshake;
+using Mono.Security.Protocol.Tls.Handshake.Client;
+
+namespace Mono.Security.Protocol.Tls
+{
+       #region Delegates
+
+       public delegate bool CertificateValidationCallback(
+               X509Certificate certificate, 
+               int[]                   certificateErrors);
+       
+       public delegate X509Certificate CertificateSelectionCallback(
+               X509CertificateCollection       clientCertificates, 
+               X509Certificate                         serverCertificate, 
+               string                                          targetHost, 
+               X509CertificateCollection       serverRequestedCertificates);
+       
+       public delegate AsymmetricAlgorithm PrivateKeySelectionCallback(
+               X509Certificate clientCertificate, 
+               string                  targetHost);
+
+       #endregion
+
+       public class SslClientStream : Stream, IDisposable
+       {
+               #region Events
+
+               public event TlsWarningAlertEventHandler WarningAlert;
+
+               #endregion
+
+               #region Internal Events
+               
+               internal event CertificateValidationCallback    ServerCertValidation;
+               internal event CertificateSelectionCallback             ClientCertSelection;
+               internal event PrivateKeySelectionCallback              PrivateKeySelection;
+               
+               #endregion
+
+               #region Fields
+
+               private CertificateValidationCallback   serverCertValidationDelegate;
+               private CertificateSelectionCallback    clientCertSelectionDelegate;
+               private PrivateKeySelectionCallback             privateKeySelectionDelegate;
+               private Stream                                                  innerStream;
+               private BufferedStream                                  inputBuffer;
+               private TlsContext                                              context;
+               private bool                                                    ownsStream;
+               private bool                                                    disposed;
+               private string                                                  read;
+               private string                                                  write;
+
+               #endregion
+
+               #region Properties
+
+               public override bool CanRead
+               {
+                       get { return this.innerStream.CanRead; }
+               }
+
+               public override bool CanSeek
+               {
+                       get { return false; }
+               }
+
+               public override bool CanWrite
+               {
+                       get { return this.innerStream.CanWrite; }
+               }
+
+               public override long Length
+               {
+                       get { throw new NotSupportedException(); }
+               }
+
+               public override long Position
+               {
+                       get { throw new NotSupportedException(); }
+                       set { throw new NotSupportedException(); }
+               }
+
+               #endregion
+
+               #region Security Properties
+
+               public bool CheckCertRevocationStatus 
+               {
+                       get { throw new NotImplementedException(); }
+                       set { throw new NotImplementedException(); }
+               }
+
+               public CipherAlgorithmType CipherAlgorithm 
+               {
+                       get { return this.context.Cipher.CipherAlgorithmType;}
+               }
+               
+               public int CipherStrength 
+               {
+                       get { return this.context.Cipher.EffectiveKeyBits;}
+               }
+               
+               public X509CertificateCollection ClientCertificates 
+               {
+                       get { return this.context.ClientSettings.Certificates;}
+               }
+               
+               public HashAlgorithmType HashAlgorithm 
+               {
+                       get { return this.context.Cipher.HashAlgorithmType; }
+               }
+               
+               public int HashStrength
+               {
+                       get { return this.context.Cipher.HashSize * 8; }
+               }
+               
+               public int KeyExchangeStrength 
+               {
+                       get 
+                       { 
+                               return this.context.ServerSettings.Certificates[0].RSA.KeySize;
+                       }
+               }
+               
+               public ExchangeAlgorithmType KeyExchangeAlgorithm 
+               {
+                       get { return this.context.Cipher.ExchangeAlgorithmType; }
+               }
+               
+               public SecurityProtocolType SecurityProtocol 
+               {
+                       get { return this.context.Protocol; }
+               }
+               
+               public X509Certificate SelectedClientCertificate 
+               {
+                       get { throw new NotImplementedException(); }
+               }
+
+               public X509Certificate ServerCertificate 
+               {
+                       get { throw new NotImplementedException(); }
+               } 
+
+               #endregion
+
+               #region Callback Properties
+
+               public CertificateValidationCallback ServerCertValidationDelegate
+               {
+                       get { return this.serverCertValidationDelegate; }
+                       set 
+                       { 
+                               if (this.ServerCertValidation != null)
+                               {
+                                       this.ServerCertValidation -= this.serverCertValidationDelegate;
+                               }
+                               this.serverCertValidationDelegate       = value;
+                               this.ServerCertValidation                       += this.serverCertValidationDelegate;
+                       }
+               }
+
+               public CertificateSelectionCallback ClientCertSelectionDelegate 
+               {
+                       get { return this.clientCertSelectionDelegate; }
+                       set 
+                       { 
+                               if (this.ClientCertSelection != null)
+                               {
+                                       this.ClientCertSelection -= this.clientCertSelectionDelegate;
+                               }
+                               this.clientCertSelectionDelegate        = value;
+                               this.ClientCertSelection                        += this.clientCertSelectionDelegate;
+                       }
+               }
+
+               public PrivateKeySelectionCallback PrivateKeyCertSelectionDelegate 
+               {
+                       get { return this.privateKeySelectionDelegate; }
+                       set 
+                       { 
+                               if (this.PrivateKeySelection != null)
+                               {
+                                       this.PrivateKeySelection -= this.privateKeySelectionDelegate;
+                               }
+                               this.privateKeySelectionDelegate        = value;
+                               this.PrivateKeySelection                        += this.privateKeySelectionDelegate;
+                       }
+               }
+
+               #endregion
+
+               #region Constructors
+               
+               public SslClientStream(
+                       Stream  stream, 
+                       string  targetHost, 
+                       bool    ownsStream) 
+                       : this(
+                               stream, targetHost, ownsStream, 
+                               SecurityProtocolType.Default, null)
+               {
+               }
+               
+               public SslClientStream(
+                       Stream                          stream, 
+                       string                          targetHost, 
+                       X509Certificate         clientCertificate) 
+                       : this(
+                               stream, targetHost, false, SecurityProtocolType.Default, 
+                               new X509CertificateCollection(new X509Certificate[]{clientCertificate}))
+               {
+               }
+
+               public SslClientStream(
+                       Stream                                          stream,
+                       string                                          targetHost, 
+                       X509CertificateCollection clientCertificates) : 
+                       this(
+                               stream, targetHost, false, SecurityProtocolType.Default, 
+                               clientCertificates)
+               {
+               }
+
+               public SslClientStream(
+                       Stream                                  stream,
+                       string                                  targetHost,
+                       bool                                    ownsStream,
+                       SecurityProtocolType    securityProtocolType) 
+                       : this(
+                               stream, targetHost, ownsStream, securityProtocolType,
+                               new X509CertificateCollection())
+               {
+               }
+
+               public SslClientStream(
+                       Stream                                          stream,
+                       string                                          targetHost,
+                       bool                                            ownsStream,
+                       SecurityProtocolType            securityProtocolType,
+                       X509CertificateCollection       clientCertificates)
+               {
+                       if (stream == null)
+                       {
+                               throw new ArgumentNullException("stream is null.");
+                       }
+                       if (!stream.CanRead || !stream.CanWrite)
+                       {
+                               throw new ArgumentNullException("stream is not both readable and writable.");
+                       }
+                       if (targetHost == null || targetHost.Length == 0)
+                       {
+                               throw new ArgumentNullException("targetHost is null or an empty string.");
+                       }
+
+                       this.context            = new TlsContext(
+                               this,
+                               securityProtocolType, 
+                               targetHost, 
+                               clientCertificates);
+                       this.inputBuffer        = new BufferedStream(new MemoryStream());
+                       this.innerStream        = stream;
+                       this.ownsStream         = ownsStream;
+                       this.read                       = String.Empty;
+                       this.write                      = String.Empty;
+               }
+
+               #endregion
+
+               #region Finalizer
+
+               ~SslClientStream()
+               {
+                       this.Dispose(false);
+               }
+
+               #endregion
+
+        #region IDisposable Methods
+
+               void IDisposable.Dispose()
+               {
+                       this.Dispose(true);
+                       GC.SuppressFinalize(this);
+               }
+
+               protected virtual void Dispose(bool disposing)
+               {
+                       if (!this.disposed)
+                       {
+                               if (disposing)
+                               {
+                                       if (this.innerStream != null)
+                                       {
+                                               if (this.context.HandshakeFinished)
+                                               {
+                                                       // Write close notify
+                                                       TlsCloseNotifyAlert alert = new TlsCloseNotifyAlert(this.context);
+                                                       this.SendAlert(alert);
+                                               }
+
+                                               if (this.ownsStream)
+                                               {
+                                                       // Close inner stream
+                                                       this.innerStream.Close();
+                                               }
+                                       }
+                                       this.ownsStream         = false;
+                                       this.innerStream        = null;
+                                       if (this.ClientCertSelection != null)
+                                       {
+                                               this.ClientCertSelection -= this.clientCertSelectionDelegate;
+                                       }
+                                       if (this.ServerCertValidation != null)
+                                       {
+                                               this.ServerCertValidation -= this.serverCertValidationDelegate;
+                                       }
+                                       this.serverCertValidationDelegate       = null;
+                                       this.clientCertSelectionDelegate        = null;
+                               }
+
+                               this.disposed = true;
+                       }
+               }
+
+               #endregion
+
+               #region Methods
+
+               public override IAsyncResult BeginRead(
+                       byte[]                  buffer,
+                       int                             offset,
+                       int                             count,
+                       AsyncCallback   callback,
+                       object                  state)
+               {
+                       this.checkDisposed();
+                       
+                       if (buffer == null)
+                       {
+                               throw new ArgumentNullException("buffer is a null reference.");
+                       }
+                       if (offset < 0)
+                       {
+                               throw new ArgumentOutOfRangeException("offset is less than 0.");
+                       }
+                       if (offset > buffer.Length)
+                       {
+                               throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
+                       }
+                       if (count < 0)
+                       {
+                               throw new ArgumentOutOfRangeException("count is less than 0.");
+                       }
+                       if (count > (buffer.Length - offset))
+                       {
+                               throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
+                       }
+
+                       if (!this.context.HandshakeFinished)
+                       {
+                               this.doHandshake();     // Handshake negotiation
+                       }
+
+                       if (!Monitor.TryEnter(this.read))
+                       {
+                               throw new InvalidOperationException("A read operation is already in progress.");
+                       }
+
+                       IAsyncResult asyncResult;
+
+                       try
+                       {
+                               System.Threading.Monitor.Enter(this.read);
+
+                               // If actual buffer is full readed reset it
+                               if (this.inputBuffer.Position == this.inputBuffer.Length &&
+                                       this.inputBuffer.Length > 0)
+                               {
+                                       this.resetBuffer();
+                               }
+
+                               // Check if we have space in the middle buffer
+                               // if not Read next TLS record and update the inputBuffer
+                               while ((this.inputBuffer.Length - this.inputBuffer.Position) < count)
+                               {
+                                       // Read next record and write it into the inputBuffer
+                                       long    position        = this.inputBuffer.Position;                                    
+                                       byte[]  record          = this.receiveRecord();
+                                       
+                                       if (record != null && record.Length > 0)
+                                       {
+                                               // Write new data to the inputBuffer
+                                               this.inputBuffer.Seek(0, SeekOrigin.End);
+                                               this.inputBuffer.Write(record, 0, record.Length);
+
+                                               // Restore buffer position
+                                               this.inputBuffer.Seek(position, SeekOrigin.Begin);
+                                       }
+                                       else
+                                       {
+                                               if (record == null)
+                                               {
+                                                       break;
+                                               }
+                                       }
+
+                                       // TODO: Review if we need to check the Length
+                                       // property of the innerStream for other types
+                                       // of streams, to check that there are data available
+                                       // for read
+                                       if (this.innerStream is NetworkStream &&
+                                               !((NetworkStream)this.innerStream).DataAvailable)
+                                       {
+                                               break;
+                                       }
+                               }
+
+                               asyncResult = this.inputBuffer.BeginRead(
+                                       buffer, offset, count, callback, state);
+                       }
+                       catch (TlsException ex)
+                       {
+                               throw new IOException("The authentication or decryption has failed.", ex);
+                       }
+                       catch (Exception ex)
+                       {
+                               throw new IOException("IO exception during read.", ex);
+                       }
+                       finally
+                       {
+                               System.Threading.Monitor.Exit(this.read);
+                       }
+
+                       return asyncResult;
+               }
+
+               public override IAsyncResult BeginWrite(
+                       byte[]                  buffer,
+                       int                             offset,
+                       int                             count,
+                       AsyncCallback   callback,
+                       object                  state)
+               {
+                       this.checkDisposed();
+
+                       if (buffer == null)
+                       {
+                               throw new ArgumentNullException("buffer is a null reference.");
+                       }
+                       if (offset < 0)
+                       {
+                               throw new ArgumentOutOfRangeException("offset is less than 0.");
+                       }
+                       if (offset > buffer.Length)
+                       {
+                               throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
+                       }
+                       if (count < 0)
+                       {
+                               throw new ArgumentOutOfRangeException("count is less than 0.");
+                       }
+                       if (count > (buffer.Length - offset))
+                       {
+                               throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
+                       }
+
+                       if (!this.context.HandshakeFinished)
+                       {
+                               // Start handshake negotiation
+                               this.doHandshake();
+                       }
+
+                       if (!Monitor.TryEnter(this.write))
+                       {
+                               throw new InvalidOperationException("A write operation is already in progress.");
+                       }
+
+                       IAsyncResult asyncResult;
+
+                       try
+                       {
+                               Monitor.Enter(this.write);
+
+                               // Send the buffer as a TLS record
+                               byte[] record = this.encodeRecord(
+                                       TlsContentType.ApplicationData, buffer, offset, count);
+                               
+                               asyncResult = this.innerStream.BeginWrite(
+                                       record, 0, record.Length, callback, state);
+                       }
+                       catch (TlsException ex)
+                       {
+                               throw new IOException("The authentication or decryption has failed.", ex);
+                       }
+                       catch (Exception ex)
+                       {
+                               throw new IOException("IO exception during Write.", ex);
+                       }
+                       finally
+                       {
+                               Monitor.Exit(this.write);
+                       }                       
+
+                       return asyncResult;
+               }
+
+               public override int EndRead(IAsyncResult asyncResult)
+               {
+                       this.checkDisposed();
+
+                       if (asyncResult == null)
+                       {
+                               throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginRead.");
+                       }
+
+                       return this.inputBuffer.EndRead(asyncResult);
+               }
+
+               public override void EndWrite(IAsyncResult asyncResult)
+               {
+                       this.checkDisposed();
+
+                       if (asyncResult == null)
+                       {
+                               throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginRead.");
+                       }
+
+                       this.innerStream.EndWrite (asyncResult);
+               }
+
+               public override void Close()
+               {
+                       ((IDisposable)this).Dispose();
+               }
+
+               public override void Flush()
+               {
+                       this.checkDisposed();
+
+                       this.innerStream.Flush();
+               }
+
+               public int Read(byte[] buffer)
+               {
+                       return this.Read(buffer, 0, buffer.Length);
+               }
+
+               public override int Read(byte[] buffer, int offset, int count)
+               {
+                       IAsyncResult res = this.BeginRead(buffer, offset, count, null, null);
+
+                       return this.EndRead(res);
+               }
+
+               public override long Seek(long offset, SeekOrigin origin)
+               {
+                       throw new NotSupportedException();
+               }
+               
+               public override void SetLength(long value)
+               {
+                       throw new NotSupportedException();
+               }
+
+               public void Write(byte[] buffer)
+               {
+                       this.Write(buffer, 0, buffer.Length);
+               }
+
+               public override void Write(byte[] buffer, int offset, int count)
+               {
+                       IAsyncResult res = this.BeginWrite (buffer, offset, count, null, null);
+
+                       this.EndWrite(res);
+               }
+
+               #endregion
+
+               #region Reveive Record Methods
+
+               private byte[] receiveRecord()
+               {
+                       if (this.context.ConnectionEnd)
+                       {
+                               throw this.context.CreateException("The session is finished and it's no longer valid.");
+                       }
+                       
+                       // Try to read the Record Content Type
+                       int type = this.innerStream.ReadByte();
+
+                       // There are no more data for read
+                       if (type == -1)
+                       {
+                               return null;
+                       }
+
+                       TlsContentType                  contentType     = (TlsContentType)type;
+                       SecurityProtocolType    protocol        = (SecurityProtocolType)this.ReadShort();
+                       short                                   length          = this.ReadShort();
+                       
+                       // Read Record data
+                       int             received        = 0;
+                       byte[]  buffer          = new byte[length];
+                       while (received != length)
+                       {
+                               received += this.innerStream.Read(
+                                       buffer, received, buffer.Length - received);
+                       }
+
+                       TlsStream message = new TlsStream(buffer);
+               
+                       // Check that the message has a valid protocol version
+                       if (protocol != this.context.Protocol)
+                       {
+                               throw this.context.CreateException("Invalid protocol version on message received from server");
+                       }
+
+                       // Decrypt message contents if needed
+                       if (contentType == TlsContentType.Alert && length == 2)
+                       {
+                       }
+                       else
+                       {
+                               if (this.context.IsActual &&
+                                       contentType != TlsContentType.ChangeCipherSpec)
+                               {
+                                       message = this.decryptRecordFragment(
+                                               contentType, 
+                                               protocol,
+                                               message.ToArray());
+                               }
+                       }
+
+                       byte[] result = message.ToArray();
+
+                       // Process record
+                       switch (contentType)
+                       {
+                               case TlsContentType.Alert:
+                                       this.processAlert((TlsAlertLevel)message.ReadByte(),
+                                               (TlsAlertDescription)message.ReadByte());
+                                       break;
+
+                               case TlsContentType.ChangeCipherSpec:
+                                       // Reset sequence numbers
+                                       this.context.ReadSequenceNumber = 0;
+                                       break;
+
+                               case TlsContentType.ApplicationData:
+                                       break;
+
+                               case TlsContentType.Handshake:
+                                       while (!message.EOF)
+                                       {
+                                               this.processHandshakeMessage(message);
+                                       }
+                                       // Update handshakes of current messages
+                                       this.context.HandshakeMessages.Write(message.ToArray());
+                                       break;
+
+                               default:
+                                       throw this.context.CreateException("Unknown record received from server.");
+                       }
+
+                       return result;
+               }
+
+               #endregion
+
+               #region Send Record Methods
+
+               internal void SendAlert(TlsAlert alert)
+               {                       
+                       // Write record
+                       this.sendRecord(TlsContentType.Alert, alert.ToArray());
+
+                       // Update session
+                       alert.Update();
+
+                       // Reset message contents
+                       alert.Reset();
+               }
+
+               private void sendChangeCipherSpec()
+               {
+                       // Send Change Cipher Spec message
+                       this.sendRecord(TlsContentType.ChangeCipherSpec, new byte[] {1});
+
+                       // Reset sequence numbers
+                       this.context.WriteSequenceNumber = 0;
+
+                       // Make the pending state to be the current state
+                       this.context.IsActual = true;
+
+                       // Send Finished message
+                       this.sendRecord(TlsHandshakeType.Finished);                     
+               }
+
+               private void sendRecord(TlsHandshakeType type)
+               {
+                       TlsHandshakeMessage msg = this.createClientHandshakeMessage(type);
+                       
+                       // Write record
+                       this.sendRecord(msg.ContentType, msg.EncodeMessage());
+
+                       // Update session
+                       msg.Update();
+
+                       // Reset message contents
+                       msg.Reset();
+               }
+
+               private void sendRecord(TlsContentType contentType, byte[] recordData)
+               {
+                       if (this.context.ConnectionEnd)
+                       {
+                               throw this.context.CreateException("The session is finished and it's no longer valid.");
+                       }
+
+                       byte[] record = this.encodeRecord(contentType, recordData);
+
+                       this.innerStream.Write(record, 0, record.Length);
+               }
+
+               private byte[] encodeRecord(TlsContentType contentType, byte[] recordData)
+               {
+                       return this.encodeRecord(
+                               contentType,
+                               recordData,
+                               0,
+                               recordData.Length);
+               }
+
+               private byte[] encodeRecord(
+                       TlsContentType  contentType, 
+                       byte[]                  recordData,
+                       int                             offset,
+                       int                             count)
+               {
+                       if (this.context.ConnectionEnd)
+                       {
+                               throw this.context.CreateException("The session is finished and it's no longer valid.");
+                       }
+
+                       TlsStream record = new TlsStream();
+
+                       int     position = offset;
+
+                       while (position < ( offset + count ))
+                       {
+                               short   fragmentLength = 0;
+                               byte[]  fragment;
+
+                               if ((count - position) > TlsContext.MAX_FRAGMENT_SIZE)
+                               {
+                                       fragmentLength = TlsContext.MAX_FRAGMENT_SIZE;
+                               }
+                               else
+                               {
+                                       fragmentLength = (short)(count - position);
+                               }
+
+                               // Fill the fragment data
+                               fragment = new byte[fragmentLength];
+                               Buffer.BlockCopy(recordData, position, fragment, 0, fragmentLength);
+
+                               if (this.context.IsActual)
+                               {
+                                       // Encrypt fragment
+                                       fragment = this.encryptRecordFragment(contentType, fragment);
+                               }
+
+                               // Write tls message
+                               record.Write((byte)contentType);
+                               record.Write((short)this.context.Protocol);
+                               record.Write((short)fragment.Length);
+                               record.Write(fragment);
+
+                               // Update buffer position
+                               position += fragmentLength;
+                       }
+
+                       return record.ToArray();
+               }
+               
+               #endregion
+
+               #region Cryptography Methods
+
+               private byte[] encryptRecordFragment(
+                       TlsContentType  contentType, 
+                       byte[]                  fragment)
+               {
+                       // Calculate message MAC
+                       byte[] mac      = this.context.Cipher.ComputeClientRecordMAC(contentType, fragment);
+
+                       // Encrypt the message
+                       byte[] ecr = this.context.Cipher.EncryptRecord(fragment, mac);
+
+                       // Set new IV
+                       if (this.context.Cipher.CipherMode == CipherMode.CBC)
+                       {
+                               byte[] iv = new byte[this.context.Cipher.IvSize];
+                               System.Array.Copy(ecr, ecr.Length - iv.Length, iv, 0, iv.Length);
+                               this.context.Cipher.UpdateClientCipherIV(iv);
+                       }
+
+                       // Update sequence number
+                       this.context.WriteSequenceNumber++;
+
+                       return ecr;
+               }
+
+               private TlsStream decryptRecordFragment(
+                       TlsContentType                  contentType, 
+                       SecurityProtocolType    protocol,
+                       byte[]                                  fragment)
+               {
+                       byte[]  dcrFragment     = null;
+                       byte[]  dcrMAC          = null;
+
+                       // Decrypt message
+                       this.context.Cipher.DecryptRecord(fragment, ref dcrFragment, ref dcrMAC);
+
+                       // Set new IV
+                       if (this.context.Cipher.CipherMode == CipherMode.CBC)
+                       {
+                               byte[] iv = new byte[this.context.Cipher.IvSize];
+                               System.Array.Copy(fragment, fragment.Length - iv.Length, iv, 0, iv.Length);
+                               this.context.Cipher.UpdateServerCipherIV(iv);
+                       }
+                       
+                       // Check MAC code
+                       byte[] mac = this.context.Cipher.ComputeServerRecordMAC(contentType, dcrFragment);
+
+                       // Check that the mac is correct
+                       if (mac.Length != dcrMAC.Length)
+                       {
+                               throw new TlsException("Invalid MAC received from server.");
+                       }
+                       for (int i = 0; i < mac.Length; i++)
+                       {
+                               if (mac[i] != dcrMAC[i])
+                               {
+                                       throw new TlsException("Invalid MAC received from server.");
+                               }
+                       }
+
+                       // Update sequence number
+                       this.context.ReadSequenceNumber++;
+
+                       return new TlsStream(dcrFragment);
+               }
+
+               #endregion
+
+               #region Handshake Processing Methods
+
+               private void processHandshakeMessage(TlsStream handMsg)
+               {
+                       TlsHandshakeType        handshakeType   = (TlsHandshakeType)handMsg.ReadByte();
+                       TlsHandshakeMessage     message                 = null;
+
+                       // Read message length
+                       int length = handMsg.ReadInt24();
+
+                       // Read message data
+                       byte[] data = new byte[length];
+                       handMsg.Read(data, 0, length);
+
+                       // Create and process the server message
+                       message = this.createServerHandshakeMessage(handshakeType, data);
+
+                       // Update session
+                       if (message != null)
+                       {
+                               message.Update();
+                       }
+               }
+
+               private void processAlert(
+                       TlsAlertLevel           alertLevel, 
+                       TlsAlertDescription alertDesc)
+               {
+                       switch (alertLevel)
+                       {
+                               case TlsAlertLevel.Fatal:
+                                       throw this.context.CreateException(alertLevel, alertDesc);                                      
+
+                               case TlsAlertLevel.Warning:
+                               default:
+                               switch (alertDesc)
+                               {
+                                       case TlsAlertDescription.CloseNotify:
+                                               this.context.ConnectionEnd = true;
+                                               break;
+
+                                       default:
+                                               this.RaiseWarningAlert(alertLevel, alertDesc);
+                                               break;
+                               }
+                               break;
+                       }
+               }
+
+               #endregion
+
+               #region Misc Methods
+
+               private void resetBuffer()
+               {
+                       this.inputBuffer.SetLength(0);
+                       this.inputBuffer.Position = 0;
+               }
+
+               private short ReadShort()
+               {
+                       byte[] b = new byte[2];
+                       this.innerStream.Read(b, 0, b.Length);
+
+                       short val = BitConverter.ToInt16(b, 0);
+
+                       return System.Net.IPAddress.HostToNetworkOrder(val);
+               }
+
+               private void checkDisposed()
+               {
+                       if (this.disposed)
+                       {
+                               throw new ObjectDisposedException("The SslClientStream is closed.");
+                       }
+               }
+
+               #endregion
+
+               #region Handsake Methods
+
+               /*
+                       Client                                                                                  Server
+
+                       ClientHello                 -------->
+                                                                                                                       ServerHello
+                                                                                                                       Certificate*
+                                                                                                                       ServerKeyExchange*
+                                                                                                                       CertificateRequest*
+                                                                               <--------                       ServerHelloDone
+                       Certificate*
+                       ClientKeyExchange
+                       CertificateVerify*
+                       [ChangeCipherSpec]
+                       Finished                    -------->
+                                                                                                                       [ChangeCipherSpec]
+                                                                               <--------           Finished
+                       Application Data            <------->                   Application Data
+
+                                       Fig. 1 - Message flow for a full handshake              
+               */
+
+               private void doHandshake()
+               {
+                       // Obtain supported cipher suite collection
+                       this.context.SupportedCiphers = TlsCipherSuiteFactory.GetSupportedCiphers(context.Protocol);
+
+                       // Send client hello
+                       this.sendRecord(TlsHandshakeType.ClientHello);
+
+                       // Read server response
+                       while (!this.context.HelloDone)
+                       {
+                               // Read next record
+                               this.receiveRecord();
+                       }
+                       
+                       // Send client certificate if requested
+                       if (this.context.ServerSettings.CertificateRequest)
+                       {
+                               this.sendRecord(TlsHandshakeType.Certificate);
+                       }
+
+                       // Send Client Key Exchange
+                       this.sendRecord(TlsHandshakeType.ClientKeyExchange);
+
+                       // Now initialize session cipher with the generated keys
+                       this.context.Cipher.InitializeCipher();
+
+                       // Send certificate verify if requested
+                       if (this.context.ServerSettings.CertificateRequest)
+                       {
+                               this.sendRecord(TlsHandshakeType.CertificateVerify);
+                       }
+
+                       // Send Cipher Spec protocol
+                       this.sendChangeCipherSpec();                    
+                       
+                       // Read record until server finished is received
+                       while (!this.context.HandshakeFinished)
+                       {
+                               // If all goes well this will process messages:
+                               //              Change Cipher Spec
+                               //              Server finished
+                               this.receiveRecord();
+                       }
+
+                       // Clear Key Info
+                       this.context.ClearKeyInfo();
+               }
+               
+               private TlsHandshakeMessage createClientHandshakeMessage(TlsHandshakeType type)
+               {
+                       switch (type)
+                       {
+                               case TlsHandshakeType.ClientHello:
+                                       return new TlsClientHello(this.context);
+
+                               case TlsHandshakeType.Certificate:
+                                       return new TlsClientCertificate(this.context);
+
+                               case TlsHandshakeType.ClientKeyExchange:
+                                       return new TlsClientKeyExchange(this.context);
+
+                               case TlsHandshakeType.CertificateVerify:
+                                       return new TlsClientCertificateVerify(this.context);
+
+                               case TlsHandshakeType.Finished:
+                                       return new TlsClientFinished(this.context);
+
+                               default:
+                                       throw new InvalidOperationException("Unknown client handshake message type: " + type.ToString() );
+                       }
+               }
+
+               private TlsHandshakeMessage createServerHandshakeMessage(TlsHandshakeType type, byte[] buffer)
+               {
+                       switch (type)
+                       {
+                               case TlsHandshakeType.HelloRequest:
+                                       this.sendRecord(TlsHandshakeType.ClientHello);
+                                       return null;
+
+                               case TlsHandshakeType.ServerHello:
+                                       return new TlsServerHello(this.context, buffer);
+
+                               case TlsHandshakeType.Certificate:
+                                       return new TlsServerCertificate(this.context, buffer);
+
+                               case TlsHandshakeType.ServerKeyExchange:
+                                       return new TlsServerKeyExchange(this.context, buffer);
+
+                               case TlsHandshakeType.CertificateRequest:
+                                       return new TlsServerCertificateRequest(this.context, buffer);
+
+                               case TlsHandshakeType.ServerHelloDone:
+                                       return new TlsServerHelloDone(this.context, buffer);
+
+                               case TlsHandshakeType.Finished:
+                                       return new TlsServerFinished(this.context, buffer);
+
+                               default:
+                                       throw this.context.CreateException("Unknown server handshake message received ({0})", type.ToString());
+                       }
+               }
+
+               #endregion
+
+               #region Event Methods
+
+               internal void RaiseWarningAlert(
+                       TlsAlertLevel           level, 
+                       TlsAlertDescription description)
+               {
+                       if (WarningAlert != null)
+                       {
+                               WarningAlert(this, new TlsWarningAlertEventArgs(level, description));
+                       }
+               }
+
+               internal bool RaiseServerCertificateValidation(
+                       X509Certificate certificate, 
+                       int[]                   certificateErrors)
+               {
+                       if (this.ServerCertValidation != null)
+                       {
+                               return this.ServerCertValidation(certificate, certificateErrors);
+                       }
+
+                       return false;
+               }
+
+               internal X509Certificate RaiseClientCertificateSelection(
+                       X509CertificateCollection       clientCertificates, 
+                       X509Certificate                         serverCertificate, 
+                       string                                          targetHost, 
+                       X509CertificateCollection       serverRequestedCertificates)
+               {
+                       if (this.ClientCertSelection != null)
+                       {
+                               return this.ClientCertSelection(
+                                       clientCertificates,
+                                       serverCertificate,
+                                       targetHost,
+                                       serverRequestedCertificates);
+                       }
+
+                       return null;
+               }
+
+               internal AsymmetricAlgorithm RaisePrivateKeySelection(
+                       X509Certificate clientCertificate, 
+                       string                  targetHost)
+               {
+                       if (this.PrivateKeySelection != null)
+                       {
+                               return this.PrivateKeySelection(
+                                       clientCertificate,
+                                       targetHost);
+                       }
+
+                       return null;
+               }
+
+               #endregion
+       }
+}