SSLHelper.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. /*
  2. * Copyright: JessMA Open Source (ldcsaa@gmail.com)
  3. *
  4. * Author : Bruce Liang
  5. * Website : https://github.com/ldcsaa
  6. * Project : https://github.com/ldcsaa/HP-Socket
  7. * Blog : http://www.cnblogs.com/ldcsaa
  8. * Wiki : http://www.oschina.net/p/hp-socket
  9. * QQ Group : 44636872, 75375912
  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 "hpsocket/HPTypeDef.h"
  25. #include "common/BufferPool.h"
  26. #ifdef _SSL_SUPPORT
  27. #include "openssl/ssl.h"
  28. #define OPENSSL_VERSION_1_0_2 0x10002000L
  29. #define OPENSSL_VERSION_1_1_0 0x10100000L
  30. #define OPENSSL_VERSION_3_0_0 0x30000000L
  31. #if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
  32. #define DEFAULT_CIPHER_LIST _T("DEFAULT:!aNULL:!eNULL:!SSLv2")
  33. #else
  34. #define DEFAULT_CIPHER_LIST _T("DEFAULT:!aNULL:!eNULL:!SSLv2:!SSLv3")
  35. #endif
  36. #if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_3_0_0
  37. #define FN_X509_NAME_HASH X509_NAME_hash
  38. #else
  39. inline unsigned long FN_X509_NAME_HASH(const X509_NAME *x) {return X509_NAME_hash_ex(x, nullptr, nullptr, nullptr);}
  40. #endif
  41. /************************************************************************
  42. 名称:SSL 全局常量
  43. 描述:声明 SSL 组件的公共全局常量
  44. ************************************************************************/
  45. #define SSL_DOMAIN_SEP_CHAR '.'
  46. /************************************************************************
  47. 名称:SSL 握手状态
  48. 描述:标识当前连接的 SSL 握手状态
  49. ************************************************************************/
  50. enum EnSSLHandShakeStatus
  51. {
  52. SSL_HSS_INIT = 0, // 初始状态
  53. SSL_HSS_PROC = 1, // 正在握手
  54. SSL_HSS_SUCC = 2, // 握手成功
  55. };
  56. #if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
  57. /* SSL CRYPTO DYNLOCK 结构 */
  58. typedef struct CRYPTO_dynlock_value
  59. {
  60. CSimpleRWLock cs;
  61. } DynamicLock;
  62. #endif
  63. class CSSLInitializer
  64. {
  65. public:
  66. static void CleanupThreadState(THR_ID dwThreadID = 0);
  67. private:
  68. CSSLInitializer();
  69. ~CSSLInitializer();
  70. private:
  71. #if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
  72. static void ssl_lock_callback(int mode, int n, const char *file, int line);
  73. static CRYPTO_dynlock_value* ssl_lock_dyn_create_callback(const char *file, int line);
  74. static void ssl_lock_dyn_callback(int mode, CRYPTO_dynlock_value* l, const char *file, int line);
  75. static void ssl_lock_dyn_destroy_callback(CRYPTO_dynlock_value* l, const char *file, int line);
  76. #endif
  77. private:
  78. #if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
  79. static int sm_iLockNum;
  80. static CSimpleRWLock* sm_pcsLocks;
  81. #endif
  82. static CSSLInitializer sm_instance;
  83. };
  84. /************************************************************************
  85. 名称:SSL Context
  86. 描述:初始化和清理 SSL 运行环境
  87. ************************************************************************/
  88. class CSSLContext
  89. {
  90. typedef unordered_map<CString, int, cstring_nc_hash_func::hash, cstring_nc_hash_func::equal_to> CServerNameMap;
  91. public:
  92. /*
  93. * 名称:初始化 SSL 环境参数
  94. * 描述:SSL 环境参数必须在 SSL 通信组件启动前完成初始化,否则启动失败
  95. *
  96. * 参数: enSessionMode -- SSL 工作模式(参考 EnSSLSessionMode)
  97. * iVerifyMode -- SSL 验证模式(参考 EnSSLVerifyMode)
  98. * lpPemCert -- 证书文件(客户端可选)
  99. * lpPemKey -- 私钥文件(客户端可选)
  100. * lpKeyPasswod -- 私钥密码(没有密码则为空)
  101. * lpCAPemCert -- CA 证书文件或目录(单向验证或客户端可选)
  102. * fnServerNameCallback -- SNI 回调函数指针(可选,只用于服务端,如果为 nullptr 则使用 SNI 默认回调函数)
  103. *
  104. * 返回值: TRUE -- 成功
  105. * FALSE -- 失败,可通过 ::GetLastError() 获取失败原因
  106. */
  107. BOOL Initialize(EnSSLSessionMode enSessionMode, int iVerifyMode = SSL_VM_NONE, BOOL bMemory = FALSE, LPVOID lpPemCert = nullptr, LPVOID lpPemKey = nullptr, LPVOID lpKeyPasswod = nullptr, LPVOID lpCAPemCert = nullptr, Fn_SNI_ServerNameCallback fnServerNameCallback = nullptr);
  108. /*
  109. * 名称:增加 SNI 主机证书(只用于服务端)
  110. * 描述:SSL 服务端在 Initialize() 成功后可以调用本方法增加多个 SNI 主机证书
  111. *
  112. * 参数: iVerifyMode -- SSL 验证模式(参考 EnSSLVerifyMode)
  113. * lpPemCert -- 证书文件
  114. * lpPemKey -- 私钥文件
  115. * lpKeyPasswod -- 私钥密码(没有密码则为空)
  116. * lpCAPemCert -- CA 证书文件或目录(单向验证可选)
  117. *
  118. * 返回值: 正数 -- 成功,并返回 SNI 主机证书对应的索引,该索引用于在 SNI 回调函数中定位 SNI 主机
  119. * 负数 -- 失败,可通过 ::GetLastError() 获取失败原因
  120. */
  121. int AddServerContext(int iVerifyMode, BOOL bMemory, LPVOID lpPemCert, LPVOID lpPemKey, LPVOID lpKeyPasswod = nullptr, LPVOID lpCAPemCert = nullptr);
  122. /*
  123. * 名称:绑定 SNI 主机域名
  124. * 描述:SSL 服务端在 AddServerContext() 成功后可以调用本方法绑定主机域名到 SNI 主机证书
  125. *
  126. * 参数: lpszServerName -- 主机域名
  127. * iContextIndex -- SNI 主机证书对应的索引
  128. *
  129. * 返回值: TRUE -- 成功
  130. * FALSE -- 失败,可通过 ::GetLastError() 获取失败原因
  131. */
  132. virtual BOOL BindServerName(LPCTSTR lpszServerName, int iContextIndex);
  133. /*
  134. * 名称:清理 SSL 运行环境
  135. * 描述:清理 SSL 运行环境,回收 SSL 相关内存
  136. * 1、CSSLContext 的析构函数会自动调用本方法
  137. * 2、当要重新设置 SSL 环境参数时,需要先调用本方法清理原先的环境参数
  138. *
  139. * 参数: 无
  140. *
  141. * 返回值:无
  142. */
  143. void Cleanup();
  144. /* 获取 SSL 运行环境 SSL_CTX 对象 */
  145. SSL_CTX* GetContext (int i) const;
  146. /* 获取 SSL 运行环境默认 SSL_CTX 对象 */
  147. SSL_CTX* GetDefaultContext () const {return m_sslCtx;}
  148. /* 获取 SSL 运行环境的配置模式,配置模式参考:EnSSLSessionMode */
  149. EnSSLSessionMode GetSessionMode () const {return m_enSessionMode;}
  150. /* 检查 SSL 运行环境是否初始化完成 */
  151. BOOL IsValid () const {return m_sslCtx != nullptr;}
  152. /* 设置 SSL 加密算法列表 */
  153. void SetCipherList(LPCTSTR lpszCipherList) {m_strCipherList = lpszCipherList;}
  154. /* 获取 SSL 加密算法列表 */
  155. LPCTSTR GetCipherList() {return m_strCipherList;}
  156. public:
  157. /*
  158. * 名称:清理线程局部环境 SSL 资源
  159. * 描述:任何一个操作 SSL 的线程,在通信结束时都需要清理线程局部环境 SSL 资源
  160. * 1、主线程和 HP-Socket 工作线程在通信结束时会自动清理线程局部环境 SSL 资源。因此,一般情况下不必手工调用本方法
  161. * 2、特殊情况下,当自定义线程参与 HP-Socket 通信操作并检查到 SSL 内存泄漏时,需在每次通信结束时自定义线程调用本方法
  162. *
  163. * 参数: dwThreadID -- 线程 ID(0:当前线程)
  164. *
  165. * 返回值:无
  166. */
  167. static void RemoveThreadLocalState(THR_ID dwThreadID = 0) {CSSLInitializer::CleanupThreadState(dwThreadID);}
  168. public:
  169. CSSLContext()
  170. : m_strCipherList (DEFAULT_CIPHER_LIST)
  171. , m_enSessionMode (SSL_SM_SERVER)
  172. , m_sslCtx (nullptr)
  173. , m_fnServerNameCallback(nullptr)
  174. {
  175. }
  176. ~CSSLContext() {Cleanup();}
  177. private:
  178. void SetServerNameCallback(Fn_SNI_ServerNameCallback fn);
  179. int AddContext(int iVerifyMode, BOOL bMemory, LPVOID lpPemCert, LPVOID lpPemKey, LPVOID lpKeyPasswod, LPVOID lpCAPemCert);
  180. BOOL LoadCertAndKey(SSL_CTX* sslCtx, int iVerifyMode, BOOL bMemory, LPVOID lpPemCert, LPVOID lpPemKey, LPVOID lpKeyPasswod, LPVOID lpCAPemCert);
  181. BOOL LoadCertAndKeyByFile(SSL_CTX* sslCtx, int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPassword, LPCTSTR lpszCAPemCertFileOrPath);
  182. BOOL LoadCertAndKeyByMemory(SSL_CTX* sslCtx, int iVerifyMode, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword, LPCSTR lpszCAPemCert);
  183. BOOL LoadCAPemCertByMemory(SSL_CTX* sslCtx, int iVerifyMode, LPCSTR lpszCAPemCert);
  184. BOOL LoadPemCertAndKeyByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword);
  185. BOOL AddCAPemCertToStoreByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert);
  186. BOOL SetClientCAListByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert);
  187. BOOL SetPrivateKeyByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemKey);
  188. BOOL SetCertChainByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert);
  189. private:
  190. static int InternalServerNameCallback(SSL* ssl, int* ad, void* arg);
  191. public:
  192. /*
  193. * 名称:SNI 默认回调函数
  194. * 描述:Initialize 方法中如果不指定 SNI 回调函数则使用此 SNI 默认回调函数
  195. *
  196. * 参数: lpszServerName -- 请求域名
  197. * pContext -- SSL Context 对象
  198. *
  199. * 返回值:SNI 主机证书对应的索引
  200. */
  201. static int __HP_CALL DefaultServerNameCallback(LPCTSTR lpszServerName, PVOID pContext);
  202. private:
  203. CString m_strCipherList;
  204. EnSSLSessionMode m_enSessionMode;
  205. CServerNameMap m_sslServerNames;
  206. vector<SSL_CTX*> m_lsSslCtxs;
  207. SSL_CTX* m_sslCtx;
  208. Fn_SNI_ServerNameCallback m_fnServerNameCallback;
  209. };
  210. class CSSLSession : public CSafeCounter
  211. {
  212. public:
  213. BOOL WriteRecvChannel(const BYTE* pData, int iLength);
  214. BOOL ReadRecvChannel();
  215. BOOL WriteSendChannel(const BYTE* pData, int iLength);
  216. BOOL WriteSendChannel(const WSABUF pBuffers[], int iCount);
  217. BOOL ReadSendChannel();
  218. const WSABUF& GetRecvBuffer() const {return m_bufRecv;}
  219. const WSABUF& GetSendBuffer() const {return m_bufSend;}
  220. CSSLSession* Renew(const CSSLContext& sslCtx, LPCSTR lpszHostName = nullptr);
  221. BOOL Reset();
  222. BOOL IsValid() const {return GetStatus() != SSL_HSS_INIT;}
  223. BOOL IsHandShaking() const {return GetStatus() == SSL_HSS_PROC;}
  224. BOOL IsReady() const {return GetStatus() == SSL_HSS_SUCC;}
  225. EnSSLHandShakeStatus GetStatus() const {return m_enStatus;}
  226. DWORD GetFreeTime() const {return m_dwFreeTime;}
  227. CCriSec& GetSendLock() {return m_csSend;}
  228. BOOL GetSessionInfo(EnSSLSessionInfo enInfo, LPVOID* lppInfo);
  229. private:
  230. BOOL IsFatalError(int iBytes);
  231. public:
  232. CSSLSession(CItemPool& itPool)
  233. : m_enStatus(SSL_HSS_INIT)
  234. , m_itPool (itPool)
  235. , m_ssl (nullptr)
  236. , m_bioSend (nullptr)
  237. , m_bioRecv (nullptr)
  238. , m_pitSend (nullptr)
  239. , m_pitRecv (nullptr)
  240. {
  241. }
  242. ~CSSLSession()
  243. {
  244. Reset();
  245. }
  246. static CSSLSession* Construct(CItemPool& itPool)
  247. {return new CSSLSession(itPool);}
  248. static void Destruct(CSSLSession* pSession)
  249. {if(pSession) delete pSession;}
  250. private:
  251. CItemPool& m_itPool;
  252. CCriSec m_csSend;
  253. DWORD m_dwFreeTime;
  254. EnSSLHandShakeStatus m_enStatus;
  255. SSL* m_ssl;
  256. BIO* m_bioSend;
  257. BIO* m_bioRecv;
  258. TItem* m_pitSend;
  259. TItem* m_pitRecv;
  260. WSABUF m_bufSend;
  261. WSABUF m_bufRecv;
  262. };
  263. class CSSLSessionPool
  264. {
  265. typedef CRingPool<CSSLSession> TSSLSessionList;
  266. typedef CCASQueue<CSSLSession> TSSLSessionQueue;
  267. public:
  268. CSSLSession* PickFreeSession (LPCSTR lpszHostName = nullptr);
  269. void PutFreeSession (CSSLSession* pSession);
  270. void Prepare ();
  271. void Clear ();
  272. void ReleaseGCSession (BOOL bForce = FALSE);
  273. public:
  274. void SetItemCapacity (DWORD dwItemCapacity) {m_itPool.SetItemCapacity(dwItemCapacity);}
  275. void SetItemPoolSize (DWORD dwItemPoolSize) {m_itPool.SetPoolSize(dwItemPoolSize);}
  276. void SetItemPoolHold (DWORD dwItemPoolHold) {m_itPool.SetPoolHold(dwItemPoolHold);}
  277. void SetSessionLockTime (DWORD dwSessionLockTime) {m_dwSessionLockTime = dwSessionLockTime;}
  278. void SetSessionPoolSize (DWORD dwSessionPoolSize) {m_dwSessionPoolSize = dwSessionPoolSize;}
  279. void SetSessionPoolHold (DWORD dwSessionPoolHold) {m_dwSessionPoolHold = dwSessionPoolHold;}
  280. DWORD GetItemCapacity () {return m_itPool.GetItemCapacity();}
  281. DWORD GetItemPoolSize () {return m_itPool.GetPoolSize();}
  282. DWORD GetItemPoolHold () {return m_itPool.GetPoolHold();}
  283. DWORD GetSessionLockTime() {return m_dwSessionLockTime;}
  284. DWORD GetSessionPoolSize() {return m_dwSessionPoolSize;}
  285. DWORD GetSessionPoolHold() {return m_dwSessionPoolHold;}
  286. public:
  287. CSSLSessionPool(const CSSLContext& sslCtx,
  288. DWORD dwPoolSize = DEFAULT_SESSION_POOL_SIZE,
  289. DWORD dwPoolHold = DEFAULT_SESSION_POOL_HOLD,
  290. DWORD dwLockTime = DEFAULT_SESSION_LOCK_TIME)
  291. : m_sslCtx(sslCtx)
  292. , m_dwSessionPoolSize(dwPoolSize)
  293. , m_dwSessionPoolHold(dwPoolHold)
  294. , m_dwSessionLockTime(dwLockTime)
  295. {
  296. }
  297. ~CSSLSessionPool() {Clear();}
  298. DECLARE_NO_COPY_CLASS(CSSLSessionPool)
  299. public:
  300. static const DWORD DEFAULT_ITEM_CAPACITY;
  301. static const DWORD DEFAULT_ITEM_POOL_SIZE;
  302. static const DWORD DEFAULT_ITEM_POOL_HOLD;
  303. static const DWORD DEFAULT_SESSION_LOCK_TIME;
  304. static const DWORD DEFAULT_SESSION_POOL_SIZE;
  305. static const DWORD DEFAULT_SESSION_POOL_HOLD;
  306. private:
  307. CItemPool m_itPool;
  308. const CSSLContext& m_sslCtx;
  309. DWORD m_dwSessionLockTime;
  310. DWORD m_dwSessionPoolSize;
  311. DWORD m_dwSessionPoolHold;
  312. TSSLSessionList m_lsFreeSession;
  313. TSSLSessionQueue m_lsGCSession;
  314. };
  315. template<class T, class S> EnHandleResult ProcessHandShake(T* pThis, S* pSocketObj, CSSLSession* pSession)
  316. {
  317. EnHandleResult result = HR_OK;
  318. CCriSecLock locallock(pSession->GetSendLock());
  319. while(TRUE)
  320. {
  321. VERIFY(pSession->ReadSendChannel());
  322. const WSABUF& buffer = pSession->GetSendBuffer();
  323. if(buffer.len == 0)
  324. break;
  325. if(!pThis->DoSendPackets(pSocketObj, &buffer, 1))
  326. {
  327. result = HR_ERROR;
  328. break;
  329. }
  330. }
  331. return result;
  332. }
  333. template<class T, class S> EnHandleResult ProcessReceive(T* pThis, S* pSocketObj, CSSLSession* pSession, const BYTE* pData, int iLength)
  334. {
  335. if(!pSession->WriteRecvChannel(pData, iLength))
  336. return HR_ERROR;
  337. EnHandleResult result = HR_OK;
  338. EnSSLHandShakeStatus enStatus = pSession->GetStatus();
  339. while(TRUE)
  340. {
  341. if(!pSession->ReadRecvChannel())
  342. return HR_ERROR;
  343. if(enStatus == SSL_HSS_PROC && pSession->IsReady())
  344. {
  345. result = ProcessHandShake(pThis, pSocketObj, pSession);
  346. if(result == HR_ERROR)
  347. break;
  348. enStatus = SSL_HSS_SUCC;
  349. result = pThis->DoFireHandShake(pSocketObj);
  350. if(result == HR_ERROR)
  351. break;
  352. }
  353. const WSABUF& buffer = pSession->GetRecvBuffer();
  354. if(buffer.len == 0)
  355. break;
  356. result = pThis->DoFireReceive(pSocketObj, (const BYTE*)buffer.buf, buffer.len);
  357. if(result == HR_ERROR)
  358. break;
  359. }
  360. if(result != HR_ERROR && pSession->IsHandShaking())
  361. result = ::ProcessHandShake(pThis, pSocketObj, pSession);
  362. return result;
  363. }
  364. template<class T, class S> BOOL ProcessSend(T* pThis, S* pSocketObj, CSSLSession* pSession, const WSABUF * pBuffers, int iCount)
  365. {
  366. if(pSession == nullptr || !pSession->IsReady())
  367. {
  368. ::SetLastError(ERROR_INVALID_STATE);
  369. return FALSE;
  370. }
  371. CCriSecLock locallock(pSession->GetSendLock());
  372. if(!pSession->IsReady())
  373. {
  374. ::SetLastError(ERROR_INVALID_STATE);
  375. return FALSE;
  376. }
  377. VERIFY(pSession->WriteSendChannel(pBuffers, iCount));
  378. while(TRUE)
  379. {
  380. VERIFY(pSession->ReadSendChannel());
  381. const WSABUF& buffer = pSession->GetSendBuffer();
  382. if(buffer.len == 0)
  383. break;
  384. if(!pThis->DoSendPackets(pSocketObj, &buffer, 1))
  385. return FALSE;
  386. }
  387. return TRUE;
  388. }
  389. #endif