SslStream.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. //
  2. // System.Net.Security.SslStream.cs
  3. //
  4. // Authors:
  5. // Tim Coleman ([email protected])
  6. // Atsushi Enomoto ([email protected])
  7. //
  8. // Copyright (C) Tim Coleman, 2004
  9. // (c) 2004,2007 Novell, Inc. (http://www.novell.com)
  10. //
  11. //
  12. // Permission is hereby granted, free of charge, to any person obtaining
  13. // a copy of this software and associated documentation files (the
  14. // "Software"), to deal in the Software without restriction, including
  15. // without limitation the rights to use, copy, modify, merge, publish,
  16. // distribute, sublicense, and/or sell copies of the Software, and to
  17. // permit persons to whom the Software is furnished to do so, subject to
  18. // the following conditions:
  19. //
  20. // The above copyright notice and this permission notice shall be
  21. // included in all copies or substantial portions of the Software.
  22. //
  23. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  24. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  26. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  27. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  28. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  29. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  30. //
  31. #if NET_2_0 && SECURITY_DEP
  32. extern alias PrebuiltSystem;
  33. using System;
  34. using System.IO;
  35. using System.Net;
  36. using System.Security.Authentication;
  37. using System.Security.Cryptography.X509Certificates;
  38. using System.Security.Principal;
  39. using System.Security.Cryptography;
  40. using Mono.Security.Protocol.Tls;
  41. using CipherAlgorithmType = System.Security.Authentication.CipherAlgorithmType;
  42. using HashAlgorithmType = System.Security.Authentication.HashAlgorithmType;
  43. 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;
  48. using X509CertificateCollection = PrebuiltSystem::System.Security.Cryptography.X509Certificates.X509CertificateCollection;
  49. namespace System.Net.Security
  50. {
  51. [MonoTODO ("Non-X509Certificate2 certificate is not supported")]
  52. public class SslStream : AuthenticatedStream
  53. {
  54. #region Fields
  55. SslStreamBase ssl_stream;
  56. RemoteCertificateValidationCallback validation_callback;
  57. LocalCertificateSelectionCallback selection_callback;
  58. #endregion // Fields
  59. #region Constructors
  60. public SslStream (Stream innerStream)
  61. : this (innerStream, false)
  62. {
  63. }
  64. public SslStream (Stream innerStream, bool leaveStreamOpen)
  65. : base (innerStream, leaveStreamOpen)
  66. {
  67. }
  68. [MonoTODO ("certValidationCallback is not passed X509Chain and SslPolicyErrors correctly")]
  69. public SslStream (Stream innerStream, bool leaveStreamOpen, RemoteCertificateValidationCallback certValidationCallback)
  70. : this (innerStream, leaveStreamOpen, certValidationCallback, null)
  71. {
  72. }
  73. [MonoTODO ("certValidationCallback is not passed X509Chain and SslPolicyErrors correctly")]
  74. public SslStream (Stream innerStream, bool leaveStreamOpen, RemoteCertificateValidationCallback certValidationCallback, LocalCertificateSelectionCallback certSelectionCallback)
  75. : base (innerStream, leaveStreamOpen)
  76. {
  77. // they are nullable.
  78. validation_callback = certValidationCallback;
  79. selection_callback = certSelectionCallback;
  80. }
  81. #endregion // Constructors
  82. #region Properties
  83. public override bool CanRead {
  84. get { return InnerStream.CanRead; }
  85. }
  86. public override bool CanSeek {
  87. get { return InnerStream.CanSeek; }
  88. }
  89. public override bool CanTimeout {
  90. get { return InnerStream.CanTimeout; }
  91. }
  92. public override bool CanWrite {
  93. get { return InnerStream.CanWrite; }
  94. }
  95. public override long Length {
  96. get { return InnerStream.Length; }
  97. }
  98. public override long Position {
  99. get { return InnerStream.Position; }
  100. set {
  101. throw new NotSupportedException ("This stream does not support seek operations");
  102. }
  103. }
  104. // AuthenticatedStream overrides
  105. public override bool IsAuthenticated {
  106. get { return ssl_stream != null; }
  107. }
  108. public override bool IsEncrypted {
  109. get { return IsAuthenticated; }
  110. }
  111. public override bool IsMutuallyAuthenticated {
  112. get { return IsAuthenticated && (IsServer ? RemoteCertificate != null : LocalCertificate != null); }
  113. }
  114. public override bool IsServer {
  115. get { return ssl_stream is SslServerStream; }
  116. }
  117. public override bool IsSigned {
  118. get { return IsAuthenticated; }
  119. }
  120. public override int ReadTimeout {
  121. get { return InnerStream.ReadTimeout; }
  122. set { InnerStream.ReadTimeout = value; }
  123. }
  124. public override int WriteTimeout {
  125. get { return InnerStream.WriteTimeout; }
  126. set { InnerStream.WriteTimeout = value; }
  127. }
  128. // SslStream
  129. public virtual bool CheckCertRevocationStatus {
  130. get {
  131. if (!IsAuthenticated)
  132. return false;
  133. return ssl_stream.CheckCertRevocationStatus;
  134. }
  135. }
  136. public virtual CipherAlgorithmType CipherAlgorithm {
  137. get {
  138. CheckConnectionAuthenticated ();
  139. switch (ssl_stream.CipherAlgorithm) {
  140. case MonoCipherAlgorithmType.Des:
  141. return CipherAlgorithmType.Des;
  142. case MonoCipherAlgorithmType.None:
  143. return CipherAlgorithmType.None;
  144. case MonoCipherAlgorithmType.Rc2:
  145. return CipherAlgorithmType.Rc2;
  146. case MonoCipherAlgorithmType.Rc4:
  147. return CipherAlgorithmType.Rc4;
  148. case MonoCipherAlgorithmType.SkipJack:
  149. break;
  150. case MonoCipherAlgorithmType.TripleDes:
  151. return CipherAlgorithmType.TripleDes;
  152. case MonoCipherAlgorithmType.Rijndael:
  153. switch (ssl_stream.CipherStrength) {
  154. case 128:
  155. return CipherAlgorithmType.Aes128;
  156. case 192:
  157. return CipherAlgorithmType.Aes192;
  158. case 256:
  159. return CipherAlgorithmType.Aes256;
  160. }
  161. break;
  162. }
  163. throw new InvalidOperationException ("Not supported cipher algorithm is in use. It is likely a bug in SslStream.");
  164. }
  165. }
  166. public virtual int CipherStrength {
  167. get {
  168. CheckConnectionAuthenticated ();
  169. return ssl_stream.CipherStrength;
  170. }
  171. }
  172. public virtual HashAlgorithmType HashAlgorithm {
  173. get {
  174. CheckConnectionAuthenticated ();
  175. switch (ssl_stream.HashAlgorithm) {
  176. case MonoHashAlgorithmType.Md5:
  177. return HashAlgorithmType.Md5;
  178. case MonoHashAlgorithmType.None:
  179. return HashAlgorithmType.None;
  180. case MonoHashAlgorithmType.Sha1:
  181. return HashAlgorithmType.Sha1;
  182. }
  183. throw new InvalidOperationException ("Not supported hash algorithm is in use. It is likely a bug in SslStream.");
  184. }
  185. }
  186. public virtual int HashStrength {
  187. get {
  188. CheckConnectionAuthenticated ();
  189. return ssl_stream.HashStrength;
  190. }
  191. }
  192. public virtual ExchangeAlgorithmType KeyExchangeAlgorithm {
  193. get {
  194. CheckConnectionAuthenticated ();
  195. switch (ssl_stream.KeyExchangeAlgorithm) {
  196. case MonoExchangeAlgorithmType.DiffieHellman:
  197. return ExchangeAlgorithmType.DiffieHellman;
  198. case MonoExchangeAlgorithmType.Fortezza:
  199. break;
  200. case MonoExchangeAlgorithmType.None:
  201. return ExchangeAlgorithmType.None;
  202. case MonoExchangeAlgorithmType.RsaKeyX:
  203. return ExchangeAlgorithmType.RsaKeyX;
  204. case MonoExchangeAlgorithmType.RsaSign:
  205. return ExchangeAlgorithmType.RsaSign;
  206. }
  207. throw new InvalidOperationException ("Not supported exchange algorithm is in use. It is likely a bug in SslStream.");
  208. }
  209. }
  210. public virtual int KeyExchangeStrength {
  211. get {
  212. CheckConnectionAuthenticated ();
  213. return ssl_stream.KeyExchangeStrength;
  214. }
  215. }
  216. public virtual X509Certificate LocalCertificate {
  217. get {
  218. CheckConnectionAuthenticated ();
  219. return IsServer ? ssl_stream.ServerCertificate : ((SslClientStream) ssl_stream).SelectedClientCertificate;
  220. }
  221. }
  222. public virtual X509Certificate RemoteCertificate {
  223. get {
  224. CheckConnectionAuthenticated ();
  225. return !IsServer ? ssl_stream.ServerCertificate : ((SslServerStream) ssl_stream).ClientCertificate;
  226. }
  227. }
  228. public virtual SslProtocols SslProtocol {
  229. get {
  230. CheckConnectionAuthenticated ();
  231. switch (ssl_stream.SecurityProtocol) {
  232. case MonoSecurityProtocolType.Default:
  233. return SslProtocols.Default;
  234. case MonoSecurityProtocolType.Ssl2:
  235. return SslProtocols.Ssl2;
  236. case MonoSecurityProtocolType.Ssl3:
  237. return SslProtocols.Ssl3;
  238. case MonoSecurityProtocolType.Tls:
  239. return SslProtocols.Tls;
  240. }
  241. throw new InvalidOperationException ("Not supported SSL/TLS protocol is in use. It is likely a bug in SslStream.");
  242. }
  243. }
  244. #endregion // Properties
  245. #region Methods
  246. /*
  247. AsymmetricAlgorithm GetPrivateKey (X509Certificate cert, string targetHost)
  248. {
  249. // FIXME: what can I do for non-X509Certificate2 ?
  250. X509Certificate2 cert2 = cert as X509Certificate2;
  251. return cert2 != null ? cert2.PrivateKey : null;
  252. }
  253. */
  254. X509Certificate OnCertificateSelection (X509CertificateCollection clientCerts, X509Certificate serverCert, string targetHost, X509CertificateCollection serverRequestedCerts)
  255. {
  256. string [] acceptableIssuers = new string [serverRequestedCerts != null ? serverRequestedCerts.Count : 0];
  257. for (int i = 0; i < acceptableIssuers.Length; i++)
  258. acceptableIssuers [i] = serverRequestedCerts [i].GetIssuerName ();
  259. return selection_callback (this, targetHost, clientCerts, serverCert, acceptableIssuers);
  260. }
  261. public virtual IAsyncResult BeginAuthenticateAsClient (string targetHost, AsyncCallback asyncCallback, object asyncState)
  262. {
  263. return BeginAuthenticateAsClient (targetHost, new X509CertificateCollection (), SslProtocols.Tls, false, asyncCallback, asyncState);
  264. }
  265. public virtual IAsyncResult BeginAuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols sslProtocolType, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState)
  266. {
  267. if (IsAuthenticated)
  268. throw new InvalidOperationException ("This SslStream is already authenticated");
  269. SslClientStream s = new SslClientStream (InnerStream, targetHost, !LeaveInnerStreamOpen, GetMonoSslProtocol (sslProtocolType), clientCertificates);
  270. s.CheckCertRevocationStatus = checkCertificateRevocation;
  271. // Due to the Mono.Security internal, it cannot reuse
  272. // the delegated argument, as Mono.Security creates
  273. // another instance of X509Certificate which lacks
  274. // private key but is filled the private key via this
  275. // delegate.
  276. s.PrivateKeyCertSelectionDelegate = delegate (X509Certificate cert, string host) {
  277. string hash = cert.GetCertHashString ();
  278. // ... so, we cannot use the delegate argument.
  279. foreach (X509Certificate cc in clientCertificates) {
  280. if (cc.GetCertHashString () != hash)
  281. continue;
  282. X509Certificate2 cert2 = cc as X509Certificate2;
  283. cert2 = cert2 ?? new X509Certificate2 (cc);
  284. return cert2.PrivateKey;
  285. }
  286. return null;
  287. };
  288. if (validation_callback != null)
  289. s.ServerCertValidationDelegate = delegate (X509Certificate cert, int [] certErrors) {
  290. X509Chain chain = new X509Chain ();
  291. X509Certificate2 x2 = (cert as X509Certificate2);
  292. if (x2 == null)
  293. x2 = new X509Certificate2 (cert);
  294. if (!ServicePointManager.CheckCertificateRevocationList)
  295. chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
  296. // SSL specific checks (done by Mono.Security.dll SSL/TLS implementation)
  297. SslPolicyErrors errors = SslPolicyErrors.None;
  298. foreach (int i in certErrors) {
  299. switch (i) {
  300. case -2146762490: // CERT_E_PURPOSE
  301. errors |= SslPolicyErrors.RemoteCertificateNotAvailable;
  302. break;
  303. case -2146762481: // CERT_E_CN_NO_MATCH
  304. errors |= SslPolicyErrors.RemoteCertificateNameMismatch;
  305. break;
  306. default:
  307. errors |= SslPolicyErrors.RemoteCertificateChainErrors;
  308. break;
  309. }
  310. }
  311. chain.Build (x2);
  312. // non-SSL specific X509 checks (i.e. RFC3280 related checks)
  313. foreach (X509ChainStatus status in chain.ChainStatus) {
  314. if ((status.Status & X509ChainStatusFlags.PartialChain) != 0)
  315. errors |= SslPolicyErrors.RemoteCertificateNotAvailable;
  316. else
  317. errors |= SslPolicyErrors.RemoteCertificateChainErrors;
  318. }
  319. return validation_callback (this, cert, chain, errors);
  320. };
  321. if (selection_callback != null)
  322. s.ClientCertSelectionDelegate = OnCertificateSelection;
  323. ssl_stream = s;
  324. return BeginWrite (new byte [0], 0, 0, asyncCallback, asyncState);
  325. }
  326. public override IAsyncResult BeginRead (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
  327. {
  328. CheckConnectionAuthenticated ();
  329. return ssl_stream.BeginRead (buffer, offset, count, asyncCallback, asyncState);
  330. }
  331. public virtual IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, AsyncCallback callback, object asyncState)
  332. {
  333. return BeginAuthenticateAsServer (serverCertificate, false, SslProtocols.Tls, false, callback, asyncState);
  334. }
  335. public virtual IAsyncResult BeginAuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols sslProtocolType, bool checkCertificateRevocation, AsyncCallback callback, object asyncState)
  336. {
  337. if (IsAuthenticated)
  338. throw new InvalidOperationException ("This SslStream is already authenticated");
  339. SslServerStream s = new SslServerStream (InnerStream, serverCertificate, clientCertificateRequired, !LeaveInnerStreamOpen, GetMonoSslProtocol (sslProtocolType));
  340. s.CheckCertRevocationStatus = checkCertificateRevocation;
  341. // Due to the Mono.Security internal, it cannot reuse
  342. // the delegated argument, as Mono.Security creates
  343. // another instance of X509Certificate which lacks
  344. // private key but is filled the private key via this
  345. // delegate.
  346. s.PrivateKeyCertSelectionDelegate = delegate (X509Certificate cert, string targetHost) {
  347. // ... so, we cannot use the delegate argument.
  348. X509Certificate2 cert2 = serverCertificate as X509Certificate2 ?? new X509Certificate2 (serverCertificate);
  349. return cert2 != null ? cert2.PrivateKey : null;
  350. };
  351. if (validation_callback != null)
  352. s.ClientCertValidationDelegate = delegate (X509Certificate cert, int [] certErrors) {
  353. X509Chain chain = null;
  354. if (cert is X509Certificate2) {
  355. chain = new X509Chain ();
  356. chain.Build ((X509Certificate2) cert);
  357. }
  358. // FIXME: SslPolicyErrors is incomplete
  359. SslPolicyErrors errors = certErrors.Length > 0 ? SslPolicyErrors.RemoteCertificateChainErrors : SslPolicyErrors.None;
  360. return validation_callback (this, cert, chain, errors);
  361. };
  362. ssl_stream = s;
  363. return BeginRead (new byte [0], 0, 0, callback, asyncState);
  364. }
  365. MonoSecurityProtocolType GetMonoSslProtocol (SslProtocols ms)
  366. {
  367. switch (ms) {
  368. case SslProtocols.Ssl2:
  369. return MonoSecurityProtocolType.Ssl2;
  370. case SslProtocols.Ssl3:
  371. return MonoSecurityProtocolType.Ssl3;
  372. case SslProtocols.Tls:
  373. return MonoSecurityProtocolType.Tls;
  374. default:
  375. return MonoSecurityProtocolType.Default;
  376. }
  377. }
  378. public override IAsyncResult BeginWrite (byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
  379. {
  380. CheckConnectionAuthenticated ();
  381. return ssl_stream.BeginWrite (buffer, offset, count, asyncCallback, asyncState);
  382. }
  383. public virtual void AuthenticateAsClient (string targetHost)
  384. {
  385. AuthenticateAsClient (targetHost, new X509CertificateCollection (), SslProtocols.Tls, false);
  386. }
  387. public virtual void AuthenticateAsClient (string targetHost, X509CertificateCollection clientCertificates, SslProtocols sslProtocolType, bool checkCertificateRevocation)
  388. {
  389. EndAuthenticateAsClient (BeginAuthenticateAsClient (
  390. targetHost, clientCertificates, sslProtocolType, checkCertificateRevocation, null, null));
  391. }
  392. public virtual void AuthenticateAsServer (X509Certificate serverCertificate)
  393. {
  394. AuthenticateAsServer (serverCertificate, false, SslProtocols.Tls, false);
  395. }
  396. public virtual void AuthenticateAsServer (X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols sslProtocolType, bool checkCertificateRevocation)
  397. {
  398. EndAuthenticateAsServer (BeginAuthenticateAsServer (
  399. serverCertificate, clientCertificateRequired, sslProtocolType, checkCertificateRevocation, null, null));
  400. }
  401. protected override void Dispose (bool disposing)
  402. {
  403. if (disposing) {
  404. if (ssl_stream != null)
  405. ssl_stream.Dispose ();
  406. ssl_stream = null;
  407. }
  408. base.Dispose (disposing);
  409. }
  410. public virtual void EndAuthenticateAsClient (IAsyncResult asyncResult)
  411. {
  412. CheckConnectionAuthenticated ();
  413. if (CanRead)
  414. ssl_stream.EndRead (asyncResult);
  415. else
  416. ssl_stream.EndWrite (asyncResult);
  417. }
  418. public virtual void EndAuthenticateAsServer (IAsyncResult asyncResult)
  419. {
  420. CheckConnectionAuthenticated ();
  421. if (CanRead)
  422. ssl_stream.EndRead (asyncResult);
  423. else
  424. ssl_stream.EndWrite (asyncResult);
  425. }
  426. public override int EndRead (IAsyncResult asyncResult)
  427. {
  428. CheckConnectionAuthenticated ();
  429. return ssl_stream.EndRead (asyncResult);
  430. }
  431. public override void EndWrite (IAsyncResult asyncResult)
  432. {
  433. CheckConnectionAuthenticated ();
  434. ssl_stream.EndWrite (asyncResult);
  435. }
  436. public override void Flush ()
  437. {
  438. CheckConnectionAuthenticated ();
  439. InnerStream.Flush ();
  440. }
  441. public override int Read (byte[] buffer, int offset, int count)
  442. {
  443. return EndRead (BeginRead (buffer, offset, count, null, null));
  444. }
  445. public override long Seek (long offset, SeekOrigin origin)
  446. {
  447. throw new NotSupportedException ("This stream does not support seek operations");
  448. }
  449. public override void SetLength (long value)
  450. {
  451. InnerStream.SetLength (value);
  452. }
  453. public override void Write (byte[] buffer, int offset, int count)
  454. {
  455. EndWrite (BeginWrite (buffer, offset, count, null, null));
  456. }
  457. public void Write (byte[] buffer)
  458. {
  459. Write (buffer, 0, buffer.Length);
  460. }
  461. void CheckConnectionAuthenticated ()
  462. {
  463. if (!IsAuthenticated)
  464. throw new InvalidOperationException ("This operation is invalid until it is successfully authenticated");
  465. }
  466. #endregion // Methods
  467. }
  468. }
  469. #endif