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;
33 using Mono.Security.Protocol.Tls.Handshake;
35 namespace Mono.Security.Protocol.Tls
37 public class SslServerStream : SslStreamBase
39 #region Internal Events
41 internal event CertificateValidationCallback ClientCertValidation;
42 internal event PrivateKeySelectionCallback PrivateKeySelection;
48 public X509Certificate ClientCertificate
52 if (this.context.HandshakeState == HandshakeState.Finished)
54 return this.context.ClientSettings.ClientCertificate;
63 #region Callback Properties
65 public CertificateValidationCallback ClientCertValidationDelegate
67 get { return this.ClientCertValidation; }
68 set { this.ClientCertValidation = value; }
71 public PrivateKeySelectionCallback PrivateKeyCertSelectionDelegate
73 get { return this.PrivateKeySelection; }
74 set { this.PrivateKeySelection = value; }
79 public event CertificateValidationCallback2 ClientCertValidation2;
82 public SslServerStream(
84 X509Certificate serverCertificate) : this(
89 SecurityProtocolType.Default)
93 public SslServerStream(
95 X509Certificate serverCertificate,
96 bool clientCertificateRequired,
97 bool ownsStream): this(
100 clientCertificateRequired,
102 SecurityProtocolType.Default)
106 public SslServerStream(
108 X509Certificate serverCertificate,
109 bool clientCertificateRequired,
110 bool requestClientCertificate,
112 : this (stream, serverCertificate, clientCertificateRequired, requestClientCertificate, ownsStream, SecurityProtocolType.Default)
116 public SslServerStream(
118 X509Certificate serverCertificate,
119 bool clientCertificateRequired,
121 SecurityProtocolType securityProtocolType)
122 : this (stream, serverCertificate, clientCertificateRequired, false, ownsStream, securityProtocolType)
126 public SslServerStream(
128 X509Certificate serverCertificate,
129 bool clientCertificateRequired,
130 bool requestClientCertificate,
132 SecurityProtocolType securityProtocolType)
133 : base(stream, ownsStream)
135 this.context = new ServerContext(
137 securityProtocolType,
139 clientCertificateRequired,
140 requestClientCertificate);
142 this.protocol = new ServerRecordProtocol(innerStream, (ServerContext)this.context);
156 #region IDisposable Methods
158 protected override void Dispose(bool disposing)
160 base.Dispose(disposing);
164 this.ClientCertValidation = null;
165 this.PrivateKeySelection = null;
171 #region Handsake Methods
176 ClientHello -------->
181 <-------- ServerHelloDone
189 Application Data <-------> Application Data
191 Fig. 1 - Message flow for a full handshake
194 internal override IAsyncResult OnBeginNegotiateHandshake(AsyncCallback callback, object state)
196 // Reset the context if needed
197 if (this.context.HandshakeState != HandshakeState.None)
199 this.context.Clear();
202 // Obtain supported cipher suites
203 this.context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(this.context.SecurityProtocol);
205 // Set handshake state
206 this.context.HandshakeState = HandshakeState.Started;
208 // Receive Client Hello message
209 return this.protocol.BeginReceiveRecord(this.innerStream, callback, state);
213 internal override void OnNegotiateHandshakeCallback(IAsyncResult asyncResult)
215 // Receive Client Hello message and ignore it
216 this.protocol.EndReceiveRecord(asyncResult);
218 // If received message is not an ClientHello send a
220 if (this.context.LastHandshakeMsg != HandshakeType.ClientHello)
222 this.protocol.SendAlert(AlertDescription.UnexpectedMessage);
225 // Send ServerHello message
226 this.protocol.SendRecord(HandshakeType.ServerHello);
228 // Send ServerCertificate message
229 this.protocol.SendRecord(HandshakeType.Certificate);
231 // If the negotiated cipher is a KeyEx cipher send ServerKeyExchange
232 if (this.context.Negotiating.Cipher.IsExportable)
234 this.protocol.SendRecord(HandshakeType.ServerKeyExchange);
237 bool certRequested = false;
239 // If the negotiated cipher is a KeyEx cipher or
240 // the client certificate is required send the CertificateRequest message
241 if (this.context.Negotiating.Cipher.IsExportable ||
242 ((ServerContext)this.context).ClientCertificateRequired ||
243 ((ServerContext)this.context).RequestClientCertificate)
245 this.protocol.SendRecord(HandshakeType.CertificateRequest);
246 certRequested = true;
249 // Send ServerHelloDone message
250 this.protocol.SendRecord(HandshakeType.ServerHelloDone);
252 // Receive client response, until the Client Finished message
253 // is received. IE can be interrupted at this stage and never
254 // complete the handshake
255 while (this.context.LastHandshakeMsg != HandshakeType.Finished)
257 byte[] record = this.protocol.ReceiveRecord(this.innerStream);
258 if ((record == null) || (record.Length == 0))
260 throw new TlsException(
261 AlertDescription.HandshakeFailiure,
262 "The client stopped the handshake.");
267 X509Certificate client_cert = this.context.ClientSettings.ClientCertificate;
268 if (client_cert == null && ((ServerContext)this.context).ClientCertificateRequired)
269 throw new TlsException (AlertDescription.BadCertificate, "No certificate received from client.");
271 if (!RaiseClientCertificateValidation (client_cert, new int[0]))
272 throw new TlsException (AlertDescription.BadCertificate, "Client certificate not accepted.");
275 // Send ChangeCipherSpec and ServerFinished messages
276 this.protocol.SendChangeCipherSpec();
277 this.protocol.SendRecord (HandshakeType.Finished);
279 // The handshake is finished
280 this.context.HandshakeState = HandshakeState.Finished;
282 // Reset Handshake messages information
283 this.context.HandshakeMessages.Reset ();
286 this.context.ClearKeyInfo();
291 #region Event Methods
293 internal override X509Certificate OnLocalCertificateSelection(X509CertificateCollection clientCertificates, X509Certificate serverCertificate, string targetHost, X509CertificateCollection serverRequestedCertificates)
295 throw new NotSupportedException();
298 internal override bool OnRemoteCertificateValidation(X509Certificate certificate, int[] errors)
300 if (this.ClientCertValidation != null)
302 return this.ClientCertValidation(certificate, errors);
305 return (errors != null && errors.Length == 0);
308 internal override bool HaveRemoteValidation2Callback {
309 get { return ClientCertValidation2 != null; }
312 internal override bool OnRemoteCertificateValidation2 (Mono.Security.X509.X509CertificateCollection collection)
314 CertificateValidationCallback2 cb = ClientCertValidation2;
316 return cb (collection);
320 internal bool RaiseClientCertificateValidation(
321 X509Certificate certificate,
322 int[] certificateErrors)
324 return base.RaiseRemoteCertificateValidation(certificate, certificateErrors);
327 internal override AsymmetricAlgorithm OnLocalPrivateKeySelection(X509Certificate certificate, string targetHost)
329 if (this.PrivateKeySelection != null)
331 return this.PrivateKeySelection(certificate, targetHost);
337 internal AsymmetricAlgorithm RaisePrivateKeySelection(
338 X509Certificate certificate,
341 return base.RaiseLocalPrivateKeySelection(certificate, targetHost);