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 #pragma warning disable 618
320 string [] acceptableIssuers = new string [serverRequestedCerts != null ? serverRequestedCerts.Count : 0];
321 for (int i = 0; i < acceptableIssuers.Length; i++)
322 acceptableIssuers [i] = serverRequestedCerts [i].GetIssuerName ();
323 X509Certificate clientCertificate;
324 certificateValidator.SelectClientCertificate (targetHost, clientCerts, serverCert, acceptableIssuers, out clientCertificate);
325 return clientCertificate;
326 #pragma warning restore 618
329 public virtual IAsyncResult BeginAuthenticateAsClient (string targetHost, AsyncCallback asyncCallback, object asyncState)
331 return BeginAuthenticateAsClient (targetHost, new X509CertificateCollection (), SslProtocols.Tls, false, asyncCallback, asyncState);
334 public virtual IAsyncResult BeginAuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
337 throw new InvalidOperationException ("This SslStream is already authenticated");
339 SslClientStream s = new SslClientStream (InnerStream, targetHost, !LeaveInnerStreamOpen, GetMonoSslProtocol (enabledSslProtocols), clientCertificates);
340 s.CheckCertRevocationStatus = checkCertificateRevocation;
342 // Due to the Mono.Security internal, it cannot reuse
343 // the delegated argument, as Mono.Security creates
344 // another instance of X509Certificate which lacks
345 // private key but is filled the private key via this
347 s.PrivateKeyCertSelectionDelegate = delegate (X509Certificate cert, string host) {
348 string hash = cert.GetCertHashString ();
349 // ... so, we cannot use the delegate argument.
350 foreach (X509Certificate cc in clientCertificates) {
351 if (cc.GetCertHashString () != hash)
353 X509Certificate2 cert2 = cc as X509Certificate2;
354 cert2 = cert2 ?? new X509Certificate2 (cc);
355 return cert2.PrivateKey;
360 // Even if validation_callback is null this allows us to verify requests where the user
361 // does not provide a verification callback but attempts to authenticate with the website
362 // as a client (see https://bugzilla.xamarin.com/show_bug.cgi?id=18962 for an example)
363 s.ServerCertValidation2 += (mcerts) => {
364 X509CertificateCollection certs = null;
365 if (mcerts != null) {
366 certs = new X509CertificateCollection ();
367 for (int i = 0; i < mcerts.Count; i++)
368 certs.Add (new X509Certificate2 (mcerts [i].RawData));
370 return ((ChainValidationHelper)certificateValidator).ValidateCertificate (targetHost, false, certs);
372 s.ClientCertSelectionDelegate = OnCertificateSelection;
376 return BeginWrite (new byte [0], 0, 0, asyncCallback, asyncState);
379 public override IAsyncResult BeginRead (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
381 CheckConnectionAuthenticated ();
383 return ssl_stream.BeginRead (buffer, offset, count, asyncCallback, asyncState);
386 public virtual IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, AsyncCallback asyncCallback, object asyncState)
388 return BeginAuthenticateAsServer (serverCertificate, false, SslProtocols.Tls, false, asyncCallback, asyncState);
391 public virtual IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
394 throw new InvalidOperationException ("This SslStream is already authenticated");
396 SslServerStream s = new SslServerStream (InnerStream, serverCertificate, false, clientCertificateRequired, !LeaveInnerStreamOpen, GetMonoSslProtocol (enabledSslProtocols));
397 s.CheckCertRevocationStatus = checkCertificateRevocation;
398 // Due to the Mono.Security internal, it cannot reuse
399 // the delegated argument, as Mono.Security creates
400 // another instance of X509Certificate which lacks
401 // private key but is filled the private key via this
403 s.PrivateKeyCertSelectionDelegate = delegate (X509Certificate cert, string targetHost) {
404 // ... so, we cannot use the delegate argument.
405 X509Certificate2 cert2 = serverCertificate as X509Certificate2 ?? new X509Certificate2 (serverCertificate);
406 return cert2 != null ? cert2.PrivateKey : null;
409 s.ClientCertValidationDelegate = delegate (X509Certificate cert, int[] certErrors) {
410 var errors = certErrors.Length > 0 ? MonoSslPolicyErrors.RemoteCertificateChainErrors : MonoSslPolicyErrors.None;
411 return ((ChainValidationHelper)certificateValidator).ValidateClientCertificate (cert, errors);
416 return BeginWrite (new byte[0], 0, 0, asyncCallback, asyncState);
419 MonoSecurityProtocolType GetMonoSslProtocol (SslProtocols ms)
422 case SslProtocols.Ssl2:
423 return MonoSecurityProtocolType.Ssl2;
424 case SslProtocols.Ssl3:
425 return MonoSecurityProtocolType.Ssl3;
426 case SslProtocols.Tls:
427 return MonoSecurityProtocolType.Tls;
429 return MonoSecurityProtocolType.Default;
433 public override IAsyncResult BeginWrite (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
435 CheckConnectionAuthenticated ();
437 return ssl_stream.BeginWrite (buffer, offset, count, asyncCallback, asyncState);
440 public virtual void AuthenticateAsClient (string targetHost)
442 AuthenticateAsClient (targetHost, new X509CertificateCollection (), SslProtocols.Tls, false);
445 public virtual void AuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
447 EndAuthenticateAsClient (BeginAuthenticateAsClient (
448 targetHost, clientCertificates, enabledSslProtocols, checkCertificateRevocation, null, null));
451 public virtual void AuthenticateAsServer (X509Certificate serverCertificate)
453 AuthenticateAsServer (serverCertificate, false, SslProtocols.Tls, false);
456 public virtual void AuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
458 EndAuthenticateAsServer (BeginAuthenticateAsServer (
459 serverCertificate, clientCertificateRequired, enabledSslProtocols, checkCertificateRevocation, null, null));
462 protected override void Dispose (bool disposing)
465 if (ssl_stream != null)
466 ssl_stream.Dispose ();
469 base.Dispose (disposing);
472 public virtual void EndAuthenticateAsClient (IAsyncResult asyncResult)
474 CheckConnectionAuthenticated ();
477 ssl_stream.EndRead (asyncResult);
479 ssl_stream.EndWrite (asyncResult);
482 public virtual void EndAuthenticateAsServer (IAsyncResult asyncResult)
484 CheckConnectionAuthenticated ();
487 ssl_stream.EndRead (asyncResult);
489 ssl_stream.EndWrite (asyncResult);
492 public override int EndRead (IAsyncResult asyncResult)
494 CheckConnectionAuthenticated ();
496 return ssl_stream.EndRead (asyncResult);
499 public override void EndWrite (IAsyncResult asyncResult)
501 CheckConnectionAuthenticated ();
503 ssl_stream.EndWrite (asyncResult);
506 public override void Flush ()
508 CheckConnectionAuthenticated ();
510 InnerStream.Flush ();
513 public override int Read (byte[] buffer, int offset, int count)
515 return EndRead (BeginRead (buffer, offset, count, null, null));
518 public override long Seek (long offset, SeekOrigin origin)
520 throw new NotSupportedException ("This stream does not support seek operations");
523 public override void SetLength (long value)
525 InnerStream.SetLength (value);
528 public override void Write (byte[] buffer, int offset, int count)
530 EndWrite (BeginWrite (buffer, offset, count, null, null));
533 public void Write (byte[] buffer)
535 Write (buffer, 0, buffer.Length);
538 void CheckConnectionAuthenticated ()
540 if (!IsAuthenticated)
541 throw new InvalidOperationException ("This operation is invalid until it is successfully authenticated");
544 public virtual Task AuthenticateAsClientAsync (string targetHost)
546 return Task.Factory.FromAsync (BeginAuthenticateAsClient, EndAuthenticateAsClient, targetHost, null);
549 public virtual Task AuthenticateAsClientAsync (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
551 var t = Tuple.Create (targetHost, clientCertificates, enabledSslProtocols, checkCertificateRevocation, this);
553 return Task.Factory.FromAsync ((callback, state) => {
554 var d = (Tuple<string, X509CertificateCollection, SslProtocols, bool, LegacySslStream>) state;
555 return d.Item5.BeginAuthenticateAsClient (d.Item1, d.Item2, d.Item3, d.Item4, callback, null);
556 }, EndAuthenticateAsClient, t);
559 public virtual Task AuthenticateAsServerAsync (X509Certificate serverCertificate)
561 return Task.Factory.FromAsync (BeginAuthenticateAsServer, EndAuthenticateAsServer, serverCertificate, null);
564 public virtual Task AuthenticateAsServerAsync (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
566 var t = Tuple.Create (serverCertificate, clientCertificateRequired, enabledSslProtocols, checkCertificateRevocation, this);
568 return Task.Factory.FromAsync ((callback, state) => {
569 var d = (Tuple<X509Certificate, bool, SslProtocols, bool, LegacySslStream>) state;
570 return d.Item5.BeginAuthenticateAsServer (d.Item1, d.Item2, d.Item3, d.Item4, callback, null);
571 }, EndAuthenticateAsServer, t);
574 #endregion // Methods
576 #region IMonoSslStream
578 AuthenticatedStream IMonoSslStream.AuthenticatedStream {
582 TransportContext IMonoSslStream.TransportContext {
583 get { throw new NotSupportedException (); }
586 MonoTlsProvider IMonoSslStream.Provider {
587 get { return provider; }
590 MonoTlsConnectionInfo IMonoSslStream.GetConnectionInfo ()