Check platform without using #define
[mono.git] / mcs / class / Mono.Security / Mono.Security.Protocol.Tls / SslClientStream.cs
1 // Transport Security Layer (TLS)
2 // Copyright (c) 2003-2004 Carlos Guzman Alvarez
3
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
12 // 
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
15 // 
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 //
24
25 using System;
26 using System.Collections;
27 using System.IO;
28 using System.Net;
29 using System.Net.Sockets;
30 using System.Security.Cryptography;
31 using System.Security.Cryptography.X509Certificates;
32 using System.Threading;
33
34 using Mono.Security.Protocol.Tls.Handshake;
35
36 namespace Mono.Security.Protocol.Tls
37 {
38         #region Delegates
39
40         public delegate bool CertificateValidationCallback(
41                 X509Certificate certificate, 
42                 int[]                   certificateErrors);
43
44         public delegate X509Certificate CertificateSelectionCallback(
45                 X509CertificateCollection       clientCertificates, 
46                 X509Certificate                         serverCertificate, 
47                 string                                          targetHost, 
48                 X509CertificateCollection       serverRequestedCertificates);
49
50         public delegate AsymmetricAlgorithm PrivateKeySelectionCallback(
51                 X509Certificate certificate, 
52                 string                  targetHost);
53
54         delegate int ReadDelegate (byte [] buffer, int offset, int count);
55         #endregion
56
57         public class SslClientStream : Stream, IDisposable
58         {
59                 #region Internal Events
60                 
61                 internal event CertificateValidationCallback    ServerCertValidation;
62                 internal event CertificateSelectionCallback             ClientCertSelection;
63                 internal event PrivateKeySelectionCallback              PrivateKeySelection;
64                 
65                 #endregion
66
67                 #region Fields
68
69                 private Stream                                                  innerStream;
70                 private MemoryStream                                    inputBuffer;
71                 private ClientContext                                   context;
72                 private ClientRecordProtocol                    protocol;
73                 private bool                                                    ownsStream;
74                 private bool                                                    disposed;
75                 private bool                                                    checkCertRevocationStatus;
76                 private object                                                  negotiate;
77                 private object                                                  read;
78                 private object                                                  write;
79                 private ReadDelegate                                            rd;
80
81                 #endregion
82
83                 #region Properties
84
85                 public override bool CanRead
86                 {
87                         get { return this.innerStream.CanRead; }
88                 }
89
90                 public override bool CanSeek
91                 {
92                         get { return false; }
93                 }
94
95                 public override bool CanWrite
96                 {
97                         get { return this.innerStream.CanWrite; }
98                 }
99
100                 public override long Length
101                 {
102                         get { throw new NotSupportedException(); }
103                 }
104
105                 public override long Position
106                 {
107                         get { throw new NotSupportedException(); }
108                         set { throw new NotSupportedException(); }
109                 }
110
111                 // required by HttpsClientStream for proxy support
112                 internal Stream InputBuffer {
113                         get { return inputBuffer; }
114                 }
115                 #endregion
116
117                 #region Security Properties
118
119                 public bool CheckCertRevocationStatus 
120                 {
121                         get { return this.checkCertRevocationStatus ; }
122                         set { this.checkCertRevocationStatus = value; }
123                 }
124
125                 public CipherAlgorithmType CipherAlgorithm 
126                 {
127                         get 
128                         { 
129                                 if (this.context.HandshakeState == HandshakeState.Finished)
130                                 {
131                                         return this.context.Cipher.CipherAlgorithmType;
132                                 }
133
134                                 return CipherAlgorithmType.None;
135                         }
136                 }
137                 
138                 public int CipherStrength 
139                 {
140                         get 
141                         { 
142                                 if (this.context.HandshakeState == HandshakeState.Finished)
143                                 {
144                                         return this.context.Cipher.EffectiveKeyBits;
145                                 }
146
147                                 return 0;
148                         }
149                 }
150                 
151                 public X509CertificateCollection ClientCertificates 
152                 {
153                         get { return this.context.ClientSettings.Certificates;}
154                 }
155                 
156                 public HashAlgorithmType HashAlgorithm 
157                 {
158                         get 
159                         { 
160                                 if (this.context.HandshakeState == HandshakeState.Finished)
161                                 {
162                                         return this.context.Cipher.HashAlgorithmType; 
163                                 }
164
165                                 return HashAlgorithmType.None;
166                         }
167                 }
168                 
169                 public int HashStrength
170                 {
171                         get 
172                         { 
173                                 if (this.context.HandshakeState == HandshakeState.Finished)
174                                 {
175                                         return this.context.Cipher.HashSize * 8; 
176                                 }
177
178                                 return 0;
179                         }
180                 }
181                 
182                 public int KeyExchangeStrength 
183                 {
184                         get 
185                         { 
186                                 if (this.context.HandshakeState == HandshakeState.Finished)
187                                 {
188                                         return this.context.ServerSettings.Certificates[0].RSA.KeySize;
189                                 }
190
191                                 return 0;
192                         }
193                 }
194                 
195                 public ExchangeAlgorithmType KeyExchangeAlgorithm 
196                 {
197                         get 
198                         { 
199                                 if (this.context.HandshakeState == HandshakeState.Finished)
200                                 {
201                                         return this.context.Cipher.ExchangeAlgorithmType; 
202                                 }
203
204                                 return ExchangeAlgorithmType.None;
205                         }
206                 }
207                 
208                 public SecurityProtocolType SecurityProtocol 
209                 {
210                         get 
211                         { 
212                                 if (this.context.HandshakeState == HandshakeState.Finished)
213                                 {
214                                         return this.context.SecurityProtocol; 
215                                 }
216
217                                 return 0;
218                         }
219                 }
220                 
221                 public X509Certificate SelectedClientCertificate 
222                 {
223                         get { return this.context.ClientSettings.ClientCertificate; }
224                 }
225
226                 public X509Certificate ServerCertificate 
227                 {
228                         get 
229                         { 
230                                 if (this.context.HandshakeState == HandshakeState.Finished)
231                                 {
232                                         if (this.context.ServerSettings.Certificates != null &&
233                                                 this.context.ServerSettings.Certificates.Count > 0)
234                                         {
235                                                 return new X509Certificate(this.context.ServerSettings.Certificates[0].RawData);
236                                         }
237                                 }
238
239                                 return null;
240                         }
241                 } 
242
243                 // this is used by Mono's certmgr tool to download certificates
244                 internal Mono.Security.X509.X509CertificateCollection ServerCertificates {
245                         get { return context.ServerSettings.Certificates; }
246                 }
247
248                 #endregion
249
250                 #region Callback Properties
251
252                 public CertificateValidationCallback ServerCertValidationDelegate
253                 {
254                         get { return this.ServerCertValidation; }
255                         set { this.ServerCertValidation = value; }                      
256                 }
257
258                 public CertificateSelectionCallback ClientCertSelectionDelegate 
259                 {
260                         get { return this.ClientCertSelection; }
261                         set { this.ClientCertSelection = value; }
262                 }
263
264                 public PrivateKeySelectionCallback PrivateKeyCertSelectionDelegate 
265                 {
266                         get { return this.PrivateKeySelection; }
267                         set { this.PrivateKeySelection = value; }
268                 }
269
270                 #endregion
271
272                 #region Constructors
273                 
274                 public SslClientStream(
275                         Stream  stream, 
276                         string  targetHost, 
277                         bool    ownsStream) 
278                         : this(
279                                 stream, targetHost, ownsStream, 
280                                 SecurityProtocolType.Default, null)
281                 {
282                 }
283                 
284                 public SslClientStream(
285                         Stream                          stream, 
286                         string                          targetHost, 
287                         X509Certificate         clientCertificate) 
288                         : this(
289                                 stream, targetHost, false, SecurityProtocolType.Default, 
290                                 new X509CertificateCollection(new X509Certificate[]{clientCertificate}))
291                 {
292                 }
293
294                 public SslClientStream(
295                         Stream                                          stream,
296                         string                                          targetHost, 
297                         X509CertificateCollection clientCertificates) : 
298                         this(
299                                 stream, targetHost, false, SecurityProtocolType.Default, 
300                                 clientCertificates)
301                 {
302                 }
303
304                 public SslClientStream(
305                         Stream                                  stream,
306                         string                                  targetHost,
307                         bool                                    ownsStream,
308                         SecurityProtocolType    securityProtocolType) 
309                         : this(
310                                 stream, targetHost, ownsStream, securityProtocolType,
311                                 new X509CertificateCollection())
312                 {
313                 }
314
315                 public SslClientStream(
316                         Stream                                          stream,
317                         string                                          targetHost,
318                         bool                                            ownsStream,
319                         SecurityProtocolType            securityProtocolType,
320                         X509CertificateCollection       clientCertificates)
321                 {
322                         if (stream == null)
323                         {
324                                 throw new ArgumentNullException("stream is null.");
325                         }
326                         if (!stream.CanRead || !stream.CanWrite)
327                         {
328                                 throw new ArgumentNullException("stream is not both readable and writable.");
329                         }
330                         if (targetHost == null || targetHost.Length == 0)
331                         {
332                                 throw new ArgumentNullException("targetHost is null or an empty string.");
333                         }
334
335                         this.context = new ClientContext(
336                                 this,
337                                 securityProtocolType, 
338                                 targetHost, 
339                                 clientCertificates);
340
341                         this.inputBuffer        = new MemoryStream();
342                         this.innerStream        = stream;
343                         this.ownsStream         = ownsStream;
344                         this.negotiate                  = new object ();
345                         this.read                       = new object ();
346                         this.write                      = new object ();
347                         this.protocol           = new ClientRecordProtocol(innerStream, context);
348                 }
349
350                 #endregion
351
352                 #region Finalizer
353
354                 ~SslClientStream()
355                 {
356                         this.Dispose(false);
357                 }
358
359                 #endregion
360
361         #region IDisposable Methods
362
363                 void IDisposable.Dispose()
364                 {
365                         this.Dispose(true);
366                         GC.SuppressFinalize(this);
367                 }
368
369                 protected virtual void Dispose(bool disposing)
370                 {
371                         if (!this.disposed)
372                         {
373                                 if (disposing)
374                                 {
375                                         if (this.innerStream != null)
376                                         {
377                                                 if (this.context.HandshakeState == HandshakeState.Finished &&
378                                                         !this.context.ConnectionEnd)
379                                                 {
380                                                         // Write close notify                                                   
381                                                         this.protocol.SendAlert(AlertDescription.CloseNotify);
382                                                 }
383
384                                                 if (this.ownsStream)
385                                                 {
386                                                         // Close inner stream
387                                                         this.innerStream.Close();
388                                                 }
389                                         }
390                                         this.ownsStream                         = false;
391                                         this.innerStream                        = null;
392                                         this.ClientCertSelection        = null;
393                                         this.ServerCertValidation       = null;
394                                         this.PrivateKeySelection        = null;
395                                 }
396
397                                 this.disposed = true;
398                         }
399                 }
400
401                 #endregion
402
403                 #region Methods
404
405                 public override IAsyncResult BeginRead(
406                         byte[]                  buffer,
407                         int                             offset,
408                         int                             count,
409                         AsyncCallback   callback,
410                         object                  state)
411                 {\r
412                         this.checkDisposed ();
413                         
414                         if (buffer == null)
415                         {
416                                 throw new ArgumentNullException("buffer is a null reference.");
417                         }
418                         if (offset < 0)
419                         {
420                                 throw new ArgumentOutOfRangeException("offset is less than 0.");
421                         }
422                         if (offset > buffer.Length)
423                         {
424                                 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
425                         }
426                         if (count < 0)
427                         {
428                                 throw new ArgumentOutOfRangeException("count is less than 0.");
429                         }
430                         if (count > (buffer.Length - offset))
431                         {
432                                 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
433                         }
434
435                         if (this.context.HandshakeState == HandshakeState.None)
436                         {
437                                 // Note: Async code may have problem if they can't ensure that
438                                 // the Negotiate phase isn't done during a read operation.
439                                 // System.Net.HttpWebRequest protects itself from that problem
440                                 lock (this.negotiate)
441                                 {
442                                         if (this.context.HandshakeState == HandshakeState.None)
443                                         {
444                                                 this.NegotiateHandshake();
445                                         }
446                                 }
447                         }
448
449                         IAsyncResult asyncResult = null;
450
451                         lock (this.read)
452                         {
453                                 try
454                                 {
455                                         // If actual buffer is full readed reset it
456                                         if (this.inputBuffer.Position == this.inputBuffer.Length &&
457                                                 this.inputBuffer.Length > 0)
458                                         {
459                                                 this.resetBuffer();
460                                         }
461
462                                         if (!this.context.ConnectionEnd)
463                                         {
464                                                 if ((this.inputBuffer.Length == this.inputBuffer.Position) && (count > 0))\r
465                                                 {
466                                                         // bigger than max record length for SSL/TLS\r
467                                                         byte[] recbuf = new byte[16384]; \r
468
469                                                         // this will read data from the network until we have (at least) one
470                                                         // record to send back to the caller\r
471                                                         this.innerStream.BeginRead (recbuf, 0, recbuf.Length, 
472                                                                 new AsyncCallback (NetworkReadCallback), recbuf);\r
473 \r
474                                                         if (!recordEvent.WaitOne (300000, false)) // 5 minutes\r
475                                                         {
476                                                                 // FAILSAFE
477                                                                 DebugHelper.WriteLine ("TIMEOUT length {0}, position {1}, count {2} - {3}\n{4}", 
478                                                                         this.inputBuffer.Length, this.inputBuffer.Position, count, GetHashCode (),
479                                                                         Environment.StackTrace);\r
480                                                                 throw new TlsException (AlertDescription.InternalError);\r
481                                                         }
482                                                 }\r
483                                         }
484
485                                         // return the record(s) to the caller
486                                         rd = new ReadDelegate (this.inputBuffer.Read);
487                                         asyncResult = rd.BeginInvoke (buffer, offset, count, callback, state);
488                                 }
489                                 catch (TlsException ex)
490                                 {
491                                         this.protocol.SendAlert(ex.Alert);
492                                         this.Close();
493
494                                         throw new IOException("The authentication or decryption has failed.");
495                                 }
496                                 catch (Exception)
497                                 {
498                                         throw new IOException("IO exception during read.");
499                                 }
500
501                         }
502
503                         return asyncResult;
504                 }\r
505 \r
506                 private ManualResetEvent recordEvent = new ManualResetEvent (false);\r
507                 private MemoryStream recordStream = new MemoryStream ();\r
508
509                 // read encrypted data until we have enough to decrypt (at least) one
510                 // record and return are the records (may be more than one) we have\r
511                 private void NetworkReadCallback (IAsyncResult result)\r
512                 {\r
513                         byte[] recbuf = (byte[])result.AsyncState;\r
514                         int n = innerStream.EndRead (result);
515                         if (n > 0)\r
516                         {\r
517                                 // add the just received data to the waiting data\r
518                                 recordStream.Write (recbuf, 0, n);\r
519                         }\r
520 \r
521                         bool dataToReturn = false;\r
522                         long pos = recordStream.Position;
523 \r
524                         recordStream.Position = 0;\r
525                         byte[] record = null;
526
527                         // don't try to decode record unless we have at least 5 bytes\r
528                         // i.e. type (1), protocol (2) and length (2)\r
529                         if (recordStream.Length >= 5)\r
530                         {\r
531                                 record = this.protocol.ReceiveRecord (recordStream);\r
532                         }\r
533
534                         // a record of 0 length is valid (and there may be more record after it)
535                         while (record != null)\r
536                         {\r
537                                 // we probably received more stuff after the record, and we must keep it!\r
538                                 long remainder = recordStream.Length - recordStream.Position;\r
539                                 byte[] outofrecord = null;\r
540                                 if (remainder > 0)\r
541                                 {\r
542                                         outofrecord = new byte[remainder];\r
543                                         recordStream.Read (outofrecord, 0, outofrecord.Length);\r
544                                 }\r
545 \r
546                                 long position = this.inputBuffer.Position;\r
547
548                                 if (record.Length > 0)
549                                 {\r
550                                         // Write new data to the inputBuffer\r
551                                         this.inputBuffer.Seek (0, SeekOrigin.End);\r
552                                         this.inputBuffer.Write (record, 0, record.Length);\r
553 \r
554                                         // Restore buffer position\r
555                                         this.inputBuffer.Seek (position, SeekOrigin.Begin);
556                                         dataToReturn = true;
557                                 }\r
558 \r
559                                 recordStream.SetLength (0);\r
560                                 record = null;\r
561 \r
562                                 if (remainder > 0)\r
563                                 {\r
564                                         recordStream.Write (outofrecord, 0, outofrecord.Length);\r
565                                         // type (1), protocol (2) and length (2)\r
566                                         if (recordStream.Length >= 5)\r
567                                         {\r
568                                                 // try to see if another record is available\r
569                                                 recordStream.Position = 0;\r
570                                                 record = this.protocol.ReceiveRecord (recordStream);\r
571                                                 if (record == null)\r
572                                                         pos = recordStream.Length;\r
573                                         }\r
574                                         else\r
575                                                 pos = remainder;\r
576                                 }\r
577                                 else\r
578                                         pos = 0;\r
579                         }\r
580
581                         if (!dataToReturn && (n > 0))\r
582                         {
583                                 // there is no record to return to caller and (possibly) more data waiting\r
584                                 // so continue reading from network (and appending to stream)\r
585                                 recordStream.Position = recordStream.Length;\r
586                                 this.innerStream.BeginRead (recbuf, 0, recbuf.Length, 
587                                         new AsyncCallback (NetworkReadCallback), recbuf);\r
588                         }\r
589                         else\r
590                         {
591                                 // we have record(s) to return -or- no more available to read from network
592                                 // reset position for further reading\r
593                                 recordStream.Position = pos;\r
594                                 recordEvent.Set ();\r
595                         }\r
596                 }
597
598                 public override IAsyncResult BeginWrite(
599                         byte[]                  buffer,
600                         int                             offset,
601                         int                             count,
602                         AsyncCallback   callback,
603                         object                  state)
604                 {
605                         this.checkDisposed();
606
607                         if (buffer == null)
608                         {
609                                 throw new ArgumentNullException("buffer is a null reference.");
610                         }
611                         if (offset < 0)
612                         {
613                                 throw new ArgumentOutOfRangeException("offset is less than 0.");
614                         }
615                         if (offset > buffer.Length)
616                         {
617                                 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
618                         }
619                         if (count < 0)
620                         {
621                                 throw new ArgumentOutOfRangeException("count is less than 0.");
622                         }
623                         if (count > (buffer.Length - offset))
624                         {
625                                 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
626                         }
627
628                         if (this.context.HandshakeState == HandshakeState.None)
629                         {
630                                 lock (this.negotiate)
631                                 {
632                                         if (this.context.HandshakeState == HandshakeState.None)
633                                         {
634                                                 this.NegotiateHandshake();
635                                         }
636                                 }
637                         }
638
639                         IAsyncResult asyncResult;
640
641                         lock (this.write)
642                         {
643                                 try
644                                 {
645                                         // Send the buffer as a TLS record
646                                         
647                                         byte[] record = this.protocol.EncodeRecord(
648                                                 ContentType.ApplicationData, buffer, offset, count);
649                                 
650                                         asyncResult = this.innerStream.BeginWrite(
651                                                 record, 0, record.Length, callback, state);
652                                 }
653                                 catch (TlsException ex)
654                                 {
655                                         this.protocol.SendAlert(ex.Alert);
656                                         this.Close();
657
658                                         throw new IOException("The authentication or decryption has failed.");
659                                 }
660                                 catch (Exception)
661                                 {
662                                         throw new IOException("IO exception during Write.");
663                                 }
664                         }
665
666                         return asyncResult;
667                 }
668
669                 public override int EndRead(IAsyncResult asyncResult)
670                 {
671                         this.checkDisposed();
672
673                         if (asyncResult == null)
674                         {
675                                 throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginRead.");
676                         }\r
677
678                         recordEvent.Reset ();
679                         return this.rd.EndInvoke (asyncResult);
680                 }
681
682                 public override void EndWrite(IAsyncResult asyncResult)
683                 {
684                         this.checkDisposed();
685
686                         if (asyncResult == null)
687                         {
688                                 throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginRead.");
689                         }
690
691                         this.innerStream.EndWrite (asyncResult);
692                 }
693
694                 public override void Close()
695                 {
696                         ((IDisposable)this).Dispose();
697                 }
698
699                 public override void Flush()
700                 {
701                         this.checkDisposed();
702
703                         this.innerStream.Flush();
704                 }
705
706                 public int Read(byte[] buffer)
707                 {
708                         return this.Read(buffer, 0, buffer.Length);
709                 }
710
711                 public override int Read(byte[] buffer, int offset, int count)
712                 {
713                         IAsyncResult res = this.BeginRead(buffer, offset, count, null, null);
714
715                         return this.EndRead(res);
716                 }
717
718                 public override long Seek(long offset, SeekOrigin origin)
719                 {
720                         throw new NotSupportedException();
721                 }
722                 
723                 public override void SetLength(long value)
724                 {
725                         throw new NotSupportedException();
726                 }
727
728                 public void Write(byte[] buffer)
729                 {
730                         this.Write(buffer, 0, buffer.Length);
731                 }
732
733                 public override void Write(byte[] buffer, int offset, int count)
734                 {
735                         IAsyncResult res = this.BeginWrite (buffer, offset, count, null, null);
736
737                         this.EndWrite(res);
738                 }
739
740                 #endregion
741
742                 #region Misc Methods
743
744                 private void resetBuffer()
745                 {
746                         this.inputBuffer.SetLength(0);
747                         this.inputBuffer.Position = 0;
748                 }
749
750                 private void checkDisposed()
751                 {
752                         if (this.disposed)
753                         {
754                                 throw new ObjectDisposedException("The SslClientStream is closed.");
755                         }
756                 }
757
758                 #endregion
759
760                 #region Handsake Methods
761
762                 /*
763                         Client                                                                                  Server
764
765                         ClientHello                 -------->
766                                                                                                                         ServerHello
767                                                                                                                         Certificate*
768                                                                                                                         ServerKeyExchange*
769                                                                                                                         CertificateRequest*
770                                                                                 <--------                       ServerHelloDone
771                         Certificate*
772                         ClientKeyExchange
773                         CertificateVerify*
774                         [ChangeCipherSpec]
775                         Finished                    -------->
776                                                                                                                         [ChangeCipherSpec]
777                                                                                 <--------           Finished
778                         Application Data            <------->                   Application Data
779
780                                         Fig. 1 - Message flow for a full handshake              
781                 */
782
783                 internal void NegotiateHandshake()
784                 {
785                         try
786                         {
787                                 if (this.context.HandshakeState != HandshakeState.None)
788                                 {
789                                         this.context.Clear();
790                                 }
791
792                                 // Obtain supported cipher suites
793                                 this.context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(this.context.SecurityProtocol);
794
795                                 // Send client hello
796                                 this.protocol.SendRecord(HandshakeType.ClientHello);
797
798                                 // Read server response
799                                 while (this.context.LastHandshakeMsg != HandshakeType.ServerHelloDone)
800                                 {
801                                         // Read next record\r
802                                         this.protocol.ReceiveRecord (this.innerStream);
803                                 }
804
805                                 // Send client certificate if requested
806                                 if (this.context.ServerSettings.CertificateRequest)
807                                 {
808                                         this.protocol.SendRecord(HandshakeType.Certificate);
809                                 }
810
811                                 // Send Client Key Exchange
812                                 this.protocol.SendRecord(HandshakeType.ClientKeyExchange);
813
814                                 // Now initialize session cipher with the generated keys
815                                 this.context.Cipher.InitializeCipher();
816
817                                 // Send certificate verify if requested
818                                 if (this.context.ServerSettings.CertificateRequest)
819                                 {
820                                         this.protocol.SendRecord(HandshakeType.CertificateVerify);
821                                 }
822
823                                 // Send Cipher Spec protocol
824                                 this.protocol.SendChangeCipherSpec();                   
825                         
826                                 // Read record until server finished is received
827                                 while (this.context.HandshakeState != HandshakeState.Finished)
828                                 {
829                                         // If all goes well this will process messages:
830                                         //              Change Cipher Spec
831                                         //              Server finished\r
832                                         this.protocol.ReceiveRecord (this.innerStream);
833                                 }
834
835                                 // Clear Key Info
836                                 this.context.ClearKeyInfo();
837                         }
838                         catch (TlsException ex)
839                         {
840                                 this.protocol.SendAlert(ex.Alert);
841                                 this.Close();
842
843                                 throw new IOException("The authentication or decryption has failed.");
844                         }
845                         catch (Exception)
846                         {
847                                 this.protocol.SendAlert(AlertDescription.InternalError);
848                                 this.Close();
849
850                                 throw new IOException("The authentication or decryption has failed.");
851                         }
852                 }
853
854                 #endregion
855
856                 #region Event Methods
857
858                 internal virtual bool RaiseServerCertificateValidation(
859                         X509Certificate certificate, 
860                         int[]                   certificateErrors)
861                 {
862                         if (this.ServerCertValidation != null)
863                         {
864                                 return this.ServerCertValidation(certificate, certificateErrors);
865                         }
866
867                         return (certificateErrors != null && certificateErrors.Length == 0);
868                 }
869
870                 internal X509Certificate RaiseClientCertificateSelection(
871                         X509CertificateCollection       clientCertificates, 
872                         X509Certificate                         serverCertificate, 
873                         string                                          targetHost, 
874                         X509CertificateCollection       serverRequestedCertificates)
875                 {
876                         if (this.ClientCertSelection != null)
877                         {
878                                 return this.ClientCertSelection(
879                                         clientCertificates,
880                                         serverCertificate,
881                                         targetHost,
882                                         serverRequestedCertificates);
883                         }
884
885                         return null;
886                 }
887
888                 internal AsymmetricAlgorithm RaisePrivateKeySelection(
889                         X509Certificate certificate, 
890                         string                  targetHost)
891                 {
892                         if (this.PrivateKeySelection != null)
893                         {
894                                 return this.PrivateKeySelection(certificate, targetHost);
895                         }
896
897                         return null;
898                 }
899
900                 #endregion
901         }
902 }