2 // System.Net.Security.SslStream.cs
5 // Tim Coleman (tim@timcoleman.com)
6 // Atsushi Enomoto (atsushi@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
9 // Copyright (C) Tim Coleman, 2004
10 // (c) 2004,2007 Novell, Inc. (http://www.novell.com)
11 // Copyright 2011 Xamarin Inc.
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 #if MONO_SECURITY_ALIAS
38 extern alias MonoSecurity;
41 #if MONO_SECURITY_ALIAS
42 using MonoCipherAlgorithmType = MonoSecurity::Mono.Security.Protocol.Tls.CipherAlgorithmType;
43 using MonoHashAlgorithmType = MonoSecurity::Mono.Security.Protocol.Tls.HashAlgorithmType;
44 using MonoExchangeAlgorithmType = MonoSecurity::Mono.Security.Protocol.Tls.ExchangeAlgorithmType;
45 using MonoSecurityProtocolType = MonoSecurity::Mono.Security.Protocol.Tls.SecurityProtocolType;
46 using MonoSecurity::Mono.Security.Protocol.Tls;
47 using MonoSecurity::Mono.Security.Interface;
49 using MonoCipherAlgorithmType = Mono.Security.Protocol.Tls.CipherAlgorithmType;
50 using MonoHashAlgorithmType = Mono.Security.Protocol.Tls.HashAlgorithmType;
51 using MonoExchangeAlgorithmType = Mono.Security.Protocol.Tls.ExchangeAlgorithmType;
52 using MonoSecurityProtocolType = Mono.Security.Protocol.Tls.SecurityProtocolType;
53 using Mono.Security.Protocol.Tls;
54 using Mono.Security.Interface;
57 using CipherAlgorithmType = System.Security.Authentication.CipherAlgorithmType;
58 using HashAlgorithmType = System.Security.Authentication.HashAlgorithmType;
59 using ExchangeAlgorithmType = System.Security.Authentication.ExchangeAlgorithmType;
64 using System.Net.Security;
65 using System.Security.Authentication;
66 using System.Security.Cryptography.X509Certificates;
67 using System.Security.Principal;
68 using System.Security.Cryptography;
70 using System.Threading.Tasks;
72 namespace Mono.Net.Security.Private
75 * Strictly private - do not use outside the Mono.Net.Security directory.
77 [MonoTODO ("Non-X509Certificate2 certificate is not supported")]
78 internal class LegacySslStream : AuthenticatedStream, IMonoSslStream
82 SslStreamBase ssl_stream;
83 ICertificateValidator certificateValidator;
84 MonoTlsProvider provider;
90 public LegacySslStream (Stream innerStream, bool leaveInnerStreamOpen, MonoTlsProvider provider, MonoTlsSettings settings)
91 : base (innerStream, leaveInnerStreamOpen)
93 this.provider = provider;
94 certificateValidator = ChainValidationHelper.GetInternalValidator (provider, settings);
96 #endregion // Constructors
100 public override bool CanRead {
101 get { return InnerStream.CanRead; }
104 public override bool CanSeek {
105 get { return InnerStream.CanSeek; }
108 public override bool CanTimeout {
109 get { return InnerStream.CanTimeout; }
112 public override bool CanWrite {
113 get { return InnerStream.CanWrite; }
116 public override long Length {
117 get { return InnerStream.Length; }
120 public override long Position {
121 get { return InnerStream.Position; }
123 throw new NotSupportedException ("This stream does not support seek operations");
127 // AuthenticatedStream overrides
129 public override bool IsAuthenticated {
130 get { return ssl_stream != null; }
133 public override bool IsEncrypted {
134 get { return IsAuthenticated; }
137 public override bool IsMutuallyAuthenticated {
138 get { return IsAuthenticated && (IsServer ? RemoteCertificate != null : LocalCertificate != null); }
141 public override bool IsServer {
142 get { return ssl_stream is SslServerStream; }
145 public override bool IsSigned {
146 get { return IsAuthenticated; }
149 public override int ReadTimeout {
150 get { return InnerStream.ReadTimeout; }
151 set { InnerStream.ReadTimeout = value; }
154 public override int WriteTimeout {
155 get { return InnerStream.WriteTimeout; }
156 set { InnerStream.WriteTimeout = value; }
161 public virtual bool CheckCertRevocationStatus {
163 if (!IsAuthenticated)
166 return ssl_stream.CheckCertRevocationStatus;
170 public virtual CipherAlgorithmType CipherAlgorithm {
172 CheckConnectionAuthenticated ();
174 switch (ssl_stream.CipherAlgorithm) {
175 case MonoCipherAlgorithmType.Des:
176 return CipherAlgorithmType.Des;
177 case MonoCipherAlgorithmType.None:
178 return CipherAlgorithmType.None;
179 case MonoCipherAlgorithmType.Rc2:
180 return CipherAlgorithmType.Rc2;
181 case MonoCipherAlgorithmType.Rc4:
182 return CipherAlgorithmType.Rc4;
183 case MonoCipherAlgorithmType.SkipJack:
185 case MonoCipherAlgorithmType.TripleDes:
186 return CipherAlgorithmType.TripleDes;
187 case MonoCipherAlgorithmType.Rijndael:
188 switch (ssl_stream.CipherStrength) {
190 return CipherAlgorithmType.Aes128;
192 return CipherAlgorithmType.Aes192;
194 return CipherAlgorithmType.Aes256;
199 throw new InvalidOperationException ("Not supported cipher algorithm is in use. It is likely a bug in SslStream.");
203 public virtual int CipherStrength {
205 CheckConnectionAuthenticated ();
207 return ssl_stream.CipherStrength;
211 public virtual HashAlgorithmType HashAlgorithm {
213 CheckConnectionAuthenticated ();
215 switch (ssl_stream.HashAlgorithm) {
216 case MonoHashAlgorithmType.Md5:
217 return HashAlgorithmType.Md5;
218 case MonoHashAlgorithmType.None:
219 return HashAlgorithmType.None;
220 case MonoHashAlgorithmType.Sha1:
221 return HashAlgorithmType.Sha1;
224 throw new InvalidOperationException ("Not supported hash algorithm is in use. It is likely a bug in SslStream.");
228 public virtual int HashStrength {
230 CheckConnectionAuthenticated ();
232 return ssl_stream.HashStrength;
236 public virtual ExchangeAlgorithmType KeyExchangeAlgorithm {
238 CheckConnectionAuthenticated ();
240 switch (ssl_stream.KeyExchangeAlgorithm) {
241 case MonoExchangeAlgorithmType.DiffieHellman:
242 return ExchangeAlgorithmType.DiffieHellman;
243 case MonoExchangeAlgorithmType.Fortezza:
245 case MonoExchangeAlgorithmType.None:
246 return ExchangeAlgorithmType.None;
247 case MonoExchangeAlgorithmType.RsaKeyX:
248 return ExchangeAlgorithmType.RsaKeyX;
249 case MonoExchangeAlgorithmType.RsaSign:
250 return ExchangeAlgorithmType.RsaSign;
253 throw new InvalidOperationException ("Not supported exchange algorithm is in use. It is likely a bug in SslStream.");
257 public virtual int KeyExchangeStrength {
259 CheckConnectionAuthenticated ();
261 return ssl_stream.KeyExchangeStrength;
265 X509Certificate IMonoSslStream.InternalLocalCertificate {
267 return IsServer ? ssl_stream.ServerCertificate : ((SslClientStream) ssl_stream).SelectedClientCertificate;
271 public virtual X509Certificate LocalCertificate {
273 CheckConnectionAuthenticated ();
275 return IsServer ? ssl_stream.ServerCertificate : ((SslClientStream) ssl_stream).SelectedClientCertificate;
279 public virtual X509Certificate RemoteCertificate {
281 CheckConnectionAuthenticated ();
282 return !IsServer ? ssl_stream.ServerCertificate : ((SslServerStream) ssl_stream).ClientCertificate;
286 public virtual SslProtocols SslProtocol {
288 CheckConnectionAuthenticated ();
290 switch (ssl_stream.SecurityProtocol) {
291 case MonoSecurityProtocolType.Default:
292 return SslProtocols.Default;
293 case MonoSecurityProtocolType.Ssl2:
294 return SslProtocols.Ssl2;
295 case MonoSecurityProtocolType.Ssl3:
296 return SslProtocols.Ssl3;
297 case MonoSecurityProtocolType.Tls:
298 return SslProtocols.Tls;
301 throw new InvalidOperationException ("Not supported SSL/TLS protocol is in use. It is likely a bug in SslStream.");
305 #endregion // Properties
310 AsymmetricAlgorithm GetPrivateKey (X509Certificate cert, string targetHost)
312 // FIXME: what can I do for non-X509Certificate2 ?
313 X509Certificate2 cert2 = cert as X509Certificate2;
314 return cert2 != null ? cert2.PrivateKey : null;
317 X509Certificate OnCertificateSelection (X509CertificateCollection clientCerts, X509Certificate serverCert, string targetHost, X509CertificateCollection serverRequestedCerts)
319 string [] acceptableIssuers = new string [serverRequestedCerts != null ? serverRequestedCerts.Count : 0];
320 for (int i = 0; i < acceptableIssuers.Length; i++)
321 acceptableIssuers [i] = serverRequestedCerts [i].GetIssuerName ();
322 X509Certificate clientCertificate;
323 certificateValidator.SelectClientCertificate (targetHost, clientCerts, serverCert, acceptableIssuers, out clientCertificate);
324 return clientCertificate;
327 public virtual IAsyncResult BeginAuthenticateAsClient (string targetHost, AsyncCallback asyncCallback, object asyncState)
329 return BeginAuthenticateAsClient (targetHost, new X509CertificateCollection (), SslProtocols.Tls, false, asyncCallback, asyncState);
332 public virtual IAsyncResult BeginAuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
335 throw new InvalidOperationException ("This SslStream is already authenticated");
337 SslClientStream s = new SslClientStream (InnerStream, targetHost, !LeaveInnerStreamOpen, GetMonoSslProtocol (enabledSslProtocols), clientCertificates);
338 s.CheckCertRevocationStatus = checkCertificateRevocation;
340 // Due to the Mono.Security internal, it cannot reuse
341 // the delegated argument, as Mono.Security creates
342 // another instance of X509Certificate which lacks
343 // private key but is filled the private key via this
345 s.PrivateKeyCertSelectionDelegate = delegate (X509Certificate cert, string host) {
346 string hash = cert.GetCertHashString ();
347 // ... so, we cannot use the delegate argument.
348 foreach (X509Certificate cc in clientCertificates) {
349 if (cc.GetCertHashString () != hash)
351 X509Certificate2 cert2 = cc as X509Certificate2;
352 cert2 = cert2 ?? new X509Certificate2 (cc);
353 return cert2.PrivateKey;
358 // Even if validation_callback is null this allows us to verify requests where the user
359 // does not provide a verification callback but attempts to authenticate with the website
360 // as a client (see https://bugzilla.xamarin.com/show_bug.cgi?id=18962 for an example)
361 s.ServerCertValidation2 += (mcerts) => {
362 X509CertificateCollection certs = null;
363 if (mcerts != null) {
364 certs = new X509CertificateCollection ();
365 for (int i = 0; i < mcerts.Count; i++)
366 certs.Add (new X509Certificate2 (mcerts [i].RawData));
368 return ((ChainValidationHelper)certificateValidator).ValidateCertificate (targetHost, false, certs);
370 s.ClientCertSelectionDelegate = OnCertificateSelection;
374 return BeginWrite (new byte [0], 0, 0, asyncCallback, asyncState);
377 public override IAsyncResult BeginRead (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
379 CheckConnectionAuthenticated ();
381 return ssl_stream.BeginRead (buffer, offset, count, asyncCallback, asyncState);
384 public virtual IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, AsyncCallback asyncCallback, object asyncState)
386 return BeginAuthenticateAsServer (serverCertificate, false, SslProtocols.Tls, false, asyncCallback, asyncState);
389 public virtual IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
392 throw new InvalidOperationException ("This SslStream is already authenticated");
394 SslServerStream s = new SslServerStream (InnerStream, serverCertificate, false, clientCertificateRequired, !LeaveInnerStreamOpen, GetMonoSslProtocol (enabledSslProtocols));
395 s.CheckCertRevocationStatus = checkCertificateRevocation;
396 // Due to the Mono.Security internal, it cannot reuse
397 // the delegated argument, as Mono.Security creates
398 // another instance of X509Certificate which lacks
399 // private key but is filled the private key via this
401 s.PrivateKeyCertSelectionDelegate = delegate (X509Certificate cert, string targetHost) {
402 // ... so, we cannot use the delegate argument.
403 X509Certificate2 cert2 = serverCertificate as X509Certificate2 ?? new X509Certificate2 (serverCertificate);
404 return cert2 != null ? cert2.PrivateKey : null;
407 s.ClientCertValidationDelegate = delegate (X509Certificate cert, int[] certErrors) {
408 var errors = certErrors.Length > 0 ? MonoSslPolicyErrors.RemoteCertificateChainErrors : MonoSslPolicyErrors.None;
409 return ((ChainValidationHelper)certificateValidator).ValidateClientCertificate (cert, errors);
414 return BeginWrite (new byte[0], 0, 0, asyncCallback, asyncState);
417 MonoSecurityProtocolType GetMonoSslProtocol (SslProtocols ms)
420 case SslProtocols.Ssl2:
421 return MonoSecurityProtocolType.Ssl2;
422 case SslProtocols.Ssl3:
423 return MonoSecurityProtocolType.Ssl3;
424 case SslProtocols.Tls:
425 return MonoSecurityProtocolType.Tls;
427 return MonoSecurityProtocolType.Default;
431 public override IAsyncResult BeginWrite (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
433 CheckConnectionAuthenticated ();
435 return ssl_stream.BeginWrite (buffer, offset, count, asyncCallback, asyncState);
438 public virtual void AuthenticateAsClient (string targetHost)
440 AuthenticateAsClient (targetHost, new X509CertificateCollection (), SslProtocols.Tls, false);
443 public virtual void AuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
445 EndAuthenticateAsClient (BeginAuthenticateAsClient (
446 targetHost, clientCertificates, enabledSslProtocols, checkCertificateRevocation, null, null));
449 public virtual void AuthenticateAsServer (X509Certificate serverCertificate)
451 AuthenticateAsServer (serverCertificate, false, SslProtocols.Tls, false);
454 public virtual void AuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
456 EndAuthenticateAsServer (BeginAuthenticateAsServer (
457 serverCertificate, clientCertificateRequired, enabledSslProtocols, checkCertificateRevocation, null, null));
460 protected override void Dispose (bool disposing)
463 if (ssl_stream != null)
464 ssl_stream.Dispose ();
467 base.Dispose (disposing);
470 public virtual void EndAuthenticateAsClient (IAsyncResult asyncResult)
472 CheckConnectionAuthenticated ();
475 ssl_stream.EndRead (asyncResult);
477 ssl_stream.EndWrite (asyncResult);
480 public virtual void EndAuthenticateAsServer (IAsyncResult asyncResult)
482 CheckConnectionAuthenticated ();
485 ssl_stream.EndRead (asyncResult);
487 ssl_stream.EndWrite (asyncResult);
490 public override int EndRead (IAsyncResult asyncResult)
492 CheckConnectionAuthenticated ();
494 return ssl_stream.EndRead (asyncResult);
497 public override void EndWrite (IAsyncResult asyncResult)
499 CheckConnectionAuthenticated ();
501 ssl_stream.EndWrite (asyncResult);
504 public override void Flush ()
506 CheckConnectionAuthenticated ();
508 InnerStream.Flush ();
511 public override int Read (byte[] buffer, int offset, int count)
513 return EndRead (BeginRead (buffer, offset, count, null, null));
516 public override long Seek (long offset, SeekOrigin origin)
518 throw new NotSupportedException ("This stream does not support seek operations");
521 public override void SetLength (long value)
523 InnerStream.SetLength (value);
526 public override void Write (byte[] buffer, int offset, int count)
528 EndWrite (BeginWrite (buffer, offset, count, null, null));
531 public void Write (byte[] buffer)
533 Write (buffer, 0, buffer.Length);
536 void CheckConnectionAuthenticated ()
538 if (!IsAuthenticated)
539 throw new InvalidOperationException ("This operation is invalid until it is successfully authenticated");
542 public virtual Task AuthenticateAsClientAsync (string targetHost)
544 return Task.Factory.FromAsync (BeginAuthenticateAsClient, EndAuthenticateAsClient, targetHost, null);
547 public virtual Task AuthenticateAsClientAsync (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
549 var t = Tuple.Create (targetHost, clientCertificates, enabledSslProtocols, checkCertificateRevocation, this);
551 return Task.Factory.FromAsync ((callback, state) => {
552 var d = (Tuple<string, X509CertificateCollection, SslProtocols, bool, LegacySslStream>) state;
553 return d.Item5.BeginAuthenticateAsClient (d.Item1, d.Item2, d.Item3, d.Item4, callback, null);
554 }, EndAuthenticateAsClient, t);
557 public virtual Task AuthenticateAsServerAsync (X509Certificate serverCertificate)
559 return Task.Factory.FromAsync (BeginAuthenticateAsServer, EndAuthenticateAsServer, serverCertificate, null);
562 public virtual Task AuthenticateAsServerAsync (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
564 var t = Tuple.Create (serverCertificate, clientCertificateRequired, enabledSslProtocols, checkCertificateRevocation, this);
566 return Task.Factory.FromAsync ((callback, state) => {
567 var d = (Tuple<X509Certificate, bool, SslProtocols, bool, LegacySslStream>) state;
568 return d.Item5.BeginAuthenticateAsServer (d.Item1, d.Item2, d.Item3, d.Item4, callback, null);
569 }, EndAuthenticateAsServer, t);
572 #endregion // Methods
574 #region IMonoSslStream
576 AuthenticatedStream IMonoSslStream.AuthenticatedStream {
580 TransportContext IMonoSslStream.TransportContext {
581 get { throw new NotSupportedException (); }
584 MonoTlsProvider IMonoSslStream.Provider {
585 get { return provider; }
588 MonoTlsConnectionInfo IMonoSslStream.GetConnectionInfo ()