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 MONOTOUCH || MONODROID
38 using Mono.Security.Protocol.Tls;
40 using CipherAlgorithmType = System.Security.Authentication.CipherAlgorithmType;
41 using HashAlgorithmType = System.Security.Authentication.HashAlgorithmType;
42 using ExchangeAlgorithmType = System.Security.Authentication.ExchangeAlgorithmType;
44 using MonoCipherAlgorithmType = Mono.Security.Protocol.Tls.CipherAlgorithmType;
45 using MonoHashAlgorithmType = Mono.Security.Protocol.Tls.HashAlgorithmType;
46 using MonoExchangeAlgorithmType = Mono.Security.Protocol.Tls.ExchangeAlgorithmType;
47 using MonoSecurityProtocolType = Mono.Security.Protocol.Tls.SecurityProtocolType;
49 extern alias PrebuiltSystem;
50 extern alias MonoSecurity;
52 using X509CertificateCollection = PrebuiltSystem::System.Security.Cryptography.X509Certificates.X509CertificateCollection;
54 using CipherAlgorithmType = System.Security.Authentication.CipherAlgorithmType;
55 using HashAlgorithmType = System.Security.Authentication.HashAlgorithmType;
56 using ExchangeAlgorithmType = System.Security.Authentication.ExchangeAlgorithmType;
58 using MonoCipherAlgorithmType = MonoSecurity::Mono.Security.Protocol.Tls.CipherAlgorithmType;
59 using MonoHashAlgorithmType = MonoSecurity::Mono.Security.Protocol.Tls.HashAlgorithmType;
60 using MonoExchangeAlgorithmType = MonoSecurity::Mono.Security.Protocol.Tls.ExchangeAlgorithmType;
61 using MonoSecurityProtocolType = MonoSecurity::Mono.Security.Protocol.Tls.SecurityProtocolType;
63 using MonoSecurity::Mono.Security.Protocol.Tls;
69 using System.Security.Authentication;
70 using System.Security.Cryptography.X509Certificates;
71 using System.Security.Principal;
72 using System.Security.Cryptography;
74 using System.Threading.Tasks;
76 namespace System.Net.Security
78 [MonoTODO ("Non-X509Certificate2 certificate is not supported")]
79 public class SslStream : AuthenticatedStream
83 SslStreamBase ssl_stream;
84 RemoteCertificateValidationCallback validation_callback;
85 LocalCertificateSelectionCallback selection_callback;
91 public SslStream (Stream innerStream)
92 : this (innerStream, false)
96 public SslStream (Stream innerStream, bool leaveInnerStreamOpen)
97 : base (innerStream, leaveInnerStreamOpen)
101 [MonoTODO ("userCertificateValidationCallback is not passed X509Chain and SslPolicyErrors correctly")]
102 public SslStream (Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback)
103 : this (innerStream, leaveInnerStreamOpen, userCertificateValidationCallback, null)
107 [MonoTODO ("userCertificateValidationCallback is not passed X509Chain and SslPolicyErrors correctly")]
108 public SslStream (Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback, LocalCertificateSelectionCallback userCertificateSelectionCallback)
109 : base (innerStream, leaveInnerStreamOpen)
111 // they are nullable.
112 validation_callback = userCertificateValidationCallback;
113 selection_callback = userCertificateSelectionCallback;
115 #endregion // Constructors
119 public override bool CanRead {
120 get { return InnerStream.CanRead; }
123 public override bool CanSeek {
124 get { return InnerStream.CanSeek; }
127 public override bool CanTimeout {
128 get { return InnerStream.CanTimeout; }
131 public override bool CanWrite {
132 get { return InnerStream.CanWrite; }
135 public override long Length {
136 get { return InnerStream.Length; }
139 public override long Position {
140 get { return InnerStream.Position; }
142 throw new NotSupportedException ("This stream does not support seek operations");
146 // AuthenticatedStream overrides
148 public override bool IsAuthenticated {
149 get { return ssl_stream != null; }
152 public override bool IsEncrypted {
153 get { return IsAuthenticated; }
156 public override bool IsMutuallyAuthenticated {
157 get { return IsAuthenticated && (IsServer ? RemoteCertificate != null : LocalCertificate != null); }
160 public override bool IsServer {
161 get { return ssl_stream is SslServerStream; }
164 public override bool IsSigned {
165 get { return IsAuthenticated; }
168 public override int ReadTimeout {
169 get { return InnerStream.ReadTimeout; }
170 set { InnerStream.ReadTimeout = value; }
173 public override int WriteTimeout {
174 get { return InnerStream.WriteTimeout; }
175 set { InnerStream.WriteTimeout = value; }
180 public virtual bool CheckCertRevocationStatus {
182 if (!IsAuthenticated)
185 return ssl_stream.CheckCertRevocationStatus;
189 public virtual CipherAlgorithmType CipherAlgorithm {
191 CheckConnectionAuthenticated ();
193 switch (ssl_stream.CipherAlgorithm) {
194 case MonoCipherAlgorithmType.Des:
195 return CipherAlgorithmType.Des;
196 case MonoCipherAlgorithmType.None:
197 return CipherAlgorithmType.None;
198 case MonoCipherAlgorithmType.Rc2:
199 return CipherAlgorithmType.Rc2;
200 case MonoCipherAlgorithmType.Rc4:
201 return CipherAlgorithmType.Rc4;
202 case MonoCipherAlgorithmType.SkipJack:
204 case MonoCipherAlgorithmType.TripleDes:
205 return CipherAlgorithmType.TripleDes;
206 case MonoCipherAlgorithmType.Rijndael:
207 switch (ssl_stream.CipherStrength) {
209 return CipherAlgorithmType.Aes128;
211 return CipherAlgorithmType.Aes192;
213 return CipherAlgorithmType.Aes256;
218 throw new InvalidOperationException ("Not supported cipher algorithm is in use. It is likely a bug in SslStream.");
222 public virtual int CipherStrength {
224 CheckConnectionAuthenticated ();
226 return ssl_stream.CipherStrength;
230 public virtual HashAlgorithmType HashAlgorithm {
232 CheckConnectionAuthenticated ();
234 switch (ssl_stream.HashAlgorithm) {
235 case MonoHashAlgorithmType.Md5:
236 return HashAlgorithmType.Md5;
237 case MonoHashAlgorithmType.None:
238 return HashAlgorithmType.None;
239 case MonoHashAlgorithmType.Sha1:
240 return HashAlgorithmType.Sha1;
243 throw new InvalidOperationException ("Not supported hash algorithm is in use. It is likely a bug in SslStream.");
247 public virtual int HashStrength {
249 CheckConnectionAuthenticated ();
251 return ssl_stream.HashStrength;
255 public virtual ExchangeAlgorithmType KeyExchangeAlgorithm {
257 CheckConnectionAuthenticated ();
259 switch (ssl_stream.KeyExchangeAlgorithm) {
260 case MonoExchangeAlgorithmType.DiffieHellman:
261 return ExchangeAlgorithmType.DiffieHellman;
262 case MonoExchangeAlgorithmType.Fortezza:
264 case MonoExchangeAlgorithmType.None:
265 return ExchangeAlgorithmType.None;
266 case MonoExchangeAlgorithmType.RsaKeyX:
267 return ExchangeAlgorithmType.RsaKeyX;
268 case MonoExchangeAlgorithmType.RsaSign:
269 return ExchangeAlgorithmType.RsaSign;
272 throw new InvalidOperationException ("Not supported exchange algorithm is in use. It is likely a bug in SslStream.");
276 public virtual int KeyExchangeStrength {
278 CheckConnectionAuthenticated ();
280 return ssl_stream.KeyExchangeStrength;
284 public virtual X509Certificate LocalCertificate {
286 CheckConnectionAuthenticated ();
288 return IsServer ? ssl_stream.ServerCertificate : ((SslClientStream) ssl_stream).SelectedClientCertificate;
292 public virtual X509Certificate RemoteCertificate {
294 CheckConnectionAuthenticated ();
295 return !IsServer ? ssl_stream.ServerCertificate : ((SslServerStream) ssl_stream).ClientCertificate;
299 public virtual SslProtocols SslProtocol {
301 CheckConnectionAuthenticated ();
303 switch (ssl_stream.SecurityProtocol) {
304 case MonoSecurityProtocolType.Default:
305 return SslProtocols.Default;
306 case MonoSecurityProtocolType.Ssl2:
307 return SslProtocols.Ssl2;
308 case MonoSecurityProtocolType.Ssl3:
309 return SslProtocols.Ssl3;
310 case MonoSecurityProtocolType.Tls:
311 return SslProtocols.Tls;
314 throw new InvalidOperationException ("Not supported SSL/TLS protocol is in use. It is likely a bug in SslStream.");
318 #endregion // Properties
322 AsymmetricAlgorithm GetPrivateKey (X509Certificate cert, string targetHost)
324 // FIXME: what can I do for non-X509Certificate2 ?
325 X509Certificate2 cert2 = cert as X509Certificate2;
326 return cert2 != null ? cert2.PrivateKey : null;
329 X509Certificate OnCertificateSelection (X509CertificateCollection clientCerts, X509Certificate serverCert, string targetHost, X509CertificateCollection serverRequestedCerts)
331 string [] acceptableIssuers = new string [serverRequestedCerts != null ? serverRequestedCerts.Count : 0];
332 for (int i = 0; i < acceptableIssuers.Length; i++)
333 acceptableIssuers [i] = serverRequestedCerts [i].GetIssuerName ();
334 return selection_callback (this, targetHost, clientCerts, serverCert, acceptableIssuers);
337 public virtual IAsyncResult BeginAuthenticateAsClient (string targetHost, AsyncCallback asyncCallback, object asyncState)
339 return BeginAuthenticateAsClient (targetHost, new X509CertificateCollection (), SslProtocols.Tls, false, asyncCallback, asyncState);
342 public virtual IAsyncResult BeginAuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
345 throw new InvalidOperationException ("This SslStream is already authenticated");
347 SslClientStream s = new SslClientStream (InnerStream, targetHost, !LeaveInnerStreamOpen, GetMonoSslProtocol (enabledSslProtocols), clientCertificates);
348 s.CheckCertRevocationStatus = checkCertificateRevocation;
350 // Due to the Mono.Security internal, it cannot reuse
351 // the delegated argument, as Mono.Security creates
352 // another instance of X509Certificate which lacks
353 // private key but is filled the private key via this
355 s.PrivateKeyCertSelectionDelegate = delegate (X509Certificate cert, string host) {
356 string hash = cert.GetCertHashString ();
357 // ... so, we cannot use the delegate argument.
358 foreach (X509Certificate cc in clientCertificates) {
359 if (cc.GetCertHashString () != hash)
361 X509Certificate2 cert2 = cc as X509Certificate2;
362 cert2 = cert2 ?? new X509Certificate2 (cc);
363 return cert2.PrivateKey;
368 #if MONOTOUCH || MONODROID
369 // Even if validation_callback is null this allows us to verify requests where the user
370 // does not provide a verification callback but attempts to authenticate with the website
371 // as a client (see https://bugzilla.xamarin.com/show_bug.cgi?id=18962 for an example)
372 var helper = new ServicePointManager.ChainValidationHelper (this, targetHost);
373 helper.ServerCertificateValidationCallback = validation_callback;
374 s.ServerCertValidation2 += new CertificateValidationCallback2 (helper.ValidateChain);
376 if (validation_callback != null) {
377 s.ServerCertValidationDelegate = delegate (X509Certificate cert, int [] certErrors) {
378 X509Chain chain = new X509Chain ();
379 X509Certificate2 x2 = (cert as X509Certificate2);
381 x2 = new X509Certificate2 (cert);
383 if (!ServicePointManager.CheckCertificateRevocationList)
384 chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
386 // SSL specific checks (done by Mono.Security.dll SSL/TLS implementation)
387 SslPolicyErrors errors = SslPolicyErrors.None;
388 foreach (int i in certErrors) {
390 case -2146762490: // CERT_E_PURPOSE
391 errors |= SslPolicyErrors.RemoteCertificateNotAvailable;
393 case -2146762481: // CERT_E_CN_NO_MATCH
394 errors |= SslPolicyErrors.RemoteCertificateNameMismatch;
397 errors |= SslPolicyErrors.RemoteCertificateChainErrors;
404 // non-SSL specific X509 checks (i.e. RFC3280 related checks)
405 foreach (X509ChainStatus status in chain.ChainStatus) {
406 if (status.Status == X509ChainStatusFlags.NoError)
408 if ((status.Status & X509ChainStatusFlags.PartialChain) != 0)
409 errors |= SslPolicyErrors.RemoteCertificateNotAvailable;
411 errors |= SslPolicyErrors.RemoteCertificateChainErrors;
414 return validation_callback (this, cert, chain, errors);
418 if (selection_callback != null)
419 s.ClientCertSelectionDelegate = OnCertificateSelection;
423 return BeginWrite (new byte [0], 0, 0, asyncCallback, asyncState);
426 public override IAsyncResult BeginRead (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
428 CheckConnectionAuthenticated ();
430 return ssl_stream.BeginRead (buffer, offset, count, asyncCallback, asyncState);
433 public virtual IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, AsyncCallback asyncCallback, object asyncState)
435 return BeginAuthenticateAsServer (serverCertificate, false, SslProtocols.Tls, false, asyncCallback, asyncState);
438 public virtual IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
441 throw new InvalidOperationException ("This SslStream is already authenticated");
443 SslServerStream s = new SslServerStream (InnerStream, serverCertificate, clientCertificateRequired, !LeaveInnerStreamOpen, GetMonoSslProtocol (enabledSslProtocols));
444 s.CheckCertRevocationStatus = checkCertificateRevocation;
445 // Due to the Mono.Security internal, it cannot reuse
446 // the delegated argument, as Mono.Security creates
447 // another instance of X509Certificate which lacks
448 // private key but is filled the private key via this
450 s.PrivateKeyCertSelectionDelegate = delegate (X509Certificate cert, string targetHost) {
451 // ... so, we cannot use the delegate argument.
452 X509Certificate2 cert2 = serverCertificate as X509Certificate2 ?? new X509Certificate2 (serverCertificate);
453 return cert2 != null ? cert2.PrivateKey : null;
456 if (validation_callback != null)
457 s.ClientCertValidationDelegate = delegate (X509Certificate cert, int [] certErrors) {
458 X509Chain chain = null;
459 if (cert is X509Certificate2) {
460 chain = new X509Chain ();
461 chain.Build ((X509Certificate2) cert);
463 // FIXME: SslPolicyErrors is incomplete
464 SslPolicyErrors errors = certErrors.Length > 0 ? SslPolicyErrors.RemoteCertificateChainErrors : SslPolicyErrors.None;
465 return validation_callback (this, cert, chain, errors);
470 return BeginWrite (new byte[0], 0, 0, asyncCallback, asyncState);
473 MonoSecurityProtocolType GetMonoSslProtocol (SslProtocols ms)
476 case SslProtocols.Ssl2:
477 return MonoSecurityProtocolType.Ssl2;
478 case SslProtocols.Ssl3:
479 return MonoSecurityProtocolType.Ssl3;
480 case SslProtocols.Tls:
481 return MonoSecurityProtocolType.Tls;
483 return MonoSecurityProtocolType.Default;
487 public override IAsyncResult BeginWrite (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
489 CheckConnectionAuthenticated ();
491 return ssl_stream.BeginWrite (buffer, offset, count, asyncCallback, asyncState);
494 public virtual void AuthenticateAsClient (string targetHost)
496 AuthenticateAsClient (targetHost, new X509CertificateCollection (), SslProtocols.Tls, false);
499 public virtual void AuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
501 EndAuthenticateAsClient (BeginAuthenticateAsClient (
502 targetHost, clientCertificates, enabledSslProtocols, checkCertificateRevocation, null, null));
505 public virtual void AuthenticateAsServer (X509Certificate serverCertificate)
507 AuthenticateAsServer (serverCertificate, false, SslProtocols.Tls, false);
510 public virtual void AuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
512 EndAuthenticateAsServer (BeginAuthenticateAsServer (
513 serverCertificate, clientCertificateRequired, enabledSslProtocols, checkCertificateRevocation, null, null));
516 protected override void Dispose (bool disposing)
519 if (ssl_stream != null)
520 ssl_stream.Dispose ();
523 base.Dispose (disposing);
526 public virtual void EndAuthenticateAsClient (IAsyncResult asyncResult)
528 CheckConnectionAuthenticated ();
531 ssl_stream.EndRead (asyncResult);
533 ssl_stream.EndWrite (asyncResult);
536 public virtual void EndAuthenticateAsServer (IAsyncResult asyncResult)
538 CheckConnectionAuthenticated ();
541 ssl_stream.EndRead (asyncResult);
543 ssl_stream.EndWrite (asyncResult);
546 public override int EndRead (IAsyncResult asyncResult)
548 CheckConnectionAuthenticated ();
550 return ssl_stream.EndRead (asyncResult);
553 public override void EndWrite (IAsyncResult asyncResult)
555 CheckConnectionAuthenticated ();
557 ssl_stream.EndWrite (asyncResult);
560 public override void Flush ()
562 CheckConnectionAuthenticated ();
564 InnerStream.Flush ();
567 public override int Read (byte[] buffer, int offset, int count)
569 return EndRead (BeginRead (buffer, offset, count, null, null));
572 public override long Seek (long offset, SeekOrigin origin)
574 throw new NotSupportedException ("This stream does not support seek operations");
577 public override void SetLength (long value)
579 InnerStream.SetLength (value);
582 public override void Write (byte[] buffer, int offset, int count)
584 EndWrite (BeginWrite (buffer, offset, count, null, null));
587 public void Write (byte[] buffer)
589 Write (buffer, 0, buffer.Length);
592 void CheckConnectionAuthenticated ()
594 if (!IsAuthenticated)
595 throw new InvalidOperationException ("This operation is invalid until it is successfully authenticated");
598 public virtual Task AuthenticateAsClientAsync (string targetHost)
600 return Task.Factory.FromAsync (BeginAuthenticateAsClient, EndAuthenticateAsClient, targetHost, null);
603 public virtual Task AuthenticateAsClientAsync (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
605 var t = Tuple.Create (targetHost, clientCertificates, enabledSslProtocols, checkCertificateRevocation, this);
607 return Task.Factory.FromAsync ((callback, state) => {
608 var d = (Tuple<string, X509CertificateCollection, SslProtocols, bool, SslStream>) state;
609 return d.Item5.BeginAuthenticateAsClient (d.Item1, d.Item2, d.Item3, d.Item4, callback, null);
610 }, EndAuthenticateAsClient, t);
613 public virtual Task AuthenticateAsServerAsync (X509Certificate serverCertificate)
615 return Task.Factory.FromAsync (BeginAuthenticateAsServer, EndAuthenticateAsServer, serverCertificate, null);
618 public virtual Task AuthenticateAsServerAsync (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
620 var t = Tuple.Create (serverCertificate, clientCertificateRequired, enabledSslProtocols, checkCertificateRevocation, this);
622 return Task.Factory.FromAsync ((callback, state) => {
623 var d = (Tuple<X509Certificate, bool, SslProtocols, bool, SslStream>) state;
624 return d.Item5.BeginAuthenticateAsServer (d.Item1, d.Item2, d.Item3, d.Item4, callback, null);
625 }, EndAuthenticateAsServer, t);
628 #endregion // Methods