工具项目

SSLHelper.h 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. /*
  2. * Copyright: JessMA Open Source (ldcsaa@gmail.com)
  3. *
  4. * Author : Bruce Liang
  5. * Website : http://www.jessma.org
  6. * Project : https://github.com/ldcsaa
  7. * Blog : http://www.cnblogs.com/ldcsaa
  8. * Wiki : http://www.oschina.net/p/hp-socket
  9. * QQ Group : 75375912, 44636872
  10. *
  11. * Licensed under the Apache License, Version 2.0 (the "License");
  12. * you may not use this file except in compliance with the License.
  13. * You may obtain a copy of the License at
  14. *
  15. * http://www.apache.org/licenses/LICENSE-2.0
  16. *
  17. * Unless required by applicable law or agreed to in writing, software
  18. * distributed under the License is distributed on an "AS IS" BASIS,
  19. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  20. * See the License for the specific language governing permissions and
  21. * limitations under the License.
  22. */
  23. #pragma once
  24. #include "HPTypeDef.h"
  25. #include "../Common/Src/BufferPool.h"
  26. #ifdef _SSL_SUPPORT
  27. #pragma warning(push)
  28. #pragma warning(disable: 4005)
  29. #include "openssl/ssl.h"
  30. #pragma warning(pop)
  31. #define OPENSSL_VERSION_1_0_2 0x10002000L
  32. #define OPENSSL_VERSION_1_1_0 0x10100000L
  33. /************************************************************************
  34. 名称:SSL 握手状态
  35. 描述:标识当前连接的 SSL 握手状态
  36. ************************************************************************/
  37. enum EnSSLHandShakeStatus
  38. {
  39. SSL_HSS_INIT = 0, // 初始状态
  40. SSL_HSS_PROC = 1, // 正在握手
  41. SSL_HSS_SUCC = 2, // 握手成功
  42. };
  43. #if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
  44. /* SSL CRYPTO DYNLOCK 结构 */
  45. typedef struct CRYPTO_dynlock_value
  46. {
  47. CSimpleRWLock cs;
  48. } DynamicLock;
  49. #endif
  50. class CSSLInitializer
  51. {
  52. public:
  53. static void CleanupThreadState(DWORD dwThreadID = 0);
  54. private:
  55. CSSLInitializer();
  56. ~CSSLInitializer();
  57. private:
  58. #if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
  59. static void ssl_lock_callback(int mode, int n, const char *file, int line);
  60. static CRYPTO_dynlock_value* ssl_lock_dyn_create_callback(const char *file, int line);
  61. static void ssl_lock_dyn_callback(int mode, CRYPTO_dynlock_value* l, const char *file, int line);
  62. static void ssl_lock_dyn_destroy_callback(CRYPTO_dynlock_value* l, const char *file, int line);
  63. #endif
  64. private:
  65. #if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
  66. static int sm_iLockNum;
  67. static CSimpleRWLock* sm_pcsLocks;
  68. #endif
  69. static CSSLInitializer sm_instance;
  70. };
  71. /************************************************************************
  72. 名称:SSL Context
  73. 描述:初始化和清理 SSL 运行环境
  74. ************************************************************************/
  75. class CSSLContext
  76. {
  77. public:
  78. /*
  79. * 名称:初始化 SSL 环境参数
  80. * 描述:SSL 环境参数必须在 SSL 通信组件启动前完成初始化,否则启动失败
  81. *
  82. * 参数: enSessionMode -- SSL 工作模式(参考 EnSSLSessionMode)
  83. * iVerifyMode -- SSL 验证模式(参考 EnSSLVerifyMode)
  84. * lpszPemCertFile -- 证书文件(客户端可选)
  85. * lpszPemKeyFile -- 私钥文件(客户端可选)
  86. * lpszKeyPasswod -- 私钥密码(没有密码则为空)
  87. * lpszCAPemCertFileOrPath -- CA 证书文件或目录(单向验证或客户端可选)
  88. * fnServerNameCallback -- SNI 回调函数指针(可选,只用于服务端)
  89. *
  90. * 返回值: TRUE -- 成功
  91. * FALSE -- 失败,可通过 ::GetLastError() 获取失败原因
  92. */
  93. BOOL Initialize(EnSSLSessionMode enSessionMode, int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPasswod = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr, Fn_SNI_ServerNameCallback fnServerNameCallback = nullptr);
  94. /*
  95. * 名称:增加 SNI 主机证书(只用于服务端)
  96. * 描述:SSL 服务端在 Initialize() 成功后可以调用本方法增加多个 SNI 主机证书
  97. *
  98. * 参数: iVerifyMode -- SSL 验证模式(参考 EnSSLVerifyMode)
  99. * lpszPemCertFile -- 证书文件
  100. * lpszPemKeyFile -- 私钥文件
  101. * lpszKeyPasswod -- 私钥密码(没有密码则为空)
  102. * lpszCAPemCertFileOrPath -- CA 证书文件或目录(单向验证可选)
  103. *
  104. * 返回值: 正数 -- 成功,并返回 SNI 主机证书对应的索引,该索引用于在 SNI 回调函数中定位 SNI 主机
  105. * 负数 -- 失败,可通过 ::GetLastError() 获取失败原因
  106. */
  107. int AddServerContext(int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPasswod = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr);
  108. /*
  109. * 名称:清理 SSL 运行环境
  110. * 描述:清理 SSL 运行环境,回收 SSL 相关内存
  111. * 1、CSSLContext 的析构函数会自动调用本方法
  112. * 2、当要重新设置 SSL 环境参数时,需要先调用本方法清理原先的环境参数
  113. *
  114. * 参数: 无
  115. *
  116. * 返回值:无
  117. */
  118. void Cleanup();
  119. /* 获取 SSL 运行环境 SSL_CTX 对象 */
  120. SSL_CTX* GetContext (int i) const;
  121. /* 获取 SSL 运行环境默认 SSL_CTX 对象 */
  122. SSL_CTX* GetDefaultContext () const {return m_sslCtx;}
  123. /* 获取 SSL 运行环境的配置模式,配置模式参考:EnSSLSessionMode */
  124. EnSSLSessionMode GetSessionMode () const {return m_enSessionMode;}
  125. /* 检查 SSL 运行环境是否初始化完成 */
  126. BOOL IsValid () const {return m_sslCtx != nullptr;}
  127. public:
  128. /*
  129. * 名称:清理线程局部环境 SSL 资源
  130. * 描述:任何一个操作 SSL 的线程,在通信结束时都需要清理线程局部环境 SSL 资源
  131. * 1、主线程和 HP-Socket 工作线程在通信结束时会自动清理线程局部环境 SSL 资源。因此,一般情况下不必手工调用本方法
  132. * 2、特殊情况下,当自定义线程参与 HP-Socket 通信操作并检查到 SSL 内存泄漏时,需在每次通信结束时自定义线程调用本方法
  133. *
  134. * 参数: dwThreadID -- 线程 ID(0:当前线程)
  135. *
  136. * 返回值:无
  137. */
  138. static void RemoveThreadLocalState(DWORD dwThreadID = 0) {CSSLInitializer::CleanupThreadState(dwThreadID);}
  139. public:
  140. CSSLContext()
  141. : m_enSessionMode (SSL_SM_SERVER)
  142. , m_sslCtx (nullptr)
  143. , m_fnServerNameCallback(nullptr)
  144. {
  145. }
  146. ~CSSLContext() {Cleanup();}
  147. private:
  148. void SetServerNameCallback(Fn_SNI_ServerNameCallback fn);
  149. int AddContext(int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPasswod, LPCTSTR lpszCAPemCertFileOrPath);
  150. BOOL LoadCertAndKey(SSL_CTX* sslCtx, int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPasswod, LPCTSTR lpszCAPemCertFileOrPath);
  151. private:
  152. static int CALLBACK InternalServerNameCallback(SSL* ssl, int* ad, void* arg);
  153. private:
  154. EnSSLSessionMode m_enSessionMode;
  155. vector<SSL_CTX*> m_lsSslCtxs;
  156. SSL_CTX* m_sslCtx;
  157. Fn_SNI_ServerNameCallback m_fnServerNameCallback;
  158. };
  159. class CSSLSession
  160. {
  161. public:
  162. BOOL WriteRecvChannel(const BYTE* pData, int iLength);
  163. BOOL ReadRecvChannel();
  164. BOOL WriteSendChannel(const BYTE* pData, int iLength);
  165. BOOL WriteSendChannel(const WSABUF pBuffers[], int iCount);
  166. BOOL ReadSendChannel();
  167. const WSABUF& GetRecvBuffer() const {return m_bufRecv;}
  168. const WSABUF& GetSendBuffer() const {return m_bufSend;}
  169. CSSLSession* Renew(const CSSLContext& sslCtx, LPCSTR lpszHostName = nullptr);
  170. BOOL Reset();
  171. BOOL IsValid() const {return GetStatus() != SSL_HSS_INIT;}
  172. BOOL IsHandShaking() const {return GetStatus() == SSL_HSS_PROC;}
  173. BOOL IsReady() const {return GetStatus() == SSL_HSS_SUCC;}
  174. EnSSLHandShakeStatus GetStatus() const {return m_enStatus;}
  175. DWORD GetFreeTime() const {return m_dwFreeTime;}
  176. CCriSec& GetSendLock() {return m_csSend;}
  177. private:
  178. BOOL IsFatalError(int iBytes);
  179. public:
  180. CSSLSession(CItemPool& itPool)
  181. : m_enStatus(SSL_HSS_INIT)
  182. , m_itPool (itPool)
  183. , m_ssl (nullptr)
  184. , m_bioSend (nullptr)
  185. , m_bioRecv (nullptr)
  186. , m_pitSend (nullptr)
  187. , m_pitRecv (nullptr)
  188. {
  189. }
  190. ~CSSLSession()
  191. {
  192. Reset();
  193. }
  194. private:
  195. CItemPool& m_itPool;
  196. CCriSec m_csSend;
  197. DWORD m_dwFreeTime;
  198. EnSSLHandShakeStatus m_enStatus;
  199. SSL* m_ssl;
  200. BIO* m_bioSend;
  201. BIO* m_bioRecv;
  202. TItem* m_pitSend;
  203. TItem* m_pitRecv;
  204. WSABUF m_bufSend;
  205. WSABUF m_bufRecv;
  206. };
  207. class CSSLSessionPool
  208. {
  209. typedef CRingPool<CSSLSession> TSSLSessionList;
  210. typedef CCASQueue<CSSLSession> TSSLSessionQueue;
  211. public:
  212. CSSLSession* PickFreeSession (LPCSTR lpszHostName = nullptr);
  213. void PutFreeSession (CSSLSession* pSession);
  214. void Prepare ();
  215. void Clear ();
  216. private:
  217. void ReleaseGCSession (BOOL bForce = FALSE);
  218. public:
  219. void SetItemCapacity (DWORD dwItemCapacity) {m_itPool.SetItemCapacity(dwItemCapacity);}
  220. void SetItemPoolSize (DWORD dwItemPoolSize) {m_itPool.SetPoolSize(dwItemPoolSize);}
  221. void SetItemPoolHold (DWORD dwItemPoolHold) {m_itPool.SetPoolHold(dwItemPoolHold);}
  222. void SetSessionLockTime (DWORD dwSessionLockTime) {m_dwSessionLockTime = dwSessionLockTime;}
  223. void SetSessionPoolSize (DWORD dwSessionPoolSize) {m_dwSessionPoolSize = dwSessionPoolSize;}
  224. void SetSessionPoolHold (DWORD dwSessionPoolHold) {m_dwSessionPoolHold = dwSessionPoolHold;}
  225. DWORD GetItemCapacity () {return m_itPool.GetItemCapacity();}
  226. DWORD GetItemPoolSize () {return m_itPool.GetPoolSize();}
  227. DWORD GetItemPoolHold () {return m_itPool.GetPoolHold();}
  228. DWORD GetSessionLockTime() {return m_dwSessionLockTime;}
  229. DWORD GetSessionPoolSize() {return m_dwSessionPoolSize;}
  230. DWORD GetSessionPoolHold() {return m_dwSessionPoolHold;}
  231. public:
  232. CSSLSessionPool(const CSSLContext& sslCtx,
  233. DWORD dwPoolSize = DEFAULT_SESSION_POOL_SIZE,
  234. DWORD dwPoolHold = DEFAULT_SESSION_POOL_HOLD,
  235. DWORD dwLockTime = DEFAULT_SESSION_LOCK_TIME)
  236. : m_sslCtx(sslCtx)
  237. , m_dwSessionPoolSize(dwPoolSize)
  238. , m_dwSessionPoolHold(dwPoolHold)
  239. , m_dwSessionLockTime(dwLockTime)
  240. {
  241. }
  242. ~CSSLSessionPool() {Clear();}
  243. DECLARE_NO_COPY_CLASS(CSSLSessionPool)
  244. public:
  245. static const DWORD DEFAULT_ITEM_CAPACITY;
  246. static const DWORD DEFAULT_ITEM_POOL_SIZE;
  247. static const DWORD DEFAULT_ITEM_POOL_HOLD;
  248. static const DWORD DEFAULT_SESSION_LOCK_TIME;
  249. static const DWORD DEFAULT_SESSION_POOL_SIZE;
  250. static const DWORD DEFAULT_SESSION_POOL_HOLD;
  251. private:
  252. CItemPool m_itPool;
  253. const CSSLContext& m_sslCtx;
  254. DWORD m_dwSessionLockTime;
  255. DWORD m_dwSessionPoolSize;
  256. DWORD m_dwSessionPoolHold;
  257. TSSLSessionList m_lsFreeSession;
  258. TSSLSessionQueue m_lsGCSession;
  259. };
  260. template<class T, class S> EnHandleResult ProcessHandShake(T* pThis, S* pSocketObj, CSSLSession* pSession)
  261. {
  262. EnHandleResult result = HR_OK;
  263. CCriSecLock locallock(pSession->GetSendLock());
  264. while(TRUE)
  265. {
  266. VERIFY(pSession->ReadSendChannel());
  267. const WSABUF& buffer = pSession->GetSendBuffer();
  268. if(buffer.len == 0)
  269. break;
  270. if(!pThis->DoSendPackets(pSocketObj, &buffer, 1))
  271. {
  272. result = HR_ERROR;
  273. break;
  274. }
  275. }
  276. return result;
  277. }
  278. template<class T, class S> EnHandleResult ProcessReceive(T* pThis, S* pSocketObj, CSSLSession* pSession, const BYTE* pData, int iLength)
  279. {
  280. if(!pSession->WriteRecvChannel(pData, iLength))
  281. return HR_ERROR;
  282. EnHandleResult result = HR_OK;
  283. EnSSLHandShakeStatus enStatus = pSession->GetStatus();
  284. while(TRUE)
  285. {
  286. if(!pSession->ReadRecvChannel())
  287. return HR_ERROR;
  288. if(enStatus == SSL_HSS_PROC && pSession->IsReady())
  289. {
  290. result = ProcessHandShake(pThis, pSocketObj, pSession);
  291. if(result == HR_ERROR)
  292. break;
  293. enStatus = SSL_HSS_SUCC;
  294. result = pThis->DoFireHandShake(pSocketObj);
  295. if(result == HR_ERROR)
  296. break;
  297. }
  298. const WSABUF& buffer = pSession->GetRecvBuffer();
  299. if(buffer.len == 0)
  300. break;
  301. result = pThis->DoFireReceive(pSocketObj, (const BYTE*)buffer.buf, buffer.len);
  302. if(result == HR_ERROR)
  303. break;
  304. }
  305. if(result != HR_ERROR && pSession->IsHandShaking())
  306. result = ::ProcessHandShake(pThis, pSocketObj, pSession);
  307. return result;
  308. }
  309. template<class T, class S> BOOL ProcessSend(T* pThis, S* pSocketObj, CSSLSession* pSession, const WSABUF * pBuffers, int iCount)
  310. {
  311. if(pSession == nullptr || !pSession->IsReady())
  312. {
  313. ::SetLastError(ERROR_INVALID_STATE);
  314. return FALSE;
  315. }
  316. CCriSecLock locallock(pSession->GetSendLock());
  317. if(!pSession->IsReady())
  318. {
  319. ::SetLastError(ERROR_INVALID_STATE);
  320. return FALSE;
  321. }
  322. VERIFY(pSession->WriteSendChannel(pBuffers, iCount));
  323. while(TRUE)
  324. {
  325. VERIFY(pSession->ReadSendChannel());
  326. const WSABUF& buffer = pSession->GetSendBuffer();
  327. if(buffer.len == 0)
  328. break;
  329. if(!pThis->DoSendPackets(pSocketObj, &buffer, 1))
  330. return FALSE;
  331. }
  332. return TRUE;
  333. }
  334. #endif