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.
38 extern alias PrebuiltSystem;
40 #if MONO_SECURITY_ALIAS
41 extern alias MonoSecurity;
44 #if MONO_SECURITY_ALIAS
45 using MonoCipherAlgorithmType = MonoSecurity::Mono.Security.Protocol.Tls.CipherAlgorithmType;
46 using MonoHashAlgorithmType = MonoSecurity::Mono.Security.Protocol.Tls.HashAlgorithmType;
47 using MonoExchangeAlgorithmType = MonoSecurity::Mono.Security.Protocol.Tls.ExchangeAlgorithmType;
48 using MonoSecurityProtocolType = MonoSecurity::Mono.Security.Protocol.Tls.SecurityProtocolType;
49 using MonoSecurity::Mono.Security.Protocol.Tls;
50 using MonoSecurity::Mono.Security.Interface;
52 using MonoCipherAlgorithmType = Mono.Security.Protocol.Tls.CipherAlgorithmType;
53 using MonoHashAlgorithmType = Mono.Security.Protocol.Tls.HashAlgorithmType;
54 using MonoExchangeAlgorithmType = Mono.Security.Protocol.Tls.ExchangeAlgorithmType;
55 using MonoSecurityProtocolType = Mono.Security.Protocol.Tls.SecurityProtocolType;
56 using Mono.Security.Protocol.Tls;
57 using Mono.Security.Interface;
60 using X509CertificateCollection = PrebuiltSystem::System.Security.Cryptography.X509Certificates.X509CertificateCollection;
63 using CipherAlgorithmType = System.Security.Authentication.CipherAlgorithmType;
64 using HashAlgorithmType = System.Security.Authentication.HashAlgorithmType;
65 using ExchangeAlgorithmType = System.Security.Authentication.ExchangeAlgorithmType;
70 using System.Net.Security;
71 using System.Security.Authentication;
72 using System.Security.Cryptography.X509Certificates;
73 using System.Security.Principal;
74 using System.Security.Cryptography;
76 using System.Threading.Tasks;
78 namespace Mono.Net.Security.Private
81 * Strictly private - do not use outside the Mono.Net.Security directory.
83 [MonoTODO ("Non-X509Certificate2 certificate is not supported")]
84 internal class LegacySslStream : AuthenticatedStream, IMonoSslStream
88 SslStreamBase ssl_stream;
89 ICertificateValidator certificateValidator;
90 MonoTlsProvider provider;
96 public LegacySslStream (Stream innerStream, bool leaveInnerStreamOpen, MonoTlsProvider provider, MonoTlsSettings settings)
97 : base (innerStream, leaveInnerStreamOpen)
99 this.provider = provider;
100 certificateValidator = ChainValidationHelper.GetDefaultValidator (provider, settings);
102 #endregion // Constructors
106 public override bool CanRead {
107 get { return InnerStream.CanRead; }
110 public override bool CanSeek {
111 get { return InnerStream.CanSeek; }
114 public override bool CanTimeout {
115 get { return InnerStream.CanTimeout; }
118 public override bool CanWrite {
119 get { return InnerStream.CanWrite; }
122 public override long Length {
123 get { return InnerStream.Length; }
126 public override long Position {
127 get { return InnerStream.Position; }
129 throw new NotSupportedException ("This stream does not support seek operations");
133 // AuthenticatedStream overrides
135 public override bool IsAuthenticated {
136 get { return ssl_stream != null; }
139 public override bool IsEncrypted {
140 get { return IsAuthenticated; }
143 public override bool IsMutuallyAuthenticated {
144 get { return IsAuthenticated && (IsServer ? RemoteCertificate != null : LocalCertificate != null); }
147 public override bool IsServer {
148 get { return ssl_stream is SslServerStream; }
151 public override bool IsSigned {
152 get { return IsAuthenticated; }
155 public override int ReadTimeout {
156 get { return InnerStream.ReadTimeout; }
157 set { InnerStream.ReadTimeout = value; }
160 public override int WriteTimeout {
161 get { return InnerStream.WriteTimeout; }
162 set { InnerStream.WriteTimeout = value; }
167 public virtual bool CheckCertRevocationStatus {
169 if (!IsAuthenticated)
172 return ssl_stream.CheckCertRevocationStatus;
176 public virtual CipherAlgorithmType CipherAlgorithm {
178 CheckConnectionAuthenticated ();
180 switch (ssl_stream.CipherAlgorithm) {
181 case MonoCipherAlgorithmType.Des:
182 return CipherAlgorithmType.Des;
183 case MonoCipherAlgorithmType.None:
184 return CipherAlgorithmType.None;
185 case MonoCipherAlgorithmType.Rc2:
186 return CipherAlgorithmType.Rc2;
187 case MonoCipherAlgorithmType.Rc4:
188 return CipherAlgorithmType.Rc4;
189 case MonoCipherAlgorithmType.SkipJack:
191 case MonoCipherAlgorithmType.TripleDes:
192 return CipherAlgorithmType.TripleDes;
193 case MonoCipherAlgorithmType.Rijndael:
194 switch (ssl_stream.CipherStrength) {
196 return CipherAlgorithmType.Aes128;
198 return CipherAlgorithmType.Aes192;
200 return CipherAlgorithmType.Aes256;
205 throw new InvalidOperationException ("Not supported cipher algorithm is in use. It is likely a bug in SslStream.");
209 public virtual int CipherStrength {
211 CheckConnectionAuthenticated ();
213 return ssl_stream.CipherStrength;
217 public virtual HashAlgorithmType HashAlgorithm {
219 CheckConnectionAuthenticated ();
221 switch (ssl_stream.HashAlgorithm) {
222 case MonoHashAlgorithmType.Md5:
223 return HashAlgorithmType.Md5;
224 case MonoHashAlgorithmType.None:
225 return HashAlgorithmType.None;
226 case MonoHashAlgorithmType.Sha1:
227 return HashAlgorithmType.Sha1;
230 throw new InvalidOperationException ("Not supported hash algorithm is in use. It is likely a bug in SslStream.");
234 public virtual int HashStrength {
236 CheckConnectionAuthenticated ();
238 return ssl_stream.HashStrength;
242 public virtual ExchangeAlgorithmType KeyExchangeAlgorithm {
244 CheckConnectionAuthenticated ();
246 switch (ssl_stream.KeyExchangeAlgorithm) {
247 case MonoExchangeAlgorithmType.DiffieHellman:
248 return ExchangeAlgorithmType.DiffieHellman;
249 case MonoExchangeAlgorithmType.Fortezza:
251 case MonoExchangeAlgorithmType.None:
252 return ExchangeAlgorithmType.None;
253 case MonoExchangeAlgorithmType.RsaKeyX:
254 return ExchangeAlgorithmType.RsaKeyX;
255 case MonoExchangeAlgorithmType.RsaSign:
256 return ExchangeAlgorithmType.RsaSign;
259 throw new InvalidOperationException ("Not supported exchange algorithm is in use. It is likely a bug in SslStream.");
263 public virtual int KeyExchangeStrength {
265 CheckConnectionAuthenticated ();
267 return ssl_stream.KeyExchangeStrength;
271 X509Certificate IMonoSslStream.InternalLocalCertificate {
273 return IsServer ? ssl_stream.ServerCertificate : ((SslClientStream) ssl_stream).SelectedClientCertificate;
277 public virtual X509Certificate LocalCertificate {
279 CheckConnectionAuthenticated ();
281 return IsServer ? ssl_stream.ServerCertificate : ((SslClientStream) ssl_stream).SelectedClientCertificate;
285 public virtual X509Certificate RemoteCertificate {
287 CheckConnectionAuthenticated ();
288 return !IsServer ? ssl_stream.ServerCertificate : ((SslServerStream) ssl_stream).ClientCertificate;
292 public virtual SslProtocols SslProtocol {
294 CheckConnectionAuthenticated ();
296 switch (ssl_stream.SecurityProtocol) {
297 case MonoSecurityProtocolType.Default:
298 return SslProtocols.Default;
299 case MonoSecurityProtocolType.Ssl2:
300 return SslProtocols.Ssl2;
301 case MonoSecurityProtocolType.Ssl3:
302 return SslProtocols.Ssl3;
303 case MonoSecurityProtocolType.Tls:
304 return SslProtocols.Tls;
307 throw new InvalidOperationException ("Not supported SSL/TLS protocol is in use. It is likely a bug in SslStream.");
311 #endregion // Properties
316 AsymmetricAlgorithm GetPrivateKey (X509Certificate cert, string targetHost)
318 // FIXME: what can I do for non-X509Certificate2 ?
319 X509Certificate2 cert2 = cert as X509Certificate2;
320 return cert2 != null ? cert2.PrivateKey : null;
323 X509Certificate OnCertificateSelection (X509CertificateCollection clientCerts, X509Certificate serverCert, string targetHost, X509CertificateCollection serverRequestedCerts)
325 string [] acceptableIssuers = new string [serverRequestedCerts != null ? serverRequestedCerts.Count : 0];
326 for (int i = 0; i < acceptableIssuers.Length; i++)
327 acceptableIssuers [i] = serverRequestedCerts [i].GetIssuerName ();
328 X509Certificate clientCertificate;
329 certificateValidator.SelectClientCertificate (targetHost, clientCerts, serverCert, acceptableIssuers, out clientCertificate);
330 return clientCertificate;
333 public virtual IAsyncResult BeginAuthenticateAsClient (string targetHost, AsyncCallback asyncCallback, object asyncState)
335 return BeginAuthenticateAsClient (targetHost, new X509CertificateCollection (), SslProtocols.Tls, false, asyncCallback, asyncState);
338 public virtual IAsyncResult BeginAuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
341 throw new InvalidOperationException ("This SslStream is already authenticated");
343 SslClientStream s = new SslClientStream (InnerStream, targetHost, !LeaveInnerStreamOpen, GetMonoSslProtocol (enabledSslProtocols), clientCertificates);
344 s.CheckCertRevocationStatus = checkCertificateRevocation;
346 // Due to the Mono.Security internal, it cannot reuse
347 // the delegated argument, as Mono.Security creates
348 // another instance of X509Certificate which lacks
349 // private key but is filled the private key via this
351 s.PrivateKeyCertSelectionDelegate = delegate (X509Certificate cert, string host) {
352 string hash = cert.GetCertHashString ();
353 // ... so, we cannot use the delegate argument.
354 foreach (X509Certificate cc in clientCertificates) {
355 if (cc.GetCertHashString () != hash)
357 X509Certificate2 cert2 = cc as X509Certificate2;
358 cert2 = cert2 ?? new X509Certificate2 (cc);
359 return cert2.PrivateKey;
364 // Even if validation_callback is null this allows us to verify requests where the user
365 // does not provide a verification callback but attempts to authenticate with the website
366 // as a client (see https://bugzilla.xamarin.com/show_bug.cgi?id=18962 for an example)
367 s.ServerCertValidation2 += (mcerts) => {
368 X509CertificateCollection certs = null;
369 if (mcerts != null) {
370 certs = new X509CertificateCollection ();
371 for (int i = 0; i < mcerts.Count; i++)
372 certs.Add (new X509Certificate2 (mcerts [i].RawData));
374 return ((ChainValidationHelper)certificateValidator).ValidateCertificate (targetHost, false, certs);
376 s.ClientCertSelectionDelegate = OnCertificateSelection;
380 return BeginWrite (new byte [0], 0, 0, asyncCallback, asyncState);
383 public override IAsyncResult BeginRead (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
385 CheckConnectionAuthenticated ();
387 return ssl_stream.BeginRead (buffer, offset, count, asyncCallback, asyncState);
390 public virtual IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, AsyncCallback asyncCallback, object asyncState)
392 return BeginAuthenticateAsServer (serverCertificate, false, SslProtocols.Tls, false, asyncCallback, asyncState);
395 public virtual IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
398 throw new InvalidOperationException ("This SslStream is already authenticated");
400 SslServerStream s = new SslServerStream (InnerStream, serverCertificate, false, clientCertificateRequired, !LeaveInnerStreamOpen, GetMonoSslProtocol (enabledSslProtocols));
401 s.CheckCertRevocationStatus = checkCertificateRevocation;
402 // Due to the Mono.Security internal, it cannot reuse
403 // the delegated argument, as Mono.Security creates
404 // another instance of X509Certificate which lacks
405 // private key but is filled the private key via this
407 s.PrivateKeyCertSelectionDelegate = delegate (X509Certificate cert, string targetHost) {
408 // ... so, we cannot use the delegate argument.
409 X509Certificate2 cert2 = serverCertificate as X509Certificate2 ?? new X509Certificate2 (serverCertificate);
410 return cert2 != null ? cert2.PrivateKey : null;
413 s.ClientCertValidationDelegate = delegate (X509Certificate cert, int[] certErrors) {
414 var errors = certErrors.Length > 0 ? MonoSslPolicyErrors.RemoteCertificateChainErrors : MonoSslPolicyErrors.None;
415 return ((ChainValidationHelper)certificateValidator).ValidateClientCertificate (cert, errors);
420 return BeginWrite (new byte[0], 0, 0, asyncCallback, asyncState);
423 MonoSecurityProtocolType GetMonoSslProtocol (SslProtocols ms)
426 case SslProtocols.Ssl2:
427 return MonoSecurityProtocolType.Ssl2;
428 case SslProtocols.Ssl3:
429 return MonoSecurityProtocolType.Ssl3;
430 case SslProtocols.Tls:
431 return MonoSecurityProtocolType.Tls;
433 return MonoSecurityProtocolType.Default;
437 public override IAsyncResult BeginWrite (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
439 CheckConnectionAuthenticated ();
441 return ssl_stream.BeginWrite (buffer, offset, count, asyncCallback, asyncState);
444 public virtual void AuthenticateAsClient (string targetHost)
446 AuthenticateAsClient (targetHost, new X509CertificateCollection (), SslProtocols.Tls, false);
449 public virtual void AuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
451 EndAuthenticateAsClient (BeginAuthenticateAsClient (
452 targetHost, clientCertificates, enabledSslProtocols, checkCertificateRevocation, null, null));
455 public virtual void AuthenticateAsServer (X509Certificate serverCertificate)
457 AuthenticateAsServer (serverCertificate, false, SslProtocols.Tls, false);
460 public virtual void AuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
462 EndAuthenticateAsServer (BeginAuthenticateAsServer (
463 serverCertificate, clientCertificateRequired, enabledSslProtocols, checkCertificateRevocation, null, null));
466 protected override void Dispose (bool disposing)
469 if (ssl_stream != null)
470 ssl_stream.Dispose ();
473 base.Dispose (disposing);
476 public virtual void EndAuthenticateAsClient (IAsyncResult asyncResult)
478 CheckConnectionAuthenticated ();
481 ssl_stream.EndRead (asyncResult);
483 ssl_stream.EndWrite (asyncResult);
486 public virtual void EndAuthenticateAsServer (IAsyncResult asyncResult)
488 CheckConnectionAuthenticated ();
491 ssl_stream.EndRead (asyncResult);
493 ssl_stream.EndWrite (asyncResult);
496 public override int EndRead (IAsyncResult asyncResult)
498 CheckConnectionAuthenticated ();
500 return ssl_stream.EndRead (asyncResult);
503 public override void EndWrite (IAsyncResult asyncResult)
505 CheckConnectionAuthenticated ();
507 ssl_stream.EndWrite (asyncResult);
510 public override void Flush ()
512 CheckConnectionAuthenticated ();
514 InnerStream.Flush ();
517 public override int Read (byte[] buffer, int offset, int count)
519 return EndRead (BeginRead (buffer, offset, count, null, null));
522 public override long Seek (long offset, SeekOrigin origin)
524 throw new NotSupportedException ("This stream does not support seek operations");
527 public override void SetLength (long value)
529 InnerStream.SetLength (value);
532 public override void Write (byte[] buffer, int offset, int count)
534 EndWrite (BeginWrite (buffer, offset, count, null, null));
537 public void Write (byte[] buffer)
539 Write (buffer, 0, buffer.Length);
542 void CheckConnectionAuthenticated ()
544 if (!IsAuthenticated)
545 throw new InvalidOperationException ("This operation is invalid until it is successfully authenticated");
548 public virtual Task AuthenticateAsClientAsync (string targetHost)
550 return Task.Factory.FromAsync (BeginAuthenticateAsClient, EndAuthenticateAsClient, targetHost, null);
553 public virtual Task AuthenticateAsClientAsync (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
555 var t = Tuple.Create (targetHost, clientCertificates, enabledSslProtocols, checkCertificateRevocation, this);
557 return Task.Factory.FromAsync ((callback, state) => {
558 var d = (Tuple<string, X509CertificateCollection, SslProtocols, bool, LegacySslStream>) state;
559 return d.Item5.BeginAuthenticateAsClient (d.Item1, d.Item2, d.Item3, d.Item4, callback, null);
560 }, EndAuthenticateAsClient, t);
563 public virtual Task AuthenticateAsServerAsync (X509Certificate serverCertificate)
565 return Task.Factory.FromAsync (BeginAuthenticateAsServer, EndAuthenticateAsServer, serverCertificate, null);
568 public virtual Task AuthenticateAsServerAsync (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
570 var t = Tuple.Create (serverCertificate, clientCertificateRequired, enabledSslProtocols, checkCertificateRevocation, this);
572 return Task.Factory.FromAsync ((callback, state) => {
573 var d = (Tuple<X509Certificate, bool, SslProtocols, bool, LegacySslStream>) state;
574 return d.Item5.BeginAuthenticateAsServer (d.Item1, d.Item2, d.Item3, d.Item4, callback, null);
575 }, EndAuthenticateAsServer, t);
578 #endregion // Methods
580 #region IMonoSslStream
582 AuthenticatedStream IMonoSslStream.AuthenticatedStream {
586 TransportContext IMonoSslStream.TransportContext {
587 get { throw new NotSupportedException (); }
590 MonoTlsProvider IMonoSslStream.Provider {
591 get { return provider; }
594 MonoTlsConnectionInfo IMonoSslStream.GetConnectionInfo ()