* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / Mono.Security / Mono.Security.Protocol.Tls / SslClientStream.cs
index ed99efe02575a3a287d41a4d4c13c173512485f2..c4407e05b010c6692982834883e2b7a179304d0f 100644 (file)
@@ -29,6 +29,7 @@ using System.Net;
 using System.Net.Sockets;
 using System.Security.Cryptography;
 using System.Security.Cryptography.X509Certificates;
+using System.Threading;
 
 using Mono.Security.Protocol.Tls.Handshake;
 
@@ -52,7 +53,7 @@ namespace Mono.Security.Protocol.Tls
 
        #endregion
 
-       public class SslClientStream : Stream, IDisposable
+       public class SslClientStream : SslStreamBase
        {
                #region Internal Events
                
@@ -62,185 +63,24 @@ namespace Mono.Security.Protocol.Tls
                
                #endregion
 
-               #region Fields
-
-               private Stream                                                  innerStream;
-               private BufferedStream                                  inputBuffer;
-               private ClientContext                                   context;
-               private ClientRecordProtocol                    protocol;
-               private bool                                                    ownsStream;
-               private bool                                                    disposed;
-               private bool                                                    checkCertRevocationStatus;
-               private object                                                  read;
-               private object                                                  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(); }
-               }
-
                // required by HttpsClientStream for proxy support
-               internal Stream InputBuffer {
-                       get { return inputBuffer; }
-               }
-               #endregion
-
-               #region Security Properties
-
-               public bool CheckCertRevocationStatus 
+               internal Stream InputBuffer 
                {
-                       get { return this.checkCertRevocationStatus ; }
-                       set { this.checkCertRevocationStatus = value; }
+                       get { return base.inputBuffer; }
                }
 
-               public CipherAlgorithmType CipherAlgorithm 
-               {
-                       get 
-                       { 
-                               if (this.context.HandshakeState == HandshakeState.Finished)
-                               {
-                                       return this.context.Cipher.CipherAlgorithmType;
-                               }
-
-                               return CipherAlgorithmType.None;
-                       }
-               }
-               
-               public int CipherStrength 
+               public X509CertificateCollection ClientCertificates
                {
-                       get 
-                       { 
-                               if (this.context.HandshakeState == HandshakeState.Finished)
-                               {
-                                       return this.context.Cipher.EffectiveKeyBits;
-                               }
-
-                               return 0;
-                       }
-               }
-               
-               public X509CertificateCollection ClientCertificates 
-               {
-                       get { return this.context.ClientSettings.Certificates;}
-               }
-               
-               public HashAlgorithmType HashAlgorithm 
-               {
-                       get 
-                       { 
-                               if (this.context.HandshakeState == HandshakeState.Finished)
-                               {
-                                       return this.context.Cipher.HashAlgorithmType; 
-                               }
-
-                               return HashAlgorithmType.None;
-                       }
-               }
-               
-               public int HashStrength
-               {
-                       get 
-                       { 
-                               if (this.context.HandshakeState == HandshakeState.Finished)
-                               {
-                                       return this.context.Cipher.HashSize * 8; 
-                               }
-
-                               return 0;
-                       }
-               }
-               
-               public int KeyExchangeStrength 
-               {
-                       get 
-                       { 
-                               if (this.context.HandshakeState == HandshakeState.Finished)
-                               {
-                                       return this.context.ServerSettings.Certificates[0].RSA.KeySize;
-                               }
-
-                               return 0;
-                       }
-               }
-               
-               public ExchangeAlgorithmType KeyExchangeAlgorithm 
-               {
-                       get 
-                       { 
-                               if (this.context.HandshakeState == HandshakeState.Finished)
-                               {
-                                       return this.context.Cipher.ExchangeAlgorithmType; 
-                               }
-
-                               return ExchangeAlgorithmType.None;
-                       }
+                       get { return this.context.ClientSettings.Certificates; }
                }
-               
-               public SecurityProtocolType SecurityProtocol 
-               {
-                       get 
-                       { 
-                               if (this.context.HandshakeState == HandshakeState.Finished)
-                               {
-                                       return this.context.SecurityProtocol; 
-                               }
 
-                               return 0;
-                       }
-               }
-               
-               public X509Certificate SelectedClientCertificate 
+               public X509Certificate SelectedClientCertificate
                {
                        get { return this.context.ClientSettings.ClientCertificate; }
                }
 
