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