SslClientStream.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. // Transport Security Layer (TLS)
  2. // Copyright (c) 2003-2004 Carlos Guzman Alvarez
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining
  5. // a copy of this software and associated documentation files (the
  6. // "Software"), to deal in the Software without restriction, including
  7. // without limitation the rights to use, copy, modify, merge, publish,
  8. // distribute, sublicense, and/or sell copies of the Software, and to
  9. // permit persons to whom the Software is furnished to do so, subject to
  10. // the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be
  13. // included in all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  18. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  19. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  20. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  21. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22. //
  23. using System;
  24. using System.Collections;
  25. using System.IO;
  26. using System.Net;
  27. using System.Net.Sockets;
  28. using System.Security.Cryptography;
  29. using System.Security.Cryptography.X509Certificates;
  30. using System.Threading;
  31. using Mono.Security.Protocol.Tls.Handshake;
  32. namespace Mono.Security.Protocol.Tls
  33. {
  34. #region Delegates
  35. public delegate bool CertificateValidationCallback(
  36. X509Certificate certificate,
  37. int[] certificateErrors);
  38. public class ValidationResult {
  39. bool trusted;
  40. bool user_denied;
  41. int error_code;
  42. public ValidationResult (bool trusted, bool user_denied, int error_code)
  43. {
  44. this.trusted = trusted;
  45. this.user_denied = user_denied;
  46. this.error_code = error_code;
  47. }
  48. public bool Trusted {
  49. get { return trusted; }
  50. }
  51. public bool UserDenied {
  52. get { return user_denied; }
  53. }
  54. public int ErrorCode {
  55. get { return error_code; }
  56. }
  57. }
  58. #if MOONLIGHT
  59. internal
  60. #else
  61. public
  62. #endif
  63. delegate ValidationResult CertificateValidationCallback2 (Mono.Security.X509.X509CertificateCollection collection);
  64. public delegate X509Certificate CertificateSelectionCallback(
  65. X509CertificateCollection clientCertificates,
  66. X509Certificate serverCertificate,
  67. string targetHost,
  68. X509CertificateCollection serverRequestedCertificates);
  69. public delegate AsymmetricAlgorithm PrivateKeySelectionCallback(
  70. X509Certificate certificate,
  71. string targetHost);
  72. #endregion
  73. public class SslClientStream : SslStreamBase
  74. {
  75. #region Internal Events
  76. internal event CertificateValidationCallback ServerCertValidation;
  77. internal event CertificateSelectionCallback ClientCertSelection;
  78. internal event PrivateKeySelectionCallback PrivateKeySelection;
  79. #endregion
  80. #region Properties
  81. // required by HttpsClientStream for proxy support
  82. internal Stream InputBuffer
  83. {
  84. get { return base.inputBuffer; }
  85. }
  86. public X509CertificateCollection ClientCertificates
  87. {
  88. get { return this.context.ClientSettings.Certificates; }
  89. }
  90. public X509Certificate SelectedClientCertificate
  91. {
  92. get { return this.context.ClientSettings.ClientCertificate; }
  93. }
  94. #endregion
  95. #region Callback Properties
  96. public CertificateValidationCallback ServerCertValidationDelegate
  97. {
  98. get { return this.ServerCertValidation; }
  99. set { this.ServerCertValidation = value; }
  100. }
  101. public CertificateSelectionCallback ClientCertSelectionDelegate
  102. {
  103. get { return this.ClientCertSelection; }
  104. set { this.ClientCertSelection = value; }
  105. }
  106. public PrivateKeySelectionCallback PrivateKeyCertSelectionDelegate
  107. {
  108. get { return this.PrivateKeySelection; }
  109. set { this.PrivateKeySelection = value; }
  110. }
  111. #endregion
  112. #if MOONLIGHT
  113. internal event CertificateValidationCallback2 ServerCertValidation2;
  114. #else
  115. public event CertificateValidationCallback2 ServerCertValidation2;
  116. #endif
  117. #region Constructors
  118. public SslClientStream(
  119. Stream stream,
  120. string targetHost,
  121. bool ownsStream)
  122. : this(
  123. stream, targetHost, ownsStream,
  124. SecurityProtocolType.Default, null)
  125. {
  126. }
  127. public SslClientStream(
  128. Stream stream,
  129. string targetHost,
  130. X509Certificate clientCertificate)
  131. : this(
  132. stream, targetHost, false, SecurityProtocolType.Default,
  133. new X509CertificateCollection(new X509Certificate[]{clientCertificate}))
  134. {
  135. }
  136. public SslClientStream(
  137. Stream stream,
  138. string targetHost,
  139. X509CertificateCollection clientCertificates) :
  140. this(
  141. stream, targetHost, false, SecurityProtocolType.Default,
  142. clientCertificates)
  143. {
  144. }
  145. public SslClientStream(
  146. Stream stream,
  147. string targetHost,
  148. bool ownsStream,
  149. SecurityProtocolType securityProtocolType)
  150. : this(
  151. stream, targetHost, ownsStream, securityProtocolType,
  152. new X509CertificateCollection())
  153. {
  154. }
  155. public SslClientStream(
  156. Stream stream,
  157. string targetHost,
  158. bool ownsStream,
  159. SecurityProtocolType securityProtocolType,
  160. X509CertificateCollection clientCertificates):
  161. base(stream, ownsStream)
  162. {
  163. if (targetHost == null || targetHost.Length == 0)
  164. {
  165. throw new ArgumentNullException("targetHost is null or an empty string.");
  166. }
  167. this.context = new ClientContext(
  168. this,
  169. securityProtocolType,
  170. targetHost,
  171. clientCertificates);
  172. this.protocol = new ClientRecordProtocol(innerStream, (ClientContext)this.context);
  173. }
  174. #endregion
  175. #region Finalizer
  176. ~SslClientStream()
  177. {
  178. base.Dispose(false);
  179. }
  180. #endregion
  181. #region IDisposable Methods
  182. protected override void Dispose(bool disposing)
  183. {
  184. base.Dispose(disposing);
  185. if (disposing)
  186. {
  187. this.ServerCertValidation = null;
  188. this.ClientCertSelection = null;
  189. this.PrivateKeySelection = null;
  190. this.ServerCertValidation2 = null;
  191. }
  192. }
  193. #endregion
  194. #region Handshake Methods
  195. /*
  196. Client Server
  197. ClientHello -------->
  198. ServerHello
  199. Certificate*
  200. ServerKeyExchange*
  201. CertificateRequest*
  202. <-------- ServerHelloDone
  203. Certificate*
  204. ClientKeyExchange
  205. CertificateVerify*
  206. [ChangeCipherSpec]
  207. Finished -------->
  208. [ChangeCipherSpec]
  209. <-------- Finished
  210. Application Data <-------> Application Data
  211. Fig. 1 - Message flow for a full handshake
  212. */
  213. internal override IAsyncResult OnBeginNegotiateHandshake(AsyncCallback callback, object state)
  214. {
  215. try
  216. {
  217. if (this.context.HandshakeState != HandshakeState.None)
  218. {
  219. this.context.Clear();
  220. }
  221. // Obtain supported cipher suites
  222. this.context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(this.context.SecurityProtocol);
  223. // Set handshake state
  224. this.context.HandshakeState = HandshakeState.Started;
  225. // Send client hello
  226. return this.protocol.BeginSendRecord(HandshakeType.ClientHello, callback, state);
  227. }
  228. catch (TlsException ex)
  229. {
  230. this.protocol.SendAlert(ex.Alert);
  231. throw new IOException("The authentication or decryption has failed.", ex);
  232. }
  233. catch (Exception ex)
  234. {
  235. this.protocol.SendAlert(AlertDescription.InternalError);
  236. throw new IOException("The authentication or decryption has failed.", ex);
  237. }
  238. }
  239. private void SafeReceiveRecord (Stream s)
  240. {
  241. byte[] record = this.protocol.ReceiveRecord (s);
  242. if ((record == null) || (record.Length == 0)) {
  243. throw new TlsException (
  244. AlertDescription.HandshakeFailiure,
  245. "The server stopped the handshake.");
  246. }
  247. }
  248. internal override void OnNegotiateHandshakeCallback(IAsyncResult asyncResult)
  249. {
  250. this.protocol.EndSendRecord(asyncResult);
  251. // Read server response
  252. while (this.context.LastHandshakeMsg != HandshakeType.ServerHelloDone)
  253. {
  254. // Read next record
  255. SafeReceiveRecord (this.innerStream);
  256. // special case for abbreviated handshake where no ServerHelloDone is sent from the server
  257. if (this.context.AbbreviatedHandshake && (this.context.LastHandshakeMsg == HandshakeType.ServerHello))
  258. break;
  259. }
  260. // the handshake is much easier if we can reuse a previous session settings
  261. if (this.context.AbbreviatedHandshake)
  262. {
  263. ClientSessionCache.SetContextFromCache (this.context);
  264. this.context.Negotiating.Cipher.ComputeKeys ();
  265. this.context.Negotiating.Cipher.InitializeCipher ();
  266. // Send Cipher Spec protocol
  267. this.protocol.SendChangeCipherSpec ();
  268. // Read record until server finished is received
  269. while (this.context.HandshakeState != HandshakeState.Finished)
  270. {
  271. // If all goes well this will process messages:
  272. // Change Cipher Spec
  273. // Server finished
  274. SafeReceiveRecord (this.innerStream);
  275. }
  276. // Send Finished message
  277. this.protocol.SendRecord (HandshakeType.Finished);
  278. }
  279. else
  280. {
  281. // Send client certificate if requested
  282. // even if the server ask for it it _may_ still be optional
  283. bool clientCertificate = this.context.ServerSettings.CertificateRequest;
  284. // NOTE: sadly SSL3 and TLS1 differs in how they handle this and
  285. // the current design doesn't allow a very cute way to handle
  286. // SSL3 alert warning for NoCertificate (41).
  287. if (this.context.SecurityProtocol == SecurityProtocolType.Ssl3)
  288. {
  289. clientCertificate = ((this.context.ClientSettings.Certificates != null) &&
  290. (this.context.ClientSettings.Certificates.Count > 0));
  291. // this works well with OpenSSL (but only for SSL3)
  292. }
  293. if (clientCertificate)
  294. {
  295. this.protocol.SendRecord(HandshakeType.Certificate);
  296. }
  297. // Send Client Key Exchange
  298. this.protocol.SendRecord(HandshakeType.ClientKeyExchange);
  299. // Now initialize session cipher with the generated keys
  300. this.context.Negotiating.Cipher.InitializeCipher();
  301. // Send certificate verify if requested (optional)
  302. if (clientCertificate && (this.context.ClientSettings.ClientCertificate != null))
  303. {
  304. this.protocol.SendRecord(HandshakeType.CertificateVerify);
  305. }
  306. // Send Cipher Spec protocol
  307. this.protocol.SendChangeCipherSpec ();
  308. // Send Finished message
  309. this.protocol.SendRecord (HandshakeType.Finished);
  310. // Read record until server finished is received
  311. while (this.context.HandshakeState != HandshakeState.Finished) {
  312. // If all goes well this will process messages:
  313. // Change Cipher Spec
  314. // Server finished
  315. SafeReceiveRecord (this.innerStream);
  316. }
  317. }
  318. // Reset Handshake messages information
  319. this.context.HandshakeMessages.Reset ();
  320. // Clear Key Info
  321. this.context.ClearKeyInfo();
  322. }
  323. #endregion
  324. #region Event Methods
  325. internal override X509Certificate OnLocalCertificateSelection(X509CertificateCollection clientCertificates, X509Certificate serverCertificate, string targetHost, X509CertificateCollection serverRequestedCertificates)
  326. {
  327. if (this.ClientCertSelection != null)
  328. {
  329. return this.ClientCertSelection(
  330. clientCertificates,
  331. serverCertificate,
  332. targetHost,
  333. serverRequestedCertificates);
  334. }
  335. return null;
  336. }
  337. internal override bool HaveRemoteValidation2Callback {
  338. get { return ServerCertValidation2 != null; }
  339. }
  340. internal override ValidationResult OnRemoteCertificateValidation2 (Mono.Security.X509.X509CertificateCollection collection)
  341. {
  342. CertificateValidationCallback2 cb = ServerCertValidation2;
  343. if (cb != null)
  344. return cb (collection);
  345. return null;
  346. }
  347. internal override bool OnRemoteCertificateValidation(X509Certificate certificate, int[] errors)
  348. {
  349. if (this.ServerCertValidation != null)
  350. {
  351. return this.ServerCertValidation(certificate, errors);
  352. }
  353. return (errors != null && errors.Length == 0);
  354. }
  355. internal virtual bool RaiseServerCertificateValidation(
  356. X509Certificate certificate,
  357. int[] certificateErrors)
  358. {
  359. return base.RaiseRemoteCertificateValidation(certificate, certificateErrors);
  360. }
  361. internal virtual ValidationResult RaiseServerCertificateValidation2 (Mono.Security.X509.X509CertificateCollection collection)
  362. {
  363. return base.RaiseRemoteCertificateValidation2 (collection);
  364. }
  365. internal X509Certificate RaiseClientCertificateSelection(
  366. X509CertificateCollection clientCertificates,
  367. X509Certificate serverCertificate,
  368. string targetHost,
  369. X509CertificateCollection serverRequestedCertificates)
  370. {
  371. return base.RaiseLocalCertificateSelection(clientCertificates, serverCertificate, targetHost, serverRequestedCertificates);
  372. }
  373. internal override AsymmetricAlgorithm OnLocalPrivateKeySelection(X509Certificate certificate, string targetHost)
  374. {
  375. if (this.PrivateKeySelection != null)
  376. {
  377. return this.PrivateKeySelection(certificate, targetHost);
  378. }
  379. return null;
  380. }
  381. internal AsymmetricAlgorithm RaisePrivateKeySelection(
  382. X509Certificate certificate,
  383. string targetHost)
  384. {
  385. return base.RaiseLocalPrivateKeySelection(certificate, targetHost);
  386. }
  387. #endregion
  388. }
  389. }