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
45 delegate bool CertificateValidationCallback(
46 X509Certificate certificate,
47 int[] certificateErrors);
54 class ValidationResult {
59 public ValidationResult (bool trusted, bool user_denied, int error_code)
61 this.trusted = trusted;
62 this.user_denied = user_denied;
63 this.error_code = error_code;
67 get { return trusted; }
70 public bool UserDenied {
71 get { return user_denied; }
74 public int ErrorCode {
75 get { return error_code; }
84 delegate ValidationResult CertificateValidationCallback2 (Mono.Security.X509.X509CertificateCollection collection);
91 delegate X509Certificate CertificateSelectionCallback(
92 X509CertificateCollection clientCertificates,
93 X509Certificate serverCertificate,
95 X509CertificateCollection serverRequestedCertificates);
102 delegate AsymmetricAlgorithm PrivateKeySelectionCallback(
103 X509Certificate certificate,
113 class SslClientStream : SslStreamBase
115 #region Internal Events
117 internal event CertificateValidationCallback ServerCertValidation;
118 internal event CertificateSelectionCallback ClientCertSelection;
119 internal event PrivateKeySelectionCallback PrivateKeySelection;
125 // required by HttpsClientStream for proxy support
126 internal Stream InputBuffer
128 get { return base.inputBuffer; }
131 public X509CertificateCollection ClientCertificates
133 get { return this.context.ClientSettings.Certificates; }
136 public X509Certificate SelectedClientCertificate
138 get { return this.context.ClientSettings.ClientCertificate; }
143 #region Callback Properties
145 public CertificateValidationCallback ServerCertValidationDelegate
147 get { return this.ServerCertValidation; }
148 set { this.ServerCertValidation = value; }
151 public CertificateSelectionCallback ClientCertSelectionDelegate
153 get { return this.ClientCertSelection; }
154 set { this.ClientCertSelection = value; }
157 public PrivateKeySelectionCallback PrivateKeyCertSelectionDelegate
159 get { return this.PrivateKeySelection; }
160 set { this.PrivateKeySelection = value; }
165 public event CertificateValidationCallback2 ServerCertValidation2;
169 public SslClientStream(
174 stream, targetHost, ownsStream,
175 SecurityProtocolType.Default, null)
179 public SslClientStream(
182 X509Certificate clientCertificate)
184 stream, targetHost, false, SecurityProtocolType.Default,
185 new X509CertificateCollection(new X509Certificate[]{clientCertificate}))
189 public SslClientStream(
192 X509CertificateCollection clientCertificates) :
194 stream, targetHost, false, SecurityProtocolType.Default,
199 public SslClientStream(
203 SecurityProtocolType securityProtocolType)
205 stream, targetHost, ownsStream, securityProtocolType,
206 new X509CertificateCollection())
210 public SslClientStream(
214 SecurityProtocolType securityProtocolType,
215 X509CertificateCollection clientCertificates):
216 base(stream, ownsStream)
218 if (targetHost == null || targetHost.Length == 0)
220 throw new ArgumentNullException("targetHost is null or an empty string.");
223 this.context = new ClientContext(
225 securityProtocolType,
229 this.protocol = new ClientRecordProtocol(innerStream, (ClientContext)this.context);
243 #region IDisposable Methods
245 protected override void Dispose(bool disposing)
247 base.Dispose(disposing);
251 this.ServerCertValidation = null;
252 this.ClientCertSelection = null;
253 this.PrivateKeySelection = null;
254 this.ServerCertValidation2 = null;
260 #region Handshake Methods
265 ClientHello -------->
270 <-------- ServerHelloDone
278 Application Data <-------> Application Data
280 Fig. 1 - Message flow for a full handshake
283 internal override IAsyncResult OnBeginNegotiateHandshake(AsyncCallback callback, object state)
287 if (this.context.HandshakeState != HandshakeState.None)
289 this.context.Clear();
292 // Obtain supported cipher suites
293 this.context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(this.context.SecurityProtocol);
295 // Set handshake state
296 this.context.HandshakeState = HandshakeState.Started;
299 return this.protocol.BeginSendRecord(HandshakeType.ClientHello, callback, state);
301 catch (TlsException ex)
303 this.protocol.SendAlert(ex.Alert);
305 throw new IOException("The authentication or decryption has failed.", ex);
309 this.protocol.SendAlert(AlertDescription.InternalError);
311 throw new IOException("The authentication or decryption has failed.", ex);
315 private void SafeReceiveRecord (Stream s, bool ignoreEmpty = false)
317 byte[] record = this.protocol.ReceiveRecord (s);
318 if (!ignoreEmpty && ((record == null) || (record.Length == 0))) {
319 throw new TlsException (
320 AlertDescription.HandshakeFailiure,
321 "The server stopped the handshake.");
325 internal override void OnNegotiateHandshakeCallback(IAsyncResult asyncResult)
327 this.protocol.EndSendRecord(asyncResult);
329 // Read server response
330 while (this.context.LastHandshakeMsg != HandshakeType.ServerHelloDone)
332 // Read next record (skip empty, e.g. warnings alerts)
333 SafeReceiveRecord (this.innerStream, true);
335 // special case for abbreviated handshake where no ServerHelloDone is sent from the server
336 if (this.context.AbbreviatedHandshake && (this.context.LastHandshakeMsg == HandshakeType.ServerHello))
340 // the handshake is much easier if we can reuse a previous session settings
341 if (this.context.AbbreviatedHandshake)
343 ClientSessionCache.SetContextFromCache (this.context);
344 this.context.Negotiating.Cipher.ComputeKeys ();
345 this.context.Negotiating.Cipher.InitializeCipher ();
347 // Send Cipher Spec protocol
348 this.protocol.SendChangeCipherSpec ();
350 // Read record until server finished is received
351 while (this.context.HandshakeState != HandshakeState.Finished)
353 // If all goes well this will process messages:
354 // Change Cipher Spec
356 SafeReceiveRecord (this.innerStream);
359 // Send Finished message
360 this.protocol.SendRecord (HandshakeType.Finished);
364 // Send client certificate if requested
365 // even if the server ask for it it _may_ still be optional
366 bool clientCertificate = this.context.ServerSettings.CertificateRequest;
368 // NOTE: sadly SSL3 and TLS1 differs in how they handle this and
369 // the current design doesn't allow a very cute way to handle
370 // SSL3 alert warning for NoCertificate (41).
371 if (this.context.SecurityProtocol == SecurityProtocolType.Ssl3)
373 clientCertificate = ((this.context.ClientSettings.Certificates != null) &&
374 (this.context.ClientSettings.Certificates.Count > 0));
375 // this works well with OpenSSL (but only for SSL3)
378 if (clientCertificate)
380 this.protocol.SendRecord(HandshakeType.Certificate);
383 // Send Client Key Exchange
384 this.protocol.SendRecord(HandshakeType.ClientKeyExchange);
386 // Now initialize session cipher with the generated keys
387 this.context.Negotiating.Cipher.InitializeCipher();
389 // Send certificate verify if requested (optional)
390 if (clientCertificate && (this.context.ClientSettings.ClientCertificate != null))
392 this.protocol.SendRecord(HandshakeType.CertificateVerify);
395 // Send Cipher Spec protocol
396 this.protocol.SendChangeCipherSpec ();
398 // Send Finished message
399 this.protocol.SendRecord (HandshakeType.Finished);
401 // Read record until server finished is received
402 while (this.context.HandshakeState != HandshakeState.Finished) {
403 // If all goes well this will process messages:
404 // Change Cipher Spec
406 SafeReceiveRecord (this.innerStream);
410 // Reset Handshake messages information
411 this.context.HandshakeMessages.Reset ();
414 this.context.ClearKeyInfo();
420 #region Event Methods
422 internal override X509Certificate OnLocalCertificateSelection(X509CertificateCollection clientCertificates, X509Certificate serverCertificate, string targetHost, X509CertificateCollection serverRequestedCertificates)
424 if (this.ClientCertSelection != null)
426 return this.ClientCertSelection(
430 serverRequestedCertificates);
436 internal override bool HaveRemoteValidation2Callback {
437 get { return ServerCertValidation2 != null; }
440 internal override ValidationResult OnRemoteCertificateValidation2 (Mono.Security.X509.X509CertificateCollection collection)
442 CertificateValidationCallback2 cb = ServerCertValidation2;
444 return cb (collection);
448 internal override bool OnRemoteCertificateValidation(X509Certificate certificate, int[] errors)
450 if (this.ServerCertValidation != null)
452 return this.ServerCertValidation(certificate, errors);
455 return (errors != null && errors.Length == 0);
458 internal virtual bool RaiseServerCertificateValidation(
459 X509Certificate certificate,
460 int[] certificateErrors)
462 return base.RaiseRemoteCertificateValidation(certificate, certificateErrors);
465 internal virtual ValidationResult RaiseServerCertificateValidation2 (Mono.Security.X509.X509CertificateCollection collection)
467 return base.RaiseRemoteCertificateValidation2 (collection);
470 internal X509Certificate RaiseClientCertificateSelection(
471 X509CertificateCollection clientCertificates,
472 X509Certificate serverCertificate,
474 X509CertificateCollection serverRequestedCertificates)
476 return base.RaiseLocalCertificateSelection(clientCertificates, serverCertificate, targetHost, serverRequestedCertificates);
479 internal override AsymmetricAlgorithm OnLocalPrivateKeySelection(X509Certificate certificate, string targetHost)
481 if (this.PrivateKeySelection != null)
483 return this.PrivateKeySelection(certificate, targetHost);
489 internal AsymmetricAlgorithm RaisePrivateKeySelection(
490 X509Certificate certificate,
493 return base.RaiseLocalPrivateKeySelection(certificate, targetHost);