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;
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.Security.Authentication;
71 using System.Security.Cryptography.X509Certificates;
72 using System.Security.Principal;
73 using System.Security.Cryptography;
75 using System.Threading.Tasks;
76 using MNS = Mono.Net.Security;
78 namespace System.Net.Security
81 * These two are defined by the referencesource; add them heere to make
82 * it easy to switch between the two implementations.
85 internal delegate bool RemoteCertValidationCallback (
87 X509Certificate certificate,
89 SslPolicyErrors sslPolicyErrors);
91 internal delegate X509Certificate LocalCertSelectionCallback (
93 X509CertificateCollection localCertificates,
94 X509Certificate remoteCertificate,
95 string[] acceptableIssuers);
97 [MonoTODO ("Non-X509Certificate2 certificate is not supported")]
98 public class SslStream : AuthenticatedStream
102 SslStreamBase ssl_stream;
103 RemoteCertificateValidationCallback validation_callback;
104 LocalCertificateSelectionCallback selection_callback;
110 public SslStream (Stream innerStream)
111 : this (innerStream, false)
115 public SslStream (Stream innerStream, bool leaveInnerStreamOpen)
116 : base (innerStream, leaveInnerStreamOpen)
120 [MonoTODO ("userCertificateValidationCallback is not passed X509Chain and SslPolicyErrors correctly")]
121 public SslStream (Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback)
122 : this (innerStream, leaveInnerStreamOpen, userCertificateValidationCallback, null)
126 [MonoTODO ("userCertificateValidationCallback is not passed X509Chain and SslPolicyErrors correctly")]
127 public SslStream (Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback, LocalCertificateSelectionCallback userCertificateSelectionCallback)
128 : base (innerStream, leaveInnerStreamOpen)
130 // they are nullable.
131 validation_callback = userCertificateValidationCallback;
132 selection_callback = userCertificateSelectionCallback;
134 #endregion // Constructors
138 public override bool CanRead {
139 get { return InnerStream.CanRead; }
142 public override bool CanSeek {
143 get { return InnerStream.CanSeek; }
146 public override bool CanTimeout {
147 get { return InnerStream.CanTimeout; }
150 public override bool CanWrite {
151 get { return InnerStream.CanWrite; }
154 public override long Length {
155 get { return InnerStream.Length; }
158 public override long Position {
159 get { return InnerStream.Position; }
161 throw new NotSupportedException ("This stream does not support seek operations");
165 // AuthenticatedStream overrides
167 public override bool IsAuthenticated {
168 get { return ssl_stream != null; }
171 public override bool IsEncrypted {
172 get { return IsAuthenticated; }
175 public override bool IsMutuallyAuthenticated {
176 get { return IsAuthenticated && (IsServer ? RemoteCertificate != null : LocalCertificate != null); }
179 public override bool IsServer {
180 get { return ssl_stream is SslServerStream; }
183 public override bool IsSigned {
184 get { return IsAuthenticated; }
187 public override int ReadTimeout {
188 get { return InnerStream.ReadTimeout; }
189 set { InnerStream.ReadTimeout = value; }
192 public override int WriteTimeout {
193 get { return InnerStream.WriteTimeout; }
194 set { InnerStream.WriteTimeout = value; }
199 public virtual bool CheckCertRevocationStatus {
201 if (!IsAuthenticated)
204 return ssl_stream.CheckCertRevocationStatus;
208 public virtual CipherAlgorithmType CipherAlgorithm {
210 CheckConnectionAuthenticated ();
212 switch (ssl_stream.CipherAlgorithm) {
213 case MonoCipherAlgorithmType.Des:
214 return CipherAlgorithmType.Des;
215 case MonoCipherAlgorithmType.None:
216 return CipherAlgorithmType.None;
217 case MonoCipherAlgorithmType.Rc2:
218 return CipherAlgorithmType.Rc2;
219 case MonoCipherAlgorithmType.Rc4:
220 return CipherAlgorithmType.Rc4;
221 case MonoCipherAlgorithmType.SkipJack:
223 case MonoCipherAlgorithmType.TripleDes:
224 return CipherAlgorithmType.TripleDes;
225 case MonoCipherAlgorithmType.Rijndael:
226 switch (ssl_stream.CipherStrength) {
228 return CipherAlgorithmType.Aes128;
230 return CipherAlgorithmType.Aes192;
232 return CipherAlgorithmType.Aes256;
237 throw new InvalidOperationException ("Not supported cipher algorithm is in use. It is likely a bug in SslStream.");
241 public virtual int CipherStrength {
243 CheckConnectionAuthenticated ();
245 return ssl_stream.CipherStrength;
249 public virtual HashAlgorithmType HashAlgorithm {
251 CheckConnectionAuthenticated ();
253 switch (ssl_stream.HashAlgorithm) {
254 case MonoHashAlgorithmType.Md5:
255 return HashAlgorithmType.Md5;
256 case MonoHashAlgorithmType.None:
257 return HashAlgorithmType.None;
258 case MonoHashAlgorithmType.Sha1:
259 return HashAlgorithmType.Sha1;
262 throw new InvalidOperationException ("Not supported hash algorithm is in use. It is likely a bug in SslStream.");
266 public virtual int HashStrength {
268 CheckConnectionAuthenticated ();
270 return ssl_stream.HashStrength;
274 public virtual ExchangeAlgorithmType KeyExchangeAlgorithm {
276 CheckConnectionAuthenticated ();
278 switch (ssl_stream.KeyExchangeAlgorithm) {
279 case MonoExchangeAlgorithmType.DiffieHellman:
280 return ExchangeAlgorithmType.DiffieHellman;
281 case MonoExchangeAlgorithmType.Fortezza:
283 case MonoExchangeAlgorithmType.None:
284 return ExchangeAlgorithmType.None;
285 case MonoExchangeAlgorithmType.RsaKeyX:
286 return ExchangeAlgorithmType.RsaKeyX;
287 case MonoExchangeAlgorithmType.RsaSign:
288 return ExchangeAlgorithmType.RsaSign;
291 throw new InvalidOperationException ("Not supported exchange algorithm is in use. It is likely a bug in SslStream.");
295 public virtual int KeyExchangeStrength {
297 CheckConnectionAuthenticated ();
299 return ssl_stream.KeyExchangeStrength;
303 public virtual X509Certificate LocalCertificate {
305 CheckConnectionAuthenticated ();
307 return IsServer ? ssl_stream.ServerCertificate : ((SslClientStream) ssl_stream).SelectedClientCertificate;
311 public virtual X509Certificate RemoteCertificate {
313 CheckConnectionAuthenticated ();
314 return !IsServer ? ssl_stream.ServerCertificate : ((SslServerStream) ssl_stream).ClientCertificate;
318 public virtual SslProtocols SslProtocol {
320 CheckConnectionAuthenticated ();
322 switch (ssl_stream.SecurityProtocol) {
323 case MonoSecurityProtocolType.Default:
324 return SslProtocols.Default;
325 case MonoSecurityProtocolType.Ssl2:
326 return SslProtocols.Ssl2;
327 case MonoSecurityProtocolType.Ssl3:
328 return SslProtocols.Ssl3;
329 case MonoSecurityProtocolType.Tls:
330 return SslProtocols.Tls;
333 throw new InvalidOperationException ("Not supported SSL/TLS protocol is in use. It is likely a bug in SslStream.");
337 #endregion // Properties
341 AsymmetricAlgorithm GetPrivateKey (X509Certificate cert, string targetHost)
343 // FIXME: what can I do for non-X509Certificate2 ?
344 X509Certificate2 cert2 = cert as X509Certificate2;
345 return cert2 != null ? cert2.PrivateKey : null;
348 X509Certificate OnCertificateSelection (X509CertificateCollection clientCerts, X509Certificate serverCert, string targetHost, X509CertificateCollection serverRequestedCerts)
350 string [] acceptableIssuers = new string [serverRequestedCerts != null ? serverRequestedCerts.Count : 0];
351 for (int i = 0; i < acceptableIssuers.Length; i++)
352 acceptableIssuers [i] = serverRequestedCerts [i].GetIssuerName ();
353 return selection_callback (this, targetHost, clientCerts, serverCert, acceptableIssuers);
356 public virtual IAsyncResult BeginAuthenticateAsClient (string targetHost, AsyncCallback asyncCallback, object asyncState)
358 return BeginAuthenticateAsClient (targetHost, new X509CertificateCollection (), SslProtocols.Tls, false, asyncCallback, asyncState);
361 public virtual IAsyncResult BeginAuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
364 throw new InvalidOperationException ("This SslStream is already authenticated");
366 SslClientStream s = new SslClientStream (InnerStream, targetHost, !LeaveInnerStreamOpen, GetMonoSslProtocol (enabledSslProtocols), clientCertificates);
367 s.CheckCertRevocationStatus = checkCertificateRevocation;
369 // Due to the Mono.Security internal, it cannot reuse
370 // the delegated argument, as Mono.Security creates
371 // another instance of X509Certificate which lacks
372 // private key but is filled the private key via this
374 s.PrivateKeyCertSelectionDelegate = delegate (X509Certificate cert, string host) {
375 string hash = cert.GetCertHashString ();
376 // ... so, we cannot use the delegate argument.
377 foreach (X509Certificate cc in clientCertificates) {
378 if (cc.GetCertHashString () != hash)
380 X509Certificate2 cert2 = cc as X509Certificate2;
381 cert2 = cert2 ?? new X509Certificate2 (cc);
382 return cert2.PrivateKey;
387 // Even if validation_callback is null this allows us to verify requests where the user
388 // does not provide a verification callback but attempts to authenticate with the website
389 // as a client (see https://bugzilla.xamarin.com/show_bug.cgi?id=18962 for an example)
390 var settings = new MonoTlsSettings ();
391 settings.ServerCertificateValidationCallback = MNS.Private.CallbackHelpers.PublicToMono (validation_callback);
392 var helper = MNS.ChainValidationHelper.Create (ref settings, null);
393 s.ServerCertValidation2 += (certs) => helper.ValidateChain (targetHost, certs);
394 if (selection_callback != null)
395 s.ClientCertSelectionDelegate = OnCertificateSelection;
399 return BeginWrite (new byte [0], 0, 0, asyncCallback, asyncState);
402 public override IAsyncResult BeginRead (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
404 CheckConnectionAuthenticated ();
406 return ssl_stream.BeginRead (buffer, offset, count, asyncCallback, asyncState);
409 public virtual IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, AsyncCallback asyncCallback, object asyncState)
411 return BeginAuthenticateAsServer (serverCertificate, false, SslProtocols.Tls, false, asyncCallback, asyncState);
414 public virtual IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
417 throw new InvalidOperationException ("This SslStream is already authenticated");
419 SslServerStream s = new SslServerStream (InnerStream, serverCertificate, clientCertificateRequired, !LeaveInnerStreamOpen, GetMonoSslProtocol (enabledSslProtocols));
420 s.CheckCertRevocationStatus = checkCertificateRevocation;
421 // Due to the Mono.Security internal, it cannot reuse
422 // the delegated argument, as Mono.Security creates
423 // another instance of X509Certificate which lacks
424 // private key but is filled the private key via this
426 s.PrivateKeyCertSelectionDelegate = delegate (X509Certificate cert, string targetHost) {
427 // ... so, we cannot use the delegate argument.
428 X509Certificate2 cert2 = serverCertificate as X509Certificate2 ?? new X509Certificate2 (serverCertificate);
429 return cert2 != null ? cert2.PrivateKey : null;
432 if (validation_callback != null)
433 s.ClientCertValidationDelegate = delegate (X509Certificate cert, int [] certErrors) {
434 X509Chain chain = null;
435 if (cert is X509Certificate2) {
436 chain = new X509Chain ();
437 chain.Build ((X509Certificate2) cert);
439 // FIXME: SslPolicyErrors is incomplete
440 SslPolicyErrors errors = certErrors.Length > 0 ? SslPolicyErrors.RemoteCertificateChainErrors : SslPolicyErrors.None;
441 return validation_callback (this, cert, chain, errors);
446 return BeginWrite (new byte[0], 0, 0, asyncCallback, asyncState);
449 MonoSecurityProtocolType GetMonoSslProtocol (SslProtocols ms)
452 case SslProtocols.Ssl2:
453 return MonoSecurityProtocolType.Ssl2;
454 case SslProtocols.Ssl3:
455 return MonoSecurityProtocolType.Ssl3;
456 case SslProtocols.Tls:
457 return MonoSecurityProtocolType.Tls;
459 return MonoSecurityProtocolType.Default;
463 public override IAsyncResult BeginWrite (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
465 CheckConnectionAuthenticated ();
467 return ssl_stream.BeginWrite (buffer, offset, count, asyncCallback, asyncState);
470 public virtual void AuthenticateAsClient (string targetHost)
472 AuthenticateAsClient (targetHost, new X509CertificateCollection (), SslProtocols.Tls, false);
475 public virtual void AuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
477 EndAuthenticateAsClient (BeginAuthenticateAsClient (
478 targetHost, clientCertificates, enabledSslProtocols, checkCertificateRevocation, null, null));
481 public virtual void AuthenticateAsServer (X509Certificate serverCertificate)
483 AuthenticateAsServer (serverCertificate, false, SslProtocols.Tls, false);
486 public virtual void AuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
488 EndAuthenticateAsServer (BeginAuthenticateAsServer (
489 serverCertificate, clientCertificateRequired, enabledSslProtocols, checkCertificateRevocation, null, null));
492 protected override void Dispose (bool disposing)
495 if (ssl_stream != null)
496 ssl_stream.Dispose ();
499 base.Dispose (disposing);
502 public virtual void EndAuthenticateAsClient (IAsyncResult asyncResult)
504 CheckConnectionAuthenticated ();
507 ssl_stream.EndRead (asyncResult);
509 ssl_stream.EndWrite (asyncResult);
512 public virtual void EndAuthenticateAsServer (IAsyncResult asyncResult)
514 CheckConnectionAuthenticated ();
517 ssl_stream.EndRead (asyncResult);
519 ssl_stream.EndWrite (asyncResult);
522 public override int EndRead (IAsyncResult asyncResult)
524 CheckConnectionAuthenticated ();
526 return ssl_stream.EndRead (asyncResult);
529 public override void EndWrite (IAsyncResult asyncResult)
531 CheckConnectionAuthenticated ();
533 ssl_stream.EndWrite (asyncResult);
536 public override void Flush ()
538 CheckConnectionAuthenticated ();
540 InnerStream.Flush ();
543 public override int Read (byte[] buffer, int offset, int count)
545 return EndRead (BeginRead (buffer, offset, count, null, null));
548 public override long Seek (long offset, SeekOrigin origin)
550 throw new NotSupportedException ("This stream does not support seek operations");
553 public override void SetLength (long value)
555 InnerStream.SetLength (value);
558 public override void Write (byte[] buffer, int offset, int count)
560 EndWrite (BeginWrite (buffer, offset, count, null, null));
563 public void Write (byte[] buffer)
565 Write (buffer, 0, buffer.Length);
568 void CheckConnectionAuthenticated ()
570 if (!IsAuthenticated)
571 throw new InvalidOperationException ("This operation is invalid until it is successfully authenticated");
574 public virtual Task AuthenticateAsClientAsync (string targetHost)
576 return Task.Factory.FromAsync (BeginAuthenticateAsClient, EndAuthenticateAsClient, targetHost, null);
579 public virtual Task AuthenticateAsClientAsync (string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
581 var t = Tuple.Create (targetHost, clientCertificates, enabledSslProtocols, checkCertificateRevocation, this);
583 return Task.Factory.FromAsync ((callback, state) => {
584 var d = (Tuple<string, X509CertificateCollection, SslProtocols, bool, SslStream>) state;
585 return d.Item5.BeginAuthenticateAsClient (d.Item1, d.Item2, d.Item3, d.Item4, callback, null);
586 }, EndAuthenticateAsClient, t);
589 public virtual Task AuthenticateAsServerAsync (X509Certificate serverCertificate)
591 return Task.Factory.FromAsync (BeginAuthenticateAsServer, EndAuthenticateAsServer, serverCertificate, null);
594 public virtual Task AuthenticateAsServerAsync (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation)
596 var t = Tuple.Create (serverCertificate, clientCertificateRequired, enabledSslProtocols, checkCertificateRevocation, this);
598 return Task.Factory.FromAsync ((callback, state) => {
599 var d = (Tuple<X509Certificate, bool, SslProtocols, bool, SslStream>) state;
600 return d.Item5.BeginAuthenticateAsServer (d.Item1, d.Item2, d.Item3, d.Item4, callback, null);
601 }, EndAuthenticateAsServer, t);
604 #endregion // Methods