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 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;
75 using System.Threading.Tasks;
78 namespace System.Net.Security
80 [MonoTODO ("Non-X509Certificate2 certificate is not supported")]
81 public class SslStream : AuthenticatedStream
85 SslStreamBase ssl_stream;
86 RemoteCertificateValidationCallback validation_callback;
87 LocalCertificateSelectionCallback selection_callback;
93 public SslStream (Stream innerStream)
94 : this (innerStream, false)
98 public SslStream (Stream innerStream, bool leaveInnerStreamOpen)
99 : base (innerStream, leaveInnerStreamOpen)
103 [MonoTODO ("userCertificateValidationCallback is not passed X509Chain and SslPolicyErrors correctly")]
104 public SslStream (Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback)
105 : this (innerStream, leaveInnerStreamOpen, userCertificateValidationCallback, null)
109 [MonoTODO ("userCertificateValidationCallback is not passed X509Chain and SslPolicyErrors correctly")]
110 public SslStream (Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback, LocalCertificateSelectionCallback userCertificateSelectionCallback)
111 : base (innerStream, leaveInnerStreamOpen)
113 // they are nullable.
114 validation_callback = userCertificateValidationCallback;
115 selection_callback = userCertificateSelectionCallback;
117 #endregion // Constructors
121 public override bool CanRead {
122 get { return InnerStream.CanRead; }
125 public override bool CanSeek {
126 get { return InnerStream.CanSeek; }
129 public override bool CanTimeout {
130 get { return InnerStream.CanTimeout; }
133 public override bool CanWrite {
134 get { return InnerStream.CanWrite; }
137 public override long Length {
138 get { return InnerStream.Length; }
141 public override long Position {
142 get { return InnerStream.Position; }
144 throw new NotSupportedException ("This stream does not support seek operations");
148 // AuthenticatedStream overrides
150 public override bool IsAuthenticated {
151 get { return ssl_stream != null; }
154 public override bool IsEncrypted {
155 get { return IsAuthenticated; }
158 public override bool IsMutuallyAuthenticated {
159 get { return IsAuthenticated && (IsServer ? RemoteCertificate != null : LocalCertificate != null); }
162 public override bool IsServer {
163 get { return ssl_stream is SslServerStream; }
166 public override bool IsSigned {
167 get { return IsAuthenticated; }
170 public override int ReadTimeout {
171 get { return InnerStream.ReadTimeout; }
172 set { InnerStream.ReadTimeout = value; }
175 public override int WriteTimeout {
176 get { return InnerStream.WriteTimeout; }
177 set { InnerStream.WriteTimeout = value; }
182 public virtual bool CheckCertRevocationStatus {
184 if (!IsAuthenticated)
187 return ssl_stream.CheckCertRevocationStatus;
191 public virtual CipherAlgorithmType CipherAlgorithm {
193 CheckConnectionAuthenticated ();
195 switch (ssl_stream.CipherAlgorithm) {
196 case MonoCipherAlgorithmType.Des:
197 return CipherAlgorithmType.Des;
198 case MonoCipherAlgorithmType.None:
199 return CipherAlgorithmType.None;
200 case MonoCipherAlgorithmType.Rc2:
201 return CipherAlgorithmType.Rc2;
202 case MonoCipherAlgorithmType.Rc4:
203 return CipherAlgorithmType.Rc4;
204 case MonoCipherAlgorithmType.SkipJack:
206 case MonoCipherAlgorithmType.TripleDes:
207 return CipherAlgorithmType.TripleDes;
208 case MonoCipherAlgorithmType.Rijndael:
209 switch (ssl_stream.CipherStrength) {
211 return CipherAlgorithmType.Aes128;
213 return CipherAlgorithmType.Aes192;
215 return CipherAlgorithmType.Aes256;
220 throw new InvalidOperationException ("Not supported cipher algorithm is in use. It is likely a bug in SslStream.");
224 public virtual int CipherStrength {
226 CheckConnectionAuthenticated ();
228 return ssl_stream.CipherStrength;
232 public virtual HashAlgorithmType HashAlgorithm {
234 CheckConnectionAuthenticated ();
236 switch (ssl_stream.HashAlgorithm) {
237 case MonoHashAlgorithmType.Md5:
238 return HashAlgorithmType.Md5;
239 case MonoHashAlgorithmType.None:
240 return HashAlgorithmType.None;
241 case MonoHashAlgorithmType.Sha1:
242 return HashAlgorithmType.Sha1;
245 throw new InvalidOperationException ("Not supported hash algorithm is in use. It is likely a bug in SslStream.");
249 public virtual int HashStrength {
251 CheckConnectionAuthenticated ();
253 return ssl_stream.HashStrength;
257 public virtual ExchangeAlgorithmType KeyExchangeAlgorithm {
259 CheckConnectionAuthenticated ();
261 switch (ssl_stream.KeyExchangeAlgorithm) {
262 case MonoExchangeAlgorithmType.DiffieHellman:
263 return ExchangeAlgorithmType.DiffieHellman;
264 case MonoExchangeAlgorithmType.Fortezza:
266 case MonoExchangeAlgorithmType.None:
267 return ExchangeAlgorithmType.None;
268 case MonoExchangeAlgorithmType.RsaKeyX:
269 return ExchangeAlgorithmType.RsaKeyX;
270 case MonoExchangeAlgorithmType.RsaSign:
271 return ExchangeAlgorithmType.RsaSign;
274 throw new InvalidOperationException ("Not supported exchange algorithm is in use. It is likely a bug in SslStream.");
278 public virtual int KeyExchangeStrength {
280 CheckConnectionAuthenticated ();
282 return ssl_stream.KeyExchangeStrength;
286 public virtual X509Certificate LocalCertificate {
288 CheckConnectionAuthenticated ();
290 return IsServer ? ssl_stream.ServerCertificate : ((SslClientStream) ssl_stream).SelectedClientCertificate;
294 public virtual X509Certificate RemoteCertificate {
296 CheckConnectionAuthenticated ();
297 return !IsServer ? ssl_stream.ServerCertificate : ((SslServerStream) ssl_stream).ClientCertificate;
301 public virtual SslProtocols SslProtocol {
303 CheckConnectionAuthenticated ();
305 switch (ssl_stream.SecurityProtocol) {
306 case MonoSecurityProtocolType.Default:
307 return SslProtocols.Default;
308 case MonoSecurityProtocolType.Ssl2:
309 return SslProtocols.Ssl2;
310 case MonoSecurityProtocolType.Ssl3:
311 return SslProtocols.Ssl3;
312 case MonoSecurityProtocolType.Tls:
313 return SslProtocols.Tls;
316 throw new InvalidOperationException ("Not supported SSL/TLS protocol is in use. It is likely a bug in SslStream.");
320 #endregion // Properties
324 AsymmetricAlgorithm GetPrivateKey (X509Certificate cert, string targetHost)
326 // FIXME: what can I do for non-X509Certificate2 ?
327 X509Certificate2 cert2 = cert as X509Certificate2;
328 return cert2 != null ? cert2.PrivateKey : null;
331 X509Certificate OnCertificateSelection (X509CertificateCollection clientCerts, X509Certificate serverCert, string targetHost, X509CertificateCollection serverRequestedCerts)
333 string [] acceptableIssuers = new string [serverRequestedCerts != null ? serverRequestedCerts.Count : 0];
334 for (int i = 0; i < acceptableIssuers.Length; i++)
335 acceptableIssuers [i] = serverRequestedCerts [i].GetIssuerName ();
336 return selection_callback (this, targetHost, clientCerts, serverCert, acceptableIssuers);
339 public virtual IAsyncResult BeginAuthenticateAsClient (string targetHost, AsyncCallback asyncCallback, object asyncState)
341 return BeginAuthenticateAsClient (targetHost, new X509CertificateCollection (), SslProtocols.Tls, false, asyncCallback, asyncState);
344 public virtual IAsyncResult BeginAuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
347 throw new InvalidOperationException ("This SslStream is already authenticated");
349 SslClientStream s = new SslClientStream (InnerStream, targetHost, !LeaveInnerStreamOpen, GetMonoSslProtocol (enabledSslProtocols), clientCertificates);
350 s.CheckCertRevocationStatus = checkCertificateRevocation;
352 // Due to the Mono.Security internal, it cannot reuse
353 // the delegated argument, as Mono.Security creates
354 // another instance of X509Certificate which lacks
355 // private key but is filled the private key via this
357 s.PrivateKeyCertSelectionDelegate = delegate (X509Certificate cert, string host) {
358 string hash = cert.GetCertHashString ();
359 // ... so, we cannot use the delegate argument.
360 foreach (X509Certificate cc in clientCertificates) {
361 if (cc.GetCertHashString () != hash)
363 X509Certificate2 cert2 = cc as X509Certificate2;
364 cert2 = cert2 ?? new X509Certificate2 (cc);
365 return cert2.PrivateKey;
370 if (validation_callback != null) {
372 var helper = new ServicePointManager.ChainValidationHelper (this, targetHost);
373 helper.ServerCertificateValidationCallback = validation_callback;
374 s.ServerCertValidation2 += new CertificateValidationCallback2 (helper.ValidateChain);
376 s.ServerCertValidationDelegate = delegate (X509Certificate cert, int [] certErrors) {
377 X509Chain chain = new X509Chain ();
378 X509Certificate2 x2 = (cert as X509Certificate2);
380 x2 = new X509Certificate2 (cert);
382 if (!ServicePointManager.CheckCertificateRevocationList)
383 chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
385 // SSL specific checks (done by Mono.Security.dll SSL/TLS implementation)
386 SslPolicyErrors errors = SslPolicyErrors.None;
387 foreach (int i in certErrors) {
389 case -2146762490: // CERT_E_PURPOSE
390 errors |= SslPolicyErrors.RemoteCertificateNotAvailable;
392 case -2146762481: // CERT_E_CN_NO_MATCH
393 errors |= SslPolicyErrors.RemoteCertificateNameMismatch;
396 errors |= SslPolicyErrors.RemoteCertificateChainErrors;
403 // non-SSL specific X509 checks (i.e. RFC3280 related checks)
404 foreach (X509ChainStatus status in chain.ChainStatus) {
405 if (status.Status == X509ChainStatusFlags.NoError)
407 if ((status.Status & X509ChainStatusFlags.PartialChain) != 0)
408 errors |= SslPolicyErrors.RemoteCertificateNotAvailable;
410 errors |= SslPolicyErrors.RemoteCertificateChainErrors;
413 return validation_callback (this, cert, chain, errors);
417 if (selection_callback != null)
418 s.ClientCertSelectionDelegate = OnCertificateSelection;
422 return BeginWrite (new byte [0], 0, 0, asyncCallback, asyncState);
425 public override IAsyncResult BeginRead (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
427 CheckConnectionAuthenticated ();
429 return ssl_stream.BeginRead (buffer, offset, count, asyncCallback, asyncState);
432 public virtual IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, AsyncCallback asyncCallback, object asyncState)
434 return BeginAuthenticateAsServer (serverCertificate, false, SslProtocols.Tls, false, asyncCallback, asyncState);
437 public virtual IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
440 throw new InvalidOperationException ("This SslStream is already authenticated");
442 SslServerStream s = new SslServerStream (InnerStream, serverCertificate, clientCertificateRequired, !LeaveInnerStreamOpen, GetMonoSslProtocol (enabledSslProtocols));
443 s.CheckCertRevocationStatus = checkCertificateRevocation;
444 // Due to the Mono.Security internal, it cannot reuse
445 // the delegated argument, as Mono.Security creates
446 // another instance of X509Certificate which lacks
447 // private key but is filled the private key via this
449 s.PrivateKeyCertSelectionDelegate = delegate (X509Certificate cert, string targetHost) {
450 // ... so, we cannot use the delegate argument.
451 X509Certificate2 cert2 = serverCertificate as X509Certificate2 ?? new X509Certificate2 (serverCertificate);
452 return cert2 != null ? cert2.PrivateKey : null;
455 if (validation_callback != null)
456 s.ClientCertValidationDelegate = delegate (X509Certificate cert, int [] certErrors) {
457 X509Chain chain = null;
458 if (cert is X509Certificate2) {
459 chain = new X509Chain ();
460 chain.Build ((X509Certificate2) cert);
462 // FIXME: SslPolicyErrors is incomplete
463 SslPolicyErrors errors = certErrors.Length > 0 ? SslPolicyErrors.RemoteCertificateChainErrors : SslPolicyErrors.None;
464 return validation_callback (this, cert, chain, errors);
469 return BeginWrite (new byte[0], 0, 0, asyncCallback, asyncState);
472 MonoSecurityProtocolType GetMonoSslProtocol (SslProtocols ms)
475 case SslProtocols.Ssl2:
476 return MonoSecurityProtocolType.Ssl2;
477 case SslProtocols.Ssl3:
478 return MonoSecurityProtocolType.Ssl3;
479 case SslProtocols.Tls:
480 return MonoSecurityProtocolType.Tls;
482 return MonoSecurityProtocolType.Default;
486 public override IAsyncResult BeginWrite (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
488 CheckConnectionAuthenticated ();
490 return ssl_stream.BeginWrite (buffer, offset, count, asyncCallback, asyncState);
493 public virtual void AuthenticateAsClient (string targetHost)
495 AuthenticateAsClient (targetHost, new X509CertificateCollection (), SslProtocols.Tls, false);
498 public virtual void AuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
500 EndAuthenticateAsClient (BeginAuthenticateAsClient (
501 targetHost, clientCertificates, enabledSslProtocols, checkCertificateRevocation, null, null));
504 public virtual void AuthenticateAsServer (X509Certificate serverCertificate)
506 AuthenticateAsServer (serverCertificate, false, SslProtocols.Tls, false);
509 public virtual void AuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
511 EndAuthenticateAsServer (BeginAuthenticateAsServer (
512 serverCertificate, clientCertificateRequired, enabledSslProtocols, checkCertificateRevocation, null, null));
515 protected override void Dispose (bool disposing)
518 if (ssl_stream != null)
519 ssl_stream.Dispose ();
522 base.Dispose (disposing);
525 public virtual void EndAuthenticateAsClient (IAsyncResult asyncResult)
527 CheckConnectionAuthenticated ();
530 ssl_stream.EndRead (asyncResult);
532 ssl_stream.EndWrite (asyncResult);
535 public virtual void EndAuthenticateAsServer (IAsyncResult asyncResult)
537 CheckConnectionAuthenticated ();
540 ssl_stream.EndRead (asyncResult);
542 ssl_stream.EndWrite (asyncResult);
545 public override int EndRead (IAsyncResult asyncResult)
547 CheckConnectionAuthenticated ();
549 return ssl_stream.EndRead (asyncResult);
552 public override void EndWrite (IAsyncResult asyncResult)
554 CheckConnectionAuthenticated ();
556 ssl_stream.EndWrite (asyncResult);
559 public override void Flush ()
561 CheckConnectionAuthenticated ();
563 InnerStream.Flush ();
566 public override int Read (byte[] buffer, int offset, int count)
568 return EndRead (BeginRead (buffer, offset, count, null, null));
571 public override long Seek (long offset, SeekOrigin origin)
573 throw new NotSupportedException ("This stream does not support seek operations");
576 public override void SetLength (long value)
578 InnerStream.SetLength (value);
581 public override void Write (byte[] buffer, int offset, int count)
583 EndWrite (BeginWrite (buffer, offset, count, null, null));
586 public void Write (byte[] buffer)
588 Write (buffer, 0, buffer.Length);
591 void CheckConnectionAuthenticated ()
593 if (!IsAuthenticated)
594 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);
629 #endregion // Methods