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.
35 #if SECURITY_DEP && !MONO_FEATURE_NEW_TLS
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;
51 using MonoCipherAlgorithmType = Mono.Security.Protocol.Tls.CipherAlgorithmType;
52 using MonoHashAlgorithmType = Mono.Security.Protocol.Tls.HashAlgorithmType;
53 using MonoExchangeAlgorithmType = Mono.Security.Protocol.Tls.ExchangeAlgorithmType;
54 using MonoSecurityProtocolType = Mono.Security.Protocol.Tls.SecurityProtocolType;
55 using Mono.Security.Protocol.Tls;
58 using X509CertificateCollection = PrebuiltSystem::System.Security.Cryptography.X509Certificates.X509CertificateCollection;
61 using CipherAlgorithmType = System.Security.Authentication.CipherAlgorithmType;
62 using HashAlgorithmType = System.Security.Authentication.HashAlgorithmType;
63 using ExchangeAlgorithmType = System.Security.Authentication.ExchangeAlgorithmType;
68 using System.Security.Authentication;
69 using System.Security.Cryptography.X509Certificates;
70 using System.Security.Principal;
71 using System.Security.Cryptography;
73 using System.Threading.Tasks;
75 namespace System.Net.Security
78 * These two are defined by the referencesource; add them heere to make
79 * it easy to switch between the two implementations.
82 internal delegate bool RemoteCertValidationCallback (
84 X509Certificate certificate,
86 SslPolicyErrors sslPolicyErrors);
88 internal delegate X509Certificate LocalCertSelectionCallback (
90 X509CertificateCollection localCertificates,
91 X509Certificate remoteCertificate,
92 string[] acceptableIssuers);
94 [MonoTODO ("Non-X509Certificate2 certificate is not supported")]
95 public class SslStream : AuthenticatedStream
99 SslStreamBase ssl_stream;
100 RemoteCertificateValidationCallback validation_callback;
101 LocalCertificateSelectionCallback selection_callback;
107 public SslStream (Stream innerStream)
108 : this (innerStream, false)
112 public SslStream (Stream innerStream, bool leaveInnerStreamOpen)
113 : base (innerStream, leaveInnerStreamOpen)
117 [MonoTODO ("userCertificateValidationCallback is not passed X509Chain and SslPolicyErrors correctly")]
118 public SslStream (Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback)
119 : this (innerStream, leaveInnerStreamOpen, userCertificateValidationCallback, null)
123 [MonoTODO ("userCertificateValidationCallback is not passed X509Chain and SslPolicyErrors correctly")]
124 public SslStream (Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback, LocalCertificateSelectionCallback userCertificateSelectionCallback)
125 : base (innerStream, leaveInnerStreamOpen)
127 // they are nullable.
128 validation_callback = userCertificateValidationCallback;
129 selection_callback = userCertificateSelectionCallback;
131 #endregion // Constructors
135 public override bool CanRead {
136 get { return InnerStream.CanRead; }
139 public override bool CanSeek {
140 get { return InnerStream.CanSeek; }
143 public override bool CanTimeout {
144 get { return InnerStream.CanTimeout; }
147 public override bool CanWrite {
148 get { return InnerStream.CanWrite; }
151 public override long Length {
152 get { return InnerStream.Length; }
155 public override long Position {
156 get { return InnerStream.Position; }
158 throw new NotSupportedException ("This stream does not support seek operations");
162 // AuthenticatedStream overrides
164 public override bool IsAuthenticated {
165 get { return ssl_stream != null; }
168 public override bool IsEncrypted {
169 get { return IsAuthenticated; }
172 public override bool IsMutuallyAuthenticated {
173 get { return IsAuthenticated && (IsServer ? RemoteCertificate != null : LocalCertificate != null); }
176 public override bool IsServer {
177 get { return ssl_stream is SslServerStream; }
180 public override bool IsSigned {
181 get { return IsAuthenticated; }
184 public override int ReadTimeout {
185 get { return InnerStream.ReadTimeout; }
186 set { InnerStream.ReadTimeout = value; }
189 public override int WriteTimeout {
190 get { return InnerStream.WriteTimeout; }
191 set { InnerStream.WriteTimeout = value; }
196 public virtual bool CheckCertRevocationStatus {
198 if (!IsAuthenticated)
201 return ssl_stream.CheckCertRevocationStatus;
205 public virtual CipherAlgorithmType CipherAlgorithm {
207 CheckConnectionAuthenticated ();
209 switch (ssl_stream.CipherAlgorithm) {
210 case MonoCipherAlgorithmType.Des:
211 return CipherAlgorithmType.Des;
212 case MonoCipherAlgorithmType.None:
213 return CipherAlgorithmType.None;
214 case MonoCipherAlgorithmType.Rc2:
215 return CipherAlgorithmType.Rc2;
216 case MonoCipherAlgorithmType.Rc4:
217 return CipherAlgorithmType.Rc4;
218 case MonoCipherAlgorithmType.SkipJack:
220 case MonoCipherAlgorithmType.TripleDes:
221 return CipherAlgorithmType.TripleDes;
222 case MonoCipherAlgorithmType.Rijndael:
223 switch (ssl_stream.CipherStrength) {
225 return CipherAlgorithmType.Aes128;
227 return CipherAlgorithmType.Aes192;
229 return CipherAlgorithmType.Aes256;
234 throw new InvalidOperationException ("Not supported cipher algorithm is in use. It is likely a bug in SslStream.");
238 public virtual int CipherStrength {
240 CheckConnectionAuthenticated ();
242 return ssl_stream.CipherStrength;
246 public virtual HashAlgorithmType HashAlgorithm {
248 CheckConnectionAuthenticated ();
250 switch (ssl_stream.HashAlgorithm) {
251 case MonoHashAlgorithmType.Md5:
252 return HashAlgorithmType.Md5;
253 case MonoHashAlgorithmType.None:
254 return HashAlgorithmType.None;
255 case MonoHashAlgorithmType.Sha1:
256 return HashAlgorithmType.Sha1;
259 throw new InvalidOperationException ("Not supported hash algorithm is in use. It is likely a bug in SslStream.");
263 public virtual int HashStrength {
265 CheckConnectionAuthenticated ();
267 return ssl_stream.HashStrength;
271 public virtual ExchangeAlgorithmType KeyExchangeAlgorithm {
273 CheckConnectionAuthenticated ();
275 switch (ssl_stream.KeyExchangeAlgorithm) {
276 case MonoExchangeAlgorithmType.DiffieHellman:
277 return ExchangeAlgorithmType.DiffieHellman;
278 case MonoExchangeAlgorithmType.Fortezza:
280 case MonoExchangeAlgorithmType.None:
281 return ExchangeAlgorithmType.None;
282 case MonoExchangeAlgorithmType.RsaKeyX:
283 return ExchangeAlgorithmType.RsaKeyX;
284 case MonoExchangeAlgorithmType.RsaSign:
285 return ExchangeAlgorithmType.RsaSign;
288 throw new InvalidOperationException ("Not supported exchange algorithm is in use. It is likely a bug in SslStream.");
292 public virtual int KeyExchangeStrength {
294 CheckConnectionAuthenticated ();
296 return ssl_stream.KeyExchangeStrength;
300 public virtual X509Certificate LocalCertificate {
302 CheckConnectionAuthenticated ();
304 return IsServer ? ssl_stream.ServerCertificate : ((SslClientStream) ssl_stream).SelectedClientCertificate;
308 public virtual X509Certificate RemoteCertificate {
310 CheckConnectionAuthenticated ();
311 return !IsServer ? ssl_stream.ServerCertificate : ((SslServerStream) ssl_stream).ClientCertificate;
315 public virtual SslProtocols SslProtocol {
317 CheckConnectionAuthenticated ();
319 switch (ssl_stream.SecurityProtocol) {
320 case MonoSecurityProtocolType.Default:
321 return SslProtocols.Default;
322 case MonoSecurityProtocolType.Ssl2:
323 return SslProtocols.Ssl2;
324 case MonoSecurityProtocolType.Ssl3:
325 return SslProtocols.Ssl3;
326 case MonoSecurityProtocolType.Tls:
327 return SslProtocols.Tls;
330 throw new InvalidOperationException ("Not supported SSL/TLS protocol is in use. It is likely a bug in SslStream.");
334 #endregion // Properties
338 AsymmetricAlgorithm GetPrivateKey (X509Certificate cert, string targetHost)
340 // FIXME: what can I do for non-X509Certificate2 ?
341 X509Certificate2 cert2 = cert as X509Certificate2;
342 return cert2 != null ? cert2.PrivateKey : null;
345 X509Certificate OnCertificateSelection (X509CertificateCollection clientCerts, X509Certificate serverCert, string targetHost, X509CertificateCollection serverRequestedCerts)
347 string [] acceptableIssuers = new string [serverRequestedCerts != null ? serverRequestedCerts.Count : 0];
348 for (int i = 0; i < acceptableIssuers.Length; i++)
349 acceptableIssuers [i] = serverRequestedCerts [i].GetIssuerName ();
350 return selection_callback (this, targetHost, clientCerts, serverCert, acceptableIssuers);
353 public virtual IAsyncResult BeginAuthenticateAsClient (string targetHost, AsyncCallback asyncCallback, object asyncState)
355 return BeginAuthenticateAsClient (targetHost, new X509CertificateCollection (), SslProtocols.Tls, false, asyncCallback, asyncState);
358 public virtual IAsyncResult BeginAuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
361 throw new InvalidOperationException ("This SslStream is already authenticated");
363 SslClientStream s = new SslClientStream (InnerStream, targetHost, !LeaveInnerStreamOpen, GetMonoSslProtocol (enabledSslProtocols), clientCertificates);
364 s.CheckCertRevocationStatus = checkCertificateRevocation;
366 // Due to the Mono.Security internal, it cannot reuse
367 // the delegated argument, as Mono.Security creates
368 // another instance of X509Certificate which lacks
369 // private key but is filled the private key via this
371 s.PrivateKeyCertSelectionDelegate = delegate (X509Certificate cert, string host) {
372 string hash = cert.GetCertHashString ();
373 // ... so, we cannot use the delegate argument.
374 foreach (X509Certificate cc in clientCertificates) {
375 if (cc.GetCertHashString () != hash)
377 X509Certificate2 cert2 = cc as X509Certificate2;
378 cert2 = cert2 ?? new X509Certificate2 (cc);
379 return cert2.PrivateKey;
384 #if MONOTOUCH || MONODROID
385 // Even if validation_callback is null this allows us to verify requests where the user
386 // does not provide a verification callback but attempts to authenticate with the website
387 // as a client (see https://bugzilla.xamarin.com/show_bug.cgi?id=18962 for an example)
388 var helper = new ServicePointManager.ChainValidationHelper (this, targetHost);
389 helper.ServerCertificateValidationCallback = validation_callback;
390 s.ServerCertValidation2 += new CertificateValidationCallback2 (helper.ValidateChain);
392 if (validation_callback != null) {
393 s.ServerCertValidationDelegate = delegate (X509Certificate cert, int [] certErrors) {
394 X509Chain chain = new X509Chain ();
395 X509Certificate2 x2 = (cert as X509Certificate2);
397 x2 = new X509Certificate2 (cert);
399 if (!ServicePointManager.CheckCertificateRevocationList)
400 chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
402 // SSL specific checks (done by Mono.Security.dll SSL/TLS implementation)
403 SslPolicyErrors errors = SslPolicyErrors.None;
404 foreach (int i in certErrors) {
406 case -2146762490: // CERT_E_PURPOSE
407 errors |= SslPolicyErrors.RemoteCertificateNotAvailable;
409 case -2146762481: // CERT_E_CN_NO_MATCH
410 errors |= SslPolicyErrors.RemoteCertificateNameMismatch;
413 errors |= SslPolicyErrors.RemoteCertificateChainErrors;
420 // non-SSL specific X509 checks (i.e. RFC3280 related checks)
421 foreach (X509ChainStatus status in chain.ChainStatus) {
422 if (status.Status == X509ChainStatusFlags.NoError)
424 if ((status.Status & X509ChainStatusFlags.PartialChain) != 0)
425 errors |= SslPolicyErrors.RemoteCertificateNotAvailable;
427 errors |= SslPolicyErrors.RemoteCertificateChainErrors;
430 return validation_callback (this, cert, chain, errors);
434 if (selection_callback != null)
435 s.ClientCertSelectionDelegate = OnCertificateSelection;
439 return BeginWrite (new byte [0], 0, 0, asyncCallback, asyncState);
442 public override IAsyncResult BeginRead (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
444 CheckConnectionAuthenticated ();
446 return ssl_stream.BeginRead (buffer, offset, count, asyncCallback, asyncState);
449 public virtual IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, AsyncCallback asyncCallback, object asyncState)
451 return BeginAuthenticateAsServer (serverCertificate, false, SslProtocols.Tls, false, asyncCallback, asyncState);
454 public virtual IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
457 throw new InvalidOperationException ("This SslStream is already authenticated");
459 SslServerStream s = new SslServerStream (InnerStream, serverCertificate, clientCertificateRequired, !LeaveInnerStreamOpen, GetMonoSslProtocol (enabledSslProtocols));
460 s.CheckCertRevocationStatus = checkCertificateRevocation;
461 // Due to the Mono.Security internal, it cannot reuse
462 // the delegated argument, as Mono.Security creates
463 // another instance of X509Certificate which lacks
464 // private key but is filled the private key via this
466 s.PrivateKeyCertSelectionDelegate = delegate (X509Certificate cert, string targetHost) {
467 // ... so, we cannot use the delegate argument.
468 X509Certificate2 cert2 = serverCertificate as X509Certificate2 ?? new X509Certificate2 (serverCertificate);
469 return cert2 != null ? cert2.PrivateKey : null;
472 if (validation_callback != null)
473 s.ClientCertValidationDelegate = delegate (X509Certificate cert, int [] certErrors) {
474 X509Chain chain = null;
475 if (cert is X509Certificate2) {
476 chain = new X509Chain ();
477 chain.Build ((X509Certificate2) cert);
479 // FIXME: SslPolicyErrors is incomplete
480 SslPolicyErrors errors = certErrors.Length > 0 ? SslPolicyErrors.RemoteCertificateChainErrors : SslPolicyErrors.None;
481 return validation_callback (this, cert, chain, errors);
486 return BeginWrite (new byte[0], 0, 0, asyncCallback, asyncState);
489 MonoSecurityProtocolType GetMonoSslProtocol (SslProtocols ms)
492 case SslProtocols.Ssl2:
493 return MonoSecurityProtocolType.Ssl2;
494 case SslProtocols.Ssl3:
495 return MonoSecurityProtocolType.Ssl3;
496 case SslProtocols.Tls:
497 return MonoSecurityProtocolType.Tls;
499 return MonoSecurityProtocolType.Default;
503 public override IAsyncResult BeginWrite (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
505 CheckConnectionAuthenticated ();
507 return ssl_stream.BeginWrite (buffer, offset, count, asyncCallback, asyncState);
510 public virtual void AuthenticateAsClient (string targetHost)
512 AuthenticateAsClient (targetHost, new X509CertificateCollection (), SslProtocols.Tls, false);
515 public virtual void AuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
517 EndAuthenticateAsClient (BeginAuthenticateAsClient (
518 targetHost, clientCertificates, enabledSslProtocols, checkCertificateRevocation, null, null));
521 public virtual void AuthenticateAsServer (X509Certificate serverCertificate)
523 AuthenticateAsServer (serverCertificate, false, SslProtocols.Tls, false);
526 public virtual void AuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
528 EndAuthenticateAsServer (BeginAuthenticateAsServer (
529 serverCertificate, clientCertificateRequired, enabledSslProtocols, checkCertificateRevocation, null, null));
532 protected override void Dispose (bool disposing)
535 if (ssl_stream != null)
536 ssl_stream.Dispose ();
539 base.Dispose (disposing);
542 public virtual void EndAuthenticateAsClient (IAsyncResult asyncResult)
544 CheckConnectionAuthenticated ();
547 ssl_stream.EndRead (asyncResult);
549 ssl_stream.EndWrite (asyncResult);
552 public virtual void EndAuthenticateAsServer (IAsyncResult asyncResult)
554 CheckConnectionAuthenticated ();
557 ssl_stream.EndRead (asyncResult);
559 ssl_stream.EndWrite (asyncResult);
562 public override int EndRead (IAsyncResult asyncResult)
564 CheckConnectionAuthenticated ();
566 return ssl_stream.EndRead (asyncResult);
569 public override void EndWrite (IAsyncResult asyncResult)
571 CheckConnectionAuthenticated ();
573 ssl_stream.EndWrite (asyncResult);
576 public override void Flush ()
578 CheckConnectionAuthenticated ();
580 InnerStream.Flush ();
583 public override int Read (byte[] buffer, int offset, int count)
585 return EndRead (BeginRead (buffer, offset, count, null, null));
588 public override long Seek (long offset, SeekOrigin origin)
590 throw new NotSupportedException ("This stream does not support seek operations");
593 public override void SetLength (long value)
595 InnerStream.SetLength (value);
598 public override void Write (byte[] buffer, int offset, int count)
600 EndWrite (BeginWrite (buffer, offset, count, null, null));
603 public void Write (byte[] buffer)
605 Write (buffer, 0, buffer.Length);
608 void CheckConnectionAuthenticated ()
610 if (!IsAuthenticated)
611 throw new InvalidOperationException ("This operation is invalid until it is successfully authenticated");
614 public virtual Task AuthenticateAsClientAsync (string targetHost)
616 return Task.Factory.FromAsync (BeginAuthenticateAsClient, EndAuthenticateAsClient, targetHost, null);
619 public virtual Task AuthenticateAsClientAsync (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
621 var t = Tuple.Create (targetHost, clientCertificates, enabledSslProtocols, checkCertificateRevocation, this);
623 return Task.Factory.FromAsync ((callback, state) => {
624 var d = (Tuple<string, X509CertificateCollection, SslProtocols, bool, SslStream>) state;
625 return d.Item5.BeginAuthenticateAsClient (d.Item1, d.Item2, d.Item3, d.Item4, callback, null);
626 }, EndAuthenticateAsClient, t);
629 public virtual Task AuthenticateAsServerAsync (X509Certificate serverCertificate)
631 return Task.Factory.FromAsync (BeginAuthenticateAsServer, EndAuthenticateAsServer, serverCertificate, null);
634 public virtual Task AuthenticateAsServerAsync (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
636 var t = Tuple.Create (serverCertificate, clientCertificateRequired, enabledSslProtocols, checkCertificateRevocation, this);
638 return Task.Factory.FromAsync ((callback, state) => {
639 var d = (Tuple<X509Certificate, bool, SslProtocols, bool, SslStream>) state;
640 return d.Item5.BeginAuthenticateAsServer (d.Item1, d.Item2, d.Item3, d.Item4, callback, null);
641 }, EndAuthenticateAsServer, t);
644 #endregion // Methods