-               public X509Certificate ServerCertificate 
-               {
-                       get 
-                       { 
-                               if (this.context.HandshakeState == HandshakeState.Finished)
-                               {
-                                       if (this.context.ServerSettings.Certificates != null &&
-                                               this.context.ServerSettings.Certificates.Count > 0)
-                                       {
-                                               return new X509Certificate(this.context.ServerSettings.Certificates[0].RawData);
-                                       }
-                               }
-
-                               return null;
-                       }
-               } 
-
-               // this is used by Mono's certmgr tool to download certificates
-               internal Mono.Security.X509.X509CertificateCollection ServerCertificates {
-                       get { return context.ServerSettings.Certificates; }
-               }
-
                #endregion
 
                #region Callback Properties
@@ -257,12 +97,12 @@ namespace Mono.Security.Protocol.Tls
                        set { this.ClientCertSelection = value; }
                }
 
-               public PrivateKeySelectionCallback PrivateKeyCertSelectionDelegate 
+               public PrivateKeySelectionCallback PrivateKeyCertSelectionDelegate
                {
                        get { return this.PrivateKeySelection; }
                        set { this.PrivateKeySelection = value; }
                }
-
+               
                #endregion
 
                #region Constructors
@@ -313,16 +153,9 @@ namespace Mono.Security.Protocol.Tls
                        string                                          targetHost,
                        bool                                            ownsStream,
                        SecurityProtocolType            securityProtocolType,
-                       X509CertificateCollection       clientCertificates)
+                       X509CertificateCollection       clientCertificates):
+                       base(stream, ownsStream)
                {
-                       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.");
@@ -334,12 +167,7 @@ namespace Mono.Security.Protocol.Tls
                                targetHost, 
                                clientCertificates);
 
-                       this.inputBuffer        = new BufferedStream(new MemoryStream());
-                       this.innerStream        = stream;
-                       this.ownsStream         = ownsStream;
-                       this.read                       = new object ();
-                       this.write                      = new object ();
-                       this.protocol           = new ClientRecordProtocol(innerStream, context);
+                       this.protocol = new ClientRecordProtocol(innerStream, (ClientContext)this.context);
                }
 
                #endregion
@@ -348,433 +176,217 @@ namespace Mono.Security.Protocol.Tls
 
                ~SslClientStream()
                {
-                       this.Dispose(false);
+                       base.Dispose(false);
                }
 
                #endregion
 
-        #region IDisposable Methods
+               #region IDisposable Methods
 
-               void IDisposable.Dispose()
+               protected override void Dispose(bool disposing)
                {
-                       this.Dispose(true);
-                       GC.SuppressFinalize(this);
-               }
+                       base.Dispose(disposing);
 
-               protected virtual void Dispose(bool disposing)
-               {
-                       if (!this.disposed)
+                       if (disposing)
                        {
-                               if (disposing)
-                               {
-                                       if (this.innerStream != null)
-                                       {
-                                               if (this.context.HandshakeState == HandshakeState.Finished &&
-                                                       !this.context.ConnectionEnd)
-                                               {
-                                                       // Write close notify                                                   
-                                                       this.protocol.SendAlert(AlertDescription.CloseNotify);
-                                               }
-
-                                               if (this.ownsStream)
-                                               {
-                                                       // Close inner stream
-                                                       this.innerStream.Close();
-                                               }
-                                       }
-                                       this.ownsStream                         = false;
-                                       this.innerStream                        = null;
-                                       this.ClientCertSelection        = null;
-                                       this.ServerCertValidation       = null;
-                                       this.PrivateKeySelection        = null;
-                               }
-
-                               this.disposed = true;
+                               this.ServerCertValidation = null;
+                               this.ClientCertSelection = null;
+                               this.PrivateKeySelection = null;
                        }
                }
 
                #endregion
 
-               #region Methods
+               #region Handshake 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.");
-                       }
+               /*
+                       Client                                                                                  Server
 
-                       lock (this)
-                       {
-                               if (this.context.HandshakeState == HandshakeState.None)
-                               {
-                                       this.NegotiateHandshake();
-                               }
-                       }
+                       ClientHello                 -------->
+                                                                                                                       ServerHello
+                                                                                                                       Certificate*
+                                                                                                                       ServerKeyExchange*
+                                                                                                                       CertificateRequest*
+                                                                               <--------                       ServerHelloDone
+                       Certificate*
+                       ClientKeyExchange
+                       CertificateVerify*
+                       [ChangeCipherSpec]
+                       Finished                    -------->
+                                                                                                                       [ChangeCipherSpec]
+                                                                               <--------           Finished
+                       Application Data            <------->                   Application Data
 
-                       IAsyncResult asyncResult;
+                                       Fig. 1 - Message flow for a full handshake              
+               */
 
