PageRenderTime 40ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/Main/Source/Libraries/GSF.SortedTreeStore/Security/SecureStreamClientBase.cs

#
C# | 266 lines | 171 code | 30 blank | 65 comment | 20 complexity | 11dda568c1e94c640796ba17ce01fb68 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, EPL-1.0
  1. //******************************************************************************************************
  2. // SecureStreamClient.cs - Gbtc
  3. //
  4. // Copyright © 2014, Grid Protection Alliance. All Rights Reserved.
  5. //
  6. // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
  7. // the NOTICE file distributed with this work for additional information regarding copyright ownership.
  8. // The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
  9. // not use this file except in compliance with the License. You may obtain a copy of the License at:
  10. //
  11. // http://www.opensource.org/licenses/eclipse-1.0.php
  12. //
  13. // Unless agreed to in writing, the subject software distributed under the License is distributed on an
  14. // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
  15. // License for the specific language governing permissions and limitations.
  16. //
  17. // Code Modification History:
  18. // ----------------------------------------------------------------------------------------------------
  19. // 08/29/2014 - Steven E. Chisholm
  20. // Generated original version of source code.
  21. //
  22. //
  23. //******************************************************************************************************
  24. using System;
  25. using System.IO;
  26. using System.Net.Security;
  27. using System.Security.Authentication;
  28. using System.Security.Cryptography.X509Certificates;
  29. using GSF.Diagnostics;
  30. using GSF.IO;
  31. #if !SQLCLR
  32. using GSF.Security.Authentication;
  33. using Org.BouncyCastle.Crypto;
  34. #endif
  35. namespace GSF.Security
  36. {
  37. /// <summary>
  38. /// Creates a secure stream that connects to a server.
  39. /// </summary>
  40. public abstract class SecureStreamClientBase
  41. : LogSourceBase
  42. {
  43. private static X509Certificate2 s_tempCert;
  44. private byte[] m_resumeTicket;
  45. private byte[] m_sessionSecret;
  46. static SecureStreamClientBase()
  47. {
  48. #if !SQLCLR
  49. s_tempCert = GenerateCertificate.CreateSelfSignedCertificate("CN=Local", 256, 1024);
  50. #endif
  51. }
  52. protected internal SecureStreamClientBase()
  53. {
  54. }
  55. private bool TryConnectSsl(Stream stream, out SslStream ssl)
  56. {
  57. ssl = new SslStream(stream, false, UserCertificateValidationCallback, UserCertificateSelectionCallback, EncryptionPolicy.RequireEncryption);
  58. try
  59. {
  60. ssl.AuthenticateAsClient("Local", null, SslProtocols.Tls12, false);
  61. }
  62. catch (Exception ex)
  63. {
  64. Log.Publish(VerboseLevel.Information, "Authentication Failed", null, null, ex);
  65. ssl.Dispose();
  66. ssl = null;
  67. return false;
  68. }
  69. return true;
  70. }
  71. private X509Certificate UserCertificateSelectionCallback(object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers)
  72. {
  73. return s_tempCert;
  74. }
  75. private bool UserCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
  76. {
  77. return true;
  78. }
  79. /// <summary>
  80. /// Attempts to authenticate the supplied stream.
  81. /// </summary>
  82. /// <param name="stream">The stream to authenticate</param>
  83. /// <param name="useSsl"></param>
  84. /// <param name="secureStream">the secure stream if the authentication was successful.</param>
  85. /// <returns>true if successful</returns>
  86. public bool TryAuthenticate(Stream stream, bool useSsl, out Stream secureStream)
  87. {
  88. secureStream = null;
  89. SslStream ssl = null;
  90. Stream stream2;
  91. byte[] certSignatures;
  92. if (useSsl)
  93. {
  94. if (!TryConnectSsl(stream, out ssl))
  95. return false;
  96. stream2 = ssl;
  97. certSignatures = SecureStream.ComputeCertificateChallenge(false, ssl);
  98. }
  99. else
  100. {
  101. stream2 = stream;
  102. certSignatures = new byte[0];
  103. }
  104. try
  105. {
  106. if (TryResumeSession(ref secureStream, stream2, certSignatures))
  107. return true;
  108. if (InternalTryAuthenticate(stream2, certSignatures))
  109. {
  110. if (stream2.ReadBoolean())
  111. {
  112. m_resumeTicket = stream2.ReadBytes(stream2.ReadNextByte());
  113. m_sessionSecret = stream2.ReadBytes(stream2.ReadNextByte());
  114. }
  115. secureStream = stream2;
  116. return true;
  117. }
  118. if (ssl != null)
  119. ssl.Dispose();
  120. return false;
  121. }
  122. catch (Exception ex)
  123. {
  124. Log.Publish(VerboseLevel.Information, "Authentication Failed", null, null, ex);
  125. if (ssl != null)
  126. ssl.Dispose();
  127. return false;
  128. }
  129. }
  130. private bool TryResumeSession(ref Stream secureStream, Stream stream2, byte[] certSignatures)
  131. {
  132. #if SQLCLR
  133. return false;
  134. #else
  135. if (m_resumeTicket != null && m_sessionSecret != null)
  136. {
  137. //Resume Session:
  138. // C => S
  139. // byte ResumeSession
  140. // byte TicketLength
  141. // byte[] Ticket
  142. // byte ClientChallengeLength
  143. // byte[] ClientChallenge
  144. byte[] clientChallenge = SaltGenerator.Create(16);
  145. stream2.WriteByte((byte)AuthenticationMode.ResumeSession);
  146. stream2.WriteByte((byte)m_resumeTicket.Length);
  147. stream2.Write(m_resumeTicket);
  148. stream2.WriteByte((byte)clientChallenge.Length);
  149. stream2.Write(clientChallenge);
  150. stream2.Flush();
  151. // S <= C
  152. // byte HashMethod
  153. // byte ServerChallengeLength
  154. // byte[] ServerChallenge
  155. HashMethod hashMethod = (HashMethod)stream2.ReadNextByte();
  156. IDigest hash = Scram.CreateDigest(hashMethod);
  157. byte[] serverChallenge = stream2.ReadBytes(stream2.ReadNextByte());
  158. // C => S
  159. // byte ClientResponseLength
  160. // byte[] ClientChallenge
  161. byte[] clientResponse = hash.ComputeHash(serverChallenge, clientChallenge, m_sessionSecret, certSignatures);
  162. byte[] serverResponse = hash.ComputeHash(clientChallenge, serverChallenge, m_sessionSecret, certSignatures);
  163. stream2.WriteByte((byte)clientResponse.Length);
  164. stream2.Write(clientResponse);
  165. stream2.Flush();
  166. // S => C
  167. // bool IsSuccessful
  168. // byte ServerResponseLength
  169. // byte[] ServerResponse
  170. if (stream2.ReadBoolean())
  171. {
  172. byte[] serverResponseCheck = stream2.ReadBytes(stream2.ReadNextByte());
  173. // C => S
  174. // bool IsSuccessful
  175. if (serverResponse.SecureEquals(serverResponseCheck))
  176. {
  177. stream2.Write(true);
  178. stream2.Flush();
  179. secureStream = stream2;
  180. return true;
  181. }
  182. stream2.Write(false);
  183. stream2.Flush();
  184. }
  185. m_resumeTicket = null;
  186. m_sessionSecret = null;
  187. }
  188. return false;
  189. #endif
  190. }
  191. protected abstract bool InternalTryAuthenticate(Stream stream, byte[] certSignatures);
  192. /// <summary>
  193. /// Attempts to authenticate the provided stream, disposing the secure stream upon completion.
  194. /// </summary>
  195. /// <param name="stream">the stream to authenticate</param>
  196. /// <param name="useSsl">gets if SSL will be used to authenticate</param>
  197. /// <returns>true if successful, false otherwise</returns>
  198. public bool TryAuthenticate(Stream stream, bool useSsl = true)
  199. {
  200. Stream secureStream = null;
  201. try
  202. {
  203. return TryAuthenticate(stream, useSsl, out secureStream);
  204. }
  205. finally
  206. {
  207. if (secureStream != null)
  208. secureStream.Dispose();
  209. }
  210. }
  211. /// <summary>
  212. /// Authenticates the supplied stream. Returns the secure stream.
  213. /// </summary>
  214. /// <param name="stream">the stream to authenticate</param>
  215. /// <param name="useSsl">gets if SSL will be used to authenticate</param>
  216. /// <returns></returns>
  217. public Stream Authenticate(Stream stream, bool useSsl = true)
  218. {
  219. Stream secureStream = null;
  220. try
  221. {
  222. if (TryAuthenticate(stream, useSsl, out secureStream))
  223. {
  224. return secureStream;
  225. }
  226. throw new AuthenticationException("Authentication Failed");
  227. }
  228. catch
  229. {
  230. if (secureStream != null)
  231. secureStream.Dispose();
  232. throw;
  233. }
  234. }
  235. }
  236. }