1 // Transport Security Layer (TLS)
2 // Copyright (c) 2003-2004 Carlos Guzman Alvarez
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:
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
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.
26 using System
.Collections
;
29 using System
.Net
.Sockets
;
30 using System
.Security
.Cryptography
;
31 using System
.Security
.Cryptography
.X509Certificates
;
32 using System
.Threading
;
34 using Mono
.Security
.Protocol
.Tls
.Handshake
;
36 namespace Mono
.Security
.Protocol
.Tls
40 public delegate bool CertificateValidationCallback(
41 X509Certificate certificate
,
42 int[] certificateErrors
);
44 public class ValidationResult
{
49 public ValidationResult (bool trusted
, bool user_denied
, int error_code
)
51 this.trusted
= trusted
;
52 this.user_denied
= user_denied
;
53 this.error_code
= error_code
;
57 get { return trusted; }
60 public bool UserDenied
{
61 get { return user_denied; }
64 public int ErrorCode
{
65 get { return error_code; }
69 public delegate ValidationResult
CertificateValidationCallback2 (Mono
.Security
.X509
.X509CertificateCollection collection
);
71 public delegate X509Certificate
CertificateSelectionCallback(
72 X509CertificateCollection clientCertificates
,
73 X509Certificate serverCertificate
,
75 X509CertificateCollection serverRequestedCertificates
);
77 public delegate AsymmetricAlgorithm
PrivateKeySelectionCallback(
78 X509Certificate certificate
,
83 public class SslClientStream
: SslStreamBase
85 #region Internal Events
87 internal event CertificateValidationCallback ServerCertValidation
;
88 internal event CertificateSelectionCallback ClientCertSelection
;
89 internal event PrivateKeySelectionCallback PrivateKeySelection
;
95 // required by HttpsClientStream for proxy support
96 internal Stream InputBuffer
98 get { return base.inputBuffer; }
101 public X509CertificateCollection ClientCertificates
103 get { return this.context.ClientSettings.Certificates; }
106 public X509Certificate SelectedClientCertificate
108 get { return this.context.ClientSettings.ClientCertificate; }
113 #region Callback Properties
115 public CertificateValidationCallback ServerCertValidationDelegate
117 get { return this.ServerCertValidation; }
118 set { this.ServerCertValidation = value; }
121 public CertificateSelectionCallback ClientCertSelectionDelegate
123 get { return this.ClientCertSelection; }
124 set { this.ClientCertSelection = value; }
127 public PrivateKeySelectionCallback PrivateKeyCertSelectionDelegate
129 get { return this.PrivateKeySelection; }
130 set { this.PrivateKeySelection = value; }
135 public event CertificateValidationCallback2 ServerCertValidation2
;
138 public SslClientStream(
143 stream
, targetHost
, ownsStream
,
144 SecurityProtocolType
.Default
, null)
148 public SslClientStream(
151 X509Certificate clientCertificate
)
153 stream
, targetHost
, false, SecurityProtocolType
.Default
,
154 new X509CertificateCollection(new X509Certificate
[]{clientCertificate}
))
158 public SslClientStream(
161 X509CertificateCollection clientCertificates
) :
163 stream
, targetHost
, false, SecurityProtocolType
.Default
,
168 public SslClientStream(
172 SecurityProtocolType securityProtocolType
)
174 stream
, targetHost
, ownsStream
, securityProtocolType
,
175 new X509CertificateCollection())
179 public SslClientStream(
183 SecurityProtocolType securityProtocolType
,
184 X509CertificateCollection clientCertificates
):
185 base(stream
, ownsStream
)
187 if (targetHost
== null || targetHost
.Length
== 0)
189 throw new ArgumentNullException("targetHost is null or an empty string.");
192 this.context
= new ClientContext(
194 securityProtocolType
,
198 this.protocol
= new ClientRecordProtocol(innerStream
, (ClientContext
)this.context
);
212 #region IDisposable Methods
214 protected override void Dispose(bool disposing
)
216 base.Dispose(disposing
);
220 this.ServerCertValidation
= null;
221 this.ClientCertSelection
= null;
222 this.PrivateKeySelection
= null;
223 this.ServerCertValidation2
= null;
229 #region Handshake Methods
234 ClientHello -------->
239 <-------- ServerHelloDone
247 Application Data <-------> Application Data
249 Fig. 1 - Message flow for a full handshake
252 internal override IAsyncResult
OnBeginNegotiateHandshake(AsyncCallback callback
, object state
)
256 if (this.context
.HandshakeState
!= HandshakeState
.None
)
258 this.context
.Clear();
261 // Obtain supported cipher suites
262 this.context
.SupportedCiphers
= CipherSuiteFactory
.GetSupportedCiphers(this.context
.SecurityProtocol
);
264 // Set handshake state
265 this.context
.HandshakeState
= HandshakeState
.Started
;
268 return this.protocol
.BeginSendRecord(HandshakeType
.ClientHello
, callback
, state
);
270 catch (TlsException ex
)
272 this.protocol
.SendAlert(ex
.Alert
);
274 throw new IOException("The authentication or decryption has failed.", ex
);
278 this.protocol
.SendAlert(AlertDescription
.InternalError
);
280 throw new IOException("The authentication or decryption has failed.", ex
);
284 private void SafeReceiveRecord (Stream s
)
286 byte[] record
= this.protocol
.ReceiveRecord (s
);
287 if ((record
== null) || (record
.Length
== 0)) {
288 throw new TlsException (
289 AlertDescription
.HandshakeFailiure
,
290 "The server stopped the handshake.");
294 internal override void OnNegotiateHandshakeCallback(IAsyncResult asyncResult
)
296 this.protocol
.EndSendRecord(asyncResult
);
298 // Read server response
299 while (this.context
.LastHandshakeMsg
!= HandshakeType
.ServerHelloDone
)
302 SafeReceiveRecord (this.innerStream
);
304 // special case for abbreviated handshake where no ServerHelloDone is sent from the server
305 if (this.context
.AbbreviatedHandshake
&& (this.context
.LastHandshakeMsg
== HandshakeType
.ServerHello
))
309 // the handshake is much easier if we can reuse a previous session settings
310 if (this.context
.AbbreviatedHandshake
)
312 ClientSessionCache
.SetContextFromCache (this.context
);
313 this.context
.Negotiating
.Cipher
.ComputeKeys ();
314 this.context
.Negotiating
.Cipher
.InitializeCipher ();
316 // Send Cipher Spec protocol
317 this.protocol
.SendChangeCipherSpec ();
319 // Read record until server finished is received
320 while (this.context
.HandshakeState
!= HandshakeState
.Finished
)
322 // If all goes well this will process messages:
323 // Change Cipher Spec
325 SafeReceiveRecord (this.innerStream
);
328 // Send Finished message
329 this.protocol
.SendRecord (HandshakeType
.Finished
);
333 // Send client certificate if requested
334 // even if the server ask for it it _may_ still be optional
335 bool clientCertificate
= this.context
.ServerSettings
.CertificateRequest
;
337 // NOTE: sadly SSL3 and TLS1 differs in how they handle this and
338 // the current design doesn't allow a very cute way to handle
339 // SSL3 alert warning for NoCertificate (41).
340 if (this.context
.SecurityProtocol
== SecurityProtocolType
.Ssl3
)
342 clientCertificate
= ((this.context
.ClientSettings
.Certificates
!= null) &&
343 (this.context
.ClientSettings
.Certificates
.Count
> 0));
344 // this works well with OpenSSL (but only for SSL3)
347 if (clientCertificate
)
349 this.protocol
.SendRecord(HandshakeType
.Certificate
);
352 // Send Client Key Exchange
353 this.protocol
.SendRecord(HandshakeType
.ClientKeyExchange
);
355 // Now initialize session cipher with the generated keys
356 this.context
.Negotiating
.Cipher
.InitializeCipher();
358 // Send certificate verify if requested (optional)
359 if (clientCertificate
&& (this.context
.ClientSettings
.ClientCertificate
!= null))
361 this.protocol
.SendRecord(HandshakeType
.CertificateVerify
);
364 // Send Cipher Spec protocol
365 this.protocol
.SendChangeCipherSpec ();
367 // Send Finished message
368 this.protocol
.SendRecord (HandshakeType
.Finished
);
370 // Read record until server finished is received
371 while (this.context
.HandshakeState
!= HandshakeState
.Finished
) {
372 // If all goes well this will process messages:
373 // Change Cipher Spec
375 SafeReceiveRecord (this.innerStream
);
379 // Reset Handshake messages information
380 this.context
.HandshakeMessages
.Reset ();
383 this.context
.ClearKeyInfo();
389 #region Event Methods
391 internal override X509Certificate
OnLocalCertificateSelection(X509CertificateCollection clientCertificates
, X509Certificate serverCertificate
, string targetHost
, X509CertificateCollection serverRequestedCertificates
)
393 if (this.ClientCertSelection
!= null)
395 return this.ClientCertSelection(
399 serverRequestedCertificates
);
405 internal override bool HaveRemoteValidation2Callback
{
406 get { return ServerCertValidation2 != null; }
409 internal override ValidationResult
OnRemoteCertificateValidation2 (Mono
.Security
.X509
.X509CertificateCollection collection
)
411 CertificateValidationCallback2 cb
= ServerCertValidation2
;
413 return cb (collection
);
417 internal override bool OnRemoteCertificateValidation(X509Certificate certificate
, int[] errors
)
419 if (this.ServerCertValidation
!= null)
421 return this.ServerCertValidation(certificate
, errors
);
424 return (errors
!= null && errors
.Length
== 0);
427 internal virtual bool RaiseServerCertificateValidation(
428 X509Certificate certificate
,
429 int[] certificateErrors
)
431 return base.RaiseRemoteCertificateValidation(certificate
, certificateErrors
);
434 internal virtual ValidationResult
RaiseServerCertificateValidation2 (Mono
.Security
.X509
.X509CertificateCollection collection
)
436 return base.RaiseRemoteCertificateValidation2 (collection
);
439 internal X509Certificate
RaiseClientCertificateSelection(
440 X509CertificateCollection clientCertificates
,
441 X509Certificate serverCertificate
,
443 X509CertificateCollection serverRequestedCertificates
)
445 return base.RaiseLocalCertificateSelection(clientCertificates
, serverCertificate
, targetHost
, serverRequestedCertificates
);
448 internal override AsymmetricAlgorithm
OnLocalPrivateKeySelection(X509Certificate certificate
, string targetHost
)
450 if (this.PrivateKeySelection
!= null)
452 return this.PrivateKeySelection(certificate
, targetHost
);
458 internal AsymmetricAlgorithm
RaisePrivateKeySelection(
459 X509Certificate certificate
,
462 return base.RaiseLocalPrivateKeySelection(certificate
, targetHost
);