-                       lock (this.read)
+               internal override IAsyncResult OnBeginNegotiateHandshake(AsyncCallback callback, object state)
+               {
+                       try
                        {
-                               try
-                               {
-                                       // If actual buffer is full readed reset it
-                                       if (this.inputBuffer.Position == this.inputBuffer.Length &&
-                                               this.inputBuffer.Length > 0)
-                                       {
-                                               this.resetBuffer();
-                                       }
-
-                                       if (!this.context.ConnectionEnd)
-                                       {
-                                               // 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.protocol.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)
-                               {
-                                       this.protocol.SendAlert(ex.Alert);
-                                       this.Close();
-
-                                       throw new IOException("The authentication or decryption has failed.");
-                               }
-                               catch (Exception)
+                               if (this.context.HandshakeState != HandshakeState.None)
                                {
-                                       throw new IOException("IO exception during read.");
+                                       this.context.Clear();
                                }
-                       }
 
-                       return asyncResult;
-               }
+                               // Obtain supported cipher suites
+                               this.context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(this.context.SecurityProtocol);
 
-               public override IAsyncResult BeginWrite(
-                       byte[]                  buffer,
-                       int                             offset,
-                       int                             count,
-                       AsyncCallback   callback,
-                       object                  state)
-               {
-                       this.checkDisposed();
+                               // Set handshake state
+                               this.context.HandshakeState = HandshakeState.Started;
 
-                       if (buffer == null)
-                       {
-                               throw new ArgumentNullException("buffer is a null reference.");
+                               // Send client hello
+                               return this.protocol.BeginSendRecord(HandshakeType.ClientHello, callback, state);
                        }
-                       if (offset < 0)
+                       catch (TlsException ex)
                        {
-                               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.");
-                       }
+                               this.protocol.SendAlert(ex.Alert);
 
-                       lock (this)
-                       {
-                               if (this.context.HandshakeState == HandshakeState.None)
-                               {
-                                       this.NegotiateHandshake();
-                               }
+                               throw new IOException("The authentication or decryption has failed.", ex);
                        }
-
-                       IAsyncResult asyncResult;
-
-                       lock (this.write)
+                       catch (Exception ex)
                        {
-                               try
-                               {
-                                       // Send the buffer as a TLS record
-                                       
-                                       byte[] record = this.protocol.EncodeRecord(
-                                               ContentType.ApplicationData, buffer, offset, count);
-                               
-                                       asyncResult = this.innerStream.BeginWrite(
-                                               record, 0, record.Length, callback, state);
-                               }
-                               catch (TlsException ex)
-                               {
-                                       this.protocol.SendAlert(ex.Alert);
-                                       this.Close();
+                               this.protocol.SendAlert(AlertDescription.InternalError);
 
-                                       throw new IOException("The authentication or decryption has failed.");
-                               }
-                               catch (Exception)
-                               {
-                                       throw new IOException("IO exception during Write.");
-                               }
+                               throw new IOException("The authentication or decryption has failed.", ex);
                        }
-
-                       return asyncResult;
                }
 
-               public override int EndRead(IAsyncResult asyncResult)
+               private void SafeReceiveRecord (Stream s)
                {
-                       this.checkDisposed();
-
-                       if (asyncResult == null)
-                       {
-                               throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginRead.");
+                       byte[] record = this.protocol.ReceiveRecord (s);
+                       if ((record == null) || (record.Length == 0)) {
+                               throw new TlsException (
+                                       AlertDescription.HandshakeFailiure,
+                                       "The server stopped the handshake.");
                        }
-
-                       return this.inputBuffer.EndRead(asyncResult);
                }
 
-               public override void EndWrite(IAsyncResult asyncResult)
+               internal override void OnNegotiateHandshakeCallback(IAsyncResult asyncResult)
                {
-                       this.checkDisposed();
+                       this.protocol.EndSendRecord(asyncResult);
 
-                       if (asyncResult == null)
+                       // Read server response
+                       while (this.context.LastHandshakeMsg != HandshakeType.ServerHelloDone) 
                        {
-                               throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginRead.");
+                               // Read next record
+                               SafeReceiveRecord (this.innerStream);
+
+                               // special case for abbreviated handshake where no ServerHelloDone is sent from the server
+                               if (this.context.AbbreviatedHandshake && (this.context.LastHandshakeMsg == HandshakeType.ServerHello))
+                                       break;
                        }
 
-                       this.innerStream.EndWrite (asyncResult);
-               }
+                       // the handshake is much easier if we can reuse a preivous session settings
+                       if (this.context.AbbreviatedHandshake) 
+                       {
+                               ClientSessionCache.SetContextFromCache (this.context);
+                               this.context.Cipher.ComputeKeys ();
+                               this.context.Cipher.InitializeCipher ();
 
-               public override void Close()
-               {
-                       ((IDisposable)this).Dispose();
-               }
+                               // Send Cipher Spec protocol
+                               this.protocol.SendChangeCipherSpec ();
 
-               public override void Flush()
-               {
-                       this.checkDisposed();
+                               // Read record until server finished is received
+                               while (this.context.HandshakeState != HandshakeState.Finished) 
+                               {
+                                       // If all goes well this will process messages:
+                                       //              Change Cipher Spec
+                                       //              Server finished
+                                       SafeReceiveRecord (this.innerStream);
+                               }
 
-                       this.innerStream.Flush();
-               }
+                               // Send Finished message
+                               this.protocol.SendRecord (HandshakeType.Finished);
+                       }
+                       else
+                       {
+                               // Send client certificate if requested
+                               // even if the server ask for it it _may_ still be optional
+                               bool clientCertificate = this.context.ServerSettings.CertificateRequest;
+
+                               // NOTE: sadly SSL3 and TLS1 differs in how they handle this and
+                               // the current design doesn't allow a very cute way to handle 
+                               // SSL3 alert warning for NoCertificate (41).
+                               if (this.context.SecurityProtocol == SecurityProtocolType.Ssl3)
+                               {
+                                       clientCertificate = ((this.context.ClientSettings.Certificates != null) &&
+                                               (this.context.ClientSettings.Certificates.Count > 0));
+                                       // this works well with OpenSSL (but only for SSL3)
+                               }
 
-               public int Read(byte[] buffer)
-               {
-                       return this.Read(buffer, 0, buffer.Length);
-               }
+                               if (clientCertificate)
+                               {
+                                       this.protocol.SendRecord(HandshakeType.Certificate);
+                               }
 
-               public override int Read(byte[] buffer, int offset, int count)
-               {
-                       IAsyncResult res = this.BeginRead(buffer, offset, count, null, null);
+                               // Send Client Key Exchange
+                               this.protocol.SendRecord(HandshakeType.ClientKeyExchange);
 
-                       return this.EndRead(res);
-               }
+                               // Now initialize session cipher with the generated keys
+                               this.context.Cipher.InitializeCipher();
 
-               public override long Seek(long offset, SeekOrigin origin)
-               {
-                       throw new NotSupportedException();
-               }
-               
-               public override void SetLength(long value)
-               {
-                       throw new NotSupportedException();
-               }
+                               // Send certificate verify if requested (optional)
+                               if (clientCertificate && (this.context.ClientSettings.ClientCertificate != null))
+                               {
+                                       this.protocol.SendRecord(HandshakeType.CertificateVerify);
+                               }
 
-               public void Write(byte[] buffer)
-               {
-                       this.Write(buffer, 0, buffer.Length);
-               }
+                               // Send Cipher Spec protocol
+                               this.protocol.SendChangeCipherSpec ();
+                               // Send Finished message
+                               this.protocol.SendRecord (HandshakeType.Finished);                      
+
+                               // Read record until server finished is received
+                               while (this.context.HandshakeState != HandshakeState.Finished) {
+                                       // If all goes well this will process messages:
+                                       //              Change Cipher Spec
+                                       //              Server finished
+                                       SafeReceiveRecord (this.innerStream);
+                               }
+                       }
 
-               public override void Write(byte[] buffer, int offset, int count)
-               {
-                       IAsyncResult res = this.BeginWrite (buffer, offset, count, null, null);
+                       // Reset Handshake messages information
+                       this.context.HandshakeMessages.Reset ();
+
+                       // Clear Key Info
+                       this.context.ClearKeyInfo();
 
-                       this.EndWrite(res);
                }
 
                #endregion
 
-               #region Misc Methods
-
-               private void resetBuffer()
-               {
-                       this.inputBuffer.SetLength(0);
-                       this.inputBuffer.Position = 0;
-               }
+               #region Event Methods
 
-               private void checkDisposed()
+               internal override X509Certificate OnLocalCertificateSelection(X509CertificateCollection clientCertificates, X509Certificate serverCertificate, string targetHost, X509CertificateCollection serverRequestedCertificates)
                {
-                       if (this.disposed)
+                       if (this.ClientCertSelection != null)
                        {
-                               throw new ObjectDisposedException("The SslClientStream is closed.");
+                               return this.ClientCertSelection(
+                                       clientCertificates,
+                                       serverCertificate,
+                                       targetHost,
+                                       serverRequestedCertificates);
                        }
-               }
 
-               #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              
-               */
-
-               internal void NegotiateHandshake()
+                       return null;
+               }
+               
+               internal override bool OnRemoteCertificateValidation(X509Certificate certificate, int[] errors)
                {
-                       lock (this)
+                       if (this.ServerCertValidation != null)
                        {
-                               try
-                               {
-                                       if (this.context.HandshakeState != HandshakeState.None)
-                                       {
-                                               this.context.Clear();
-                                       }
-
-                                       // Obtain supported cipher suites
-                                       this.context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(this.context.SecurityProtocol);
-
-                                       // Send client hello
-                                       this.protocol.SendRecord(HandshakeType.ClientHello);
-
-                                       // Read server response
-                                       while (this.context.LastHandshakeMsg != HandshakeType.ServerHelloDone)
-                                       {
-                                               // Read next record
-                                               this.protocol.ReceiveRecord();
-                                       }
-
-                                       // Send client certificate if requested
-                                       if (this.context.ServerSettings.CertificateRequest)
-                                       {
-                                               this.protocol.SendRecord(HandshakeType.Certificate);
-                                       }
-
-                                       // Send Client Key Exchange
-                                       this.protocol.SendRecord(HandshakeType.ClientKeyExchange);
-
-                                       // Now initialize session cipher with the generated keys
-                                       this.context.Cipher.InitializeCipher();
-
-                                       // Send certificate verify if requested
-                                       if (this.context.ServerSettings.CertificateRequest)
-                                       {
-                                               this.protocol.SendRecord(HandshakeType.CertificateVerify);
-                                       }
-
-                                       // Send Cipher Spec protocol
-                                       this.protocol.SendChangeCipherSpec();                   
-                       
-                                       // Read record until server finished is received
-                                       while (this.context.HandshakeState != HandshakeState.Finished)
-                                       {
-                                               // If all goes well this will process messages:
-                                               //              Change Cipher Spec
-                                               //              Server finished
-                                               this.protocol.ReceiveRecord();
-                                       }
-
-                                       // Clear Key Info
-                                       this.context.ClearKeyInfo();
-                               }
-                               catch (TlsException ex)
-                               {
-                                       this.protocol.SendAlert(ex.Alert);
-                                       this.Close();
-
-                                       throw new IOException("The authentication or decryption has failed.");
-                               }
-                               catch (Exception)
-                               {
-                                       this.protocol.SendAlert(AlertDescription.InternalError);
-                                       this.Close();
-
-                                       throw new IOException("The authentication or decryption has failed.");
-                               }
+                               return this.ServerCertValidation(certificate, errors);
                        }
-               }
 
-               #endregion
-
-               #region Event Methods
+                       return (errors != null && errors.Length == 0);
+               }
 
                internal virtual bool RaiseServerCertificateValidation(
                        X509Certificate certificate, 
                        int[]                   certificateErrors)
                {
-                       if (this.ServerCertValidation != null)
-                       {
-                               return this.ServerCertValidation(certificate, certificateErrors);
-                       }
-
-                       return (certificateErrors != null && certificateErrors.Length == 0);
+                       return base.RaiseRemoteCertificateValidation(certificate, certificateErrors);
                }
 
                internal X509Certificate RaiseClientCertificateSelection(
@@ -783,21 +395,10 @@ namespace Mono.Security.Protocol.Tls
                        string                                          targetHost, 
                        X509CertificateCollection       serverRequestedCertificates)
                {
-                       if (this.ClientCertSelection != null)
-                       {
-                               return this.ClientCertSelection(
-                                       clientCertificates,
-                                       serverCertificate,
-                                       targetHost,
-                                       serverRequestedCertificates);
-                       }
-
-                       return null;
+                       return base.RaiseLocalCertificateSelection(clientCertificates, serverCertificate, targetHost, serverRequestedCertificates);
                }
 
-               internal AsymmetricAlgorithm RaisePrivateKeySelection(
-                       X509Certificate certificate, 
-                       string                  targetHost)
+               internal override AsymmetricAlgorithm OnLocalPrivateKeySelection(X509Certificate certificate, string targetHost)
                {
                        if (this.PrivateKeySelection != null)
                        {
@@ -807,6 +408,13 @@ namespace Mono.Security.Protocol.Tls
                        return null;
                }
 
+               internal AsymmetricAlgorithm RaisePrivateKeySelection(
+                       X509Certificate certificate,
+                       string targetHost)
+               {
+                       return base.RaiseLocalPrivateKeySelection(certificate, targetHost);
+               }
+
                #endregion
        }
 }