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