SslClientStream.cs 13 KB

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