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