SslClientStream.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  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 delegate X509Certificate CertificateSelectionCallback(
  39. X509CertificateCollection clientCertificates,
  40. X509Certificate serverCertificate,
  41. string targetHost,
  42. X509CertificateCollection serverRequestedCertificates);
  43. public delegate AsymmetricAlgorithm PrivateKeySelectionCallback(
  44. X509Certificate certificate,
  45. string targetHost);
  46. #endregion
  47. public class SslClientStream : SslStreamBase
  48. {
  49. #region Internal Events
  50. internal event CertificateValidationCallback ServerCertValidation;
  51. internal event CertificateSelectionCallback ClientCertSelection;
  52. internal event PrivateKeySelectionCallback PrivateKeySelection;
  53. #endregion
  54. #region Properties
  55. // required by HttpsClientStream for proxy support
  56. internal Stream InputBuffer
  57. {
  58. get { return base.inputBuffer; }
  59. }
  60. public X509CertificateCollection ClientCertificates
  61. {
  62. get { return this.context.ClientSettings.Certificates; }
  63. }
  64. public X509Certificate SelectedClientCertificate
  65. {
  66. get { return this.context.ClientSettings.ClientCertificate; }
  67. }
  68. #endregion
  69. #region Callback Properties
  70. public CertificateValidationCallback ServerCertValidationDelegate
  71. {
  72. get { return this.ServerCertValidation; }
  73. set { this.ServerCertValidation = value; }
  74. }
  75. public CertificateSelectionCallback ClientCertSelectionDelegate
  76. {
  77. get { return this.ClientCertSelection; }
  78. set { this.ClientCertSelection = value; }
  79. }
  80. public PrivateKeySelectionCallback PrivateKeyCertSelectionDelegate
  81. {
  82. get { return this.PrivateKeySelection; }
  83. set { this.PrivateKeySelection = value; }
  84. }
  85. #endregion
  86. #region Constructors
  87. public SslClientStream(
  88. Stream stream,
  89. string targetHost,
  90. bool ownsStream)
  91. : this(
  92. stream, targetHost, ownsStream,
  93. SecurityProtocolType.Default, null)
  94. {
  95. }
  96. public SslClientStream(
  97. Stream stream,
  98. string targetHost,
  99. X509Certificate clientCertificate)
  100. : this(
  101. stream, targetHost, false, SecurityProtocolType.Default,
  102. new X509CertificateCollection(new X509Certificate[]{clientCertificate}))
  103. {
  104. }
  105. public SslClientStream(
  106. Stream stream,
  107. string targetHost,
  108. X509CertificateCollection clientCertificates) :
  109. this(
  110. stream, targetHost, false, SecurityProtocolType.Default,
  111. clientCertificates)
  112. {
  113. }
  114. public SslClientStream(
  115. Stream stream,
  116. string targetHost,
  117. bool ownsStream,
  118. SecurityProtocolType securityProtocolType)
  119. : this(
  120. stream, targetHost, ownsStream, securityProtocolType,
  121. new X509CertificateCollection())
  122. {
  123. }
  124. public SslClientStream(
  125. Stream stream,
  126. string targetHost,
  127. bool ownsStream,
  128. SecurityProtocolType securityProtocolType,
  129. X509CertificateCollection clientCertificates):
  130. base(stream, ownsStream)
  131. {
  132. if (targetHost == null || targetHost.Length == 0)
  133. {
  134. throw new ArgumentNullException("targetHost is null or an empty string.");
  135. }
  136. this.context = new ClientContext(
  137. this,
  138. securityProtocolType,
  139. targetHost,
  140. clientCertificates);
  141. this.protocol = new ClientRecordProtocol(innerStream, (ClientContext)this.context);
  142. }
  143. #endregion
  144. #region Finalizer
  145. ~SslClientStream()
  146. {
  147. base.Dispose(false);
  148. }
  149. #endregion
  150. #region IDisposable Methods
  151. protected override void Dispose(bool disposing)
  152. {
  153. base.Dispose(disposing);
  154. if (disposing)
  155. {
  156. this.ServerCertValidation = null;
  157. this.ClientCertSelection = null;
  158. this.PrivateKeySelection = null;
  159. }
  160. }
  161. #endregion
  162. #region Handshake Methods
  163. /*
  164. Client Server
  165. ClientHello -------->
  166. ServerHello
  167. Certificate*
  168. ServerKeyExchange*
  169. CertificateRequest*
  170. <-------- ServerHelloDone
  171. Certificate*
  172. ClientKeyExchange
  173. CertificateVerify*
  174. [ChangeCipherSpec]
  175. Finished -------->
  176. [ChangeCipherSpec]
  177. <-------- Finished
  178. Application Data <-------> Application Data
  179. Fig. 1 - Message flow for a full handshake
  180. */
  181. internal override IAsyncResult OnBeginNegotiateHandshake(AsyncCallback callback, object state)
  182. {
  183. try
  184. {
  185. if (this.context.HandshakeState != HandshakeState.None)
  186. {
  187. this.context.Clear();
  188. }
  189. // Obtain supported cipher suites
  190. this.context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(this.context.SecurityProtocol);
  191. // Set handshake state
  192. this.context.HandshakeState = HandshakeState.Started;
  193. // Send client hello
  194. return this.protocol.BeginSendRecord(HandshakeType.ClientHello, callback, state);
  195. }
  196. catch (TlsException ex)
  197. {
  198. this.protocol.SendAlert(ex.Alert);
  199. throw new IOException("The authentication or decryption has failed.", ex);
  200. }
  201. catch (Exception ex)
  202. {
  203. this.protocol.SendAlert(AlertDescription.InternalError);
  204. throw new IOException("The authentication or decryption has failed.", ex);
  205. }
  206. }
  207. private void SafeReceiveRecord (Stream s)
  208. {
  209. byte[] record = this.protocol.ReceiveRecord (s);
  210. if ((record == null) || (record.Length == 0)) {
  211. throw new TlsException (
  212. AlertDescription.HandshakeFailiure,
  213. "The server stopped the handshake.");
  214. }
  215. }
  216. internal override void OnNegotiateHandshakeCallback(IAsyncResult asyncResult)
  217. {
  218. this.protocol.EndSendRecord(asyncResult);
  219. // Read server response
  220. while (this.context.LastHandshakeMsg != HandshakeType.ServerHelloDone)
  221. {
  222. // Read next record
  223. SafeReceiveRecord (this.innerStream);
  224. // special case for abbreviated handshake where no ServerHelloDone is sent from the server
  225. if (this.context.AbbreviatedHandshake && (this.context.LastHandshakeMsg == HandshakeType.ServerHello))
  226. break;
  227. }
  228. // the handshake is much easier if we can reuse a previous session settings
  229. if (this.context.AbbreviatedHandshake)
  230. {
  231. ClientSessionCache.SetContextFromCache (this.context);
  232. this.context.Negotiating.Cipher.ComputeKeys ();
  233. this.context.Negotiating.Cipher.InitializeCipher ();
  234. // Send Cipher Spec protocol
  235. this.protocol.SendChangeCipherSpec ();
  236. // Read record until server finished is received
  237. while (this.context.HandshakeState != HandshakeState.Finished)
  238. {
  239. // If all goes well this will process messages:
  240. // Change Cipher Spec
  241. // Server finished
  242. SafeReceiveRecord (this.innerStream);
  243. }
  244. // Send Finished message
  245. this.protocol.SendRecord (HandshakeType.Finished);
  246. }
  247. else
  248. {
  249. // Send client certificate if requested
  250. // even if the server ask for it it _may_ still be optional
  251. bool clientCertificate = this.context.ServerSettings.CertificateRequest;
  252. // NOTE: sadly SSL3 and TLS1 differs in how they handle this and
  253. // the current design doesn't allow a very cute way to handle
  254. // SSL3 alert warning for NoCertificate (41).
  255. if (this.context.SecurityProtocol == SecurityProtocolType.Ssl3)
  256. {
  257. clientCertificate = ((this.context.ClientSettings.Certificates != null) &&
  258. (this.context.ClientSettings.Certificates.Count > 0));
  259. // this works well with OpenSSL (but only for SSL3)
  260. }
  261. if (clientCertificate)
  262. {
  263. this.protocol.SendRecord(HandshakeType.Certificate);
  264. }
  265. // Send Client Key Exchange
  266. this.protocol.SendRecord(HandshakeType.ClientKeyExchange);
  267. // Now initialize session cipher with the generated keys
  268. this.context.Negotiating.Cipher.InitializeCipher();
  269. // Send certificate verify if requested (optional)
  270. if (clientCertificate && (this.context.ClientSettings.ClientCertificate != null))
  271. {
  272. this.protocol.SendRecord(HandshakeType.CertificateVerify);
  273. }
  274. // Send Cipher Spec protocol
  275. this.protocol.SendChangeCipherSpec ();
  276. // Send Finished message
  277. this.protocol.SendRecord (HandshakeType.Finished);
  278. // Read record until server finished is received
  279. while (this.context.HandshakeState != HandshakeState.Finished) {
  280. // If all goes well this will process messages:
  281. // Change Cipher Spec
  282. // Server finished
  283. SafeReceiveRecord (this.innerStream);
  284. }
  285. }
  286. // Reset Handshake messages information
  287. this.context.HandshakeMessages.Reset ();
  288. // Clear Key Info
  289. this.context.ClearKeyInfo();
  290. }
  291. #endregion
  292. #region Event Methods
  293. internal override X509Certificate OnLocalCertificateSelection(X509CertificateCollection clientCertificates, X509Certificate serverCertificate, string targetHost, X509CertificateCollection serverRequestedCertificates)
  294. {
  295. if (this.ClientCertSelection != null)
  296. {
  297. return this.ClientCertSelection(
  298. clientCertificates,
  299. serverCertificate,
  300. targetHost,
  301. serverRequestedCertificates);
  302. }
  303. return null;
  304. }
  305. internal override bool OnRemoteCertificateValidation(X509Certificate certificate, int[] errors)
  306. {
  307. if (this.ServerCertValidation != null)
  308. {
  309. return this.ServerCertValidation(certificate, errors);
  310. }
  311. return (errors != null && errors.Length == 0);
  312. }
  313. internal virtual bool RaiseServerCertificateValidation(
  314. X509Certificate certificate,
  315. int[] certificateErrors)
  316. {
  317. return base.RaiseRemoteCertificateValidation(certificate, certificateErrors);
  318. }
  319. internal X509Certificate RaiseClientCertificateSelection(
  320. X509CertificateCollection clientCertificates,
  321. X509Certificate serverCertificate,
  322. string targetHost,
  323. X509CertificateCollection serverRequestedCertificates)
  324. {
  325. return base.RaiseLocalCertificateSelection(clientCertificates, serverCertificate, targetHost, serverRequestedCertificates);
  326. }
  327. internal override AsymmetricAlgorithm OnLocalPrivateKeySelection(X509Certificate certificate, string targetHost)
  328. {
  329. if (this.PrivateKeySelection != null)
  330. {
  331. return this.PrivateKeySelection(certificate, targetHost);
  332. }
  333. return null;
  334. }
  335. internal AsymmetricAlgorithm RaisePrivateKeySelection(
  336. X509Certificate certificate,
  337. string targetHost)
  338. {
  339. return base.RaiseLocalPrivateKeySelection(certificate, targetHost);
  340. }
  341. #endregion
  342. }
  343. }