一、背景
1.1 定义
SSL(Secure Sockets Layer 安全套接字协议),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层与应用层之间对网络连接进行加密。
1.2 解析
Secure Socket Layer,为Netscape所研发,用以保障在Internet上数据传输的安全,利用数据加密(Encryption)技术,可确保数据在网络上的传输过程中不会被截取及窃听。一般通用的规格为40 bit的安全标准,美国则已推出128 bit的更高安全标准。只要3.0版本以上的I.E.或Netscape浏览器即可支持SSL。
当前版本为3.0。它已被广泛地用于Web浏览器与服务器之间的身份认证和加密数据传输。
SSL协议位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持。SSL协议可分为两层: SSL记录协议(SSL Record Protocol):它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。 SSL握手协议(SSL Handshake Protocol):它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。
1.3 提供服务
1)认证用户和服务器,确保数据发送到正确的客户机和服务器;
2)加密数据以防止数据中途被窃取;
3)维护数据的完整性,确保数据在传输过程中不被改变。
1.4 工作流程
服务器认证阶段:1)客户端向服务器发送一个开始信息“Hello”以便开始一个新的会话连接;2)服务器根据客户的信息确定是否需要生成新的主密钥,如需要则服务器在响应客户的“Hello”信息时将包含生成主密钥所需的信息;3)客户根据收到的服务器响应信息,产生一个主密钥,并用服务器的公开密钥加密后传给服务器;4)服务器回复该主密钥,并返回给客户一个用主密钥认证的信息,以此让客户认证服务器。
用户认证阶段:在此之前,服务器已经通过了客户认证,这一阶段主要完成对客户的认证。经认证的服务器发送一个提问给客户,客户则返回(数字)签名后的提问和其公开密钥,从而向服务器提供认证。
SSL协议提供的安全通道有以下三个特性:
机密性:SSL协议使用密钥加密通信数据。
可靠性:服务器和客户都会被认证,客户的认证是可选的。
完整性:SSL协议会对传送的数据进行完整性检查。
从SSL 协议所提供的服务及其工作流程可以看出,SSL协议运行的基础是商家对消费者信息保密的承诺,这就有利于商家而不利于消费者。在电子商务初级阶段,由于运作电子商务的企业大多是信誉较高的大公司,因此这问题还没有充分暴露出来。但随着电子商务的发展,各中小型公司也参与进来,这样在电子支付过程中的单一认证问题就越来越突出。虽然在SSL3.0中通过数字签名和数字证书可实现浏览器和Web服务器双方的身份验证,但是SSL协议仍存在一些问题,比如,只能提供交易中客户与服务器间的双方认证,在涉及多方的电子交易中,SSL协议并不能协调各方间的安全传输和信任关系。在这种情况下,Visa和 MasterCard两大信用卡公司组织制定了SET协议,为网上信用卡支付提供了全球性的标准。
参考百度百科
二、SSL运行环境
hp-socket v3.5版本开始,所有的TCP组件全面支持SSL。SSL 组件与对应的非 SSL 组件实现相同的接口,它们的使用方式也一致。
SSL 组件在启动通信前需要初始化 SSL 环境参数,通信完毕时需要清理 SSL 运行环境。HP-Socket v4.x.x 及其之前的版本使用全局唯一 SSL 运行环境,所有 SSL 组件都共享该环境,并 通 过 HP_SSL_Initialize() / HP_SSL_AddServerContext() / HP_SSL_Cleanup() /
HP_SSL_IsValid() 等全局函数操作该全局 SSL 环境, HP-Socket v5.0.x 版本开始,每个 SSL组件使用独立的 SSL 运行环境,因此上述函数已被删除。取而代之, SSL 组件提供相应的实例方法来操作自身的 SSL 运行环境:
2.1 设置SSL加密套件
VOID SetSSLCipherList(lpszCipherList)
HP-Socket 的默认加密套件为 DEFAULT:!aNULL:!eNULL:!SSLv2:!SSLv3,如需修改加密套件(如: 启用 SSLv3 用来支持旧应用系统) 则需要调用 SetSSLCipherList() 设置所需的加密套件。加密套件详细信息请参考 OpenSSL Ciphers 文档。
注 意 : 要 使 加 密 套 件 设 置 生 效 , 必 须 在 调 用SetupSSLContext()
、AddSSLContext()
方法前调用 SetSSLCiperList()
设置加密套件
2.2 初始化SSL环境参数
BOOL SetupSSLContext(enSessionMode, iVerifyMode = SSL_VM_NONE,
lpszPemCertFile = nullptr, lpszPemKeyFile = nullptr, lpszKeyPasswod = nullptr,
lpszCAPemCertFileOrPath = nullptr, [fnServerNameCallback = nullptr])
BOOL SetupSSLContextByMemory(enSessionMode, iVerifyMode = SSL_VM_NONE,
lpszPemCert = nullptr, lpszPemKey = nullptr, lpszKeyPasswod = nullptr, lpszCAPemCert =
nullptr, [fnServerNameCallback = nullptr])
参数 iVerifyMode 指定 SSL 验证模式;参数lpszPemCert[File]、 lpszPemKey[File]、lpszKeyPasswod
和 lpszCAPemCert[FileOrPath]
分别指定证书文件、 私钥文件、 私钥密码和CA 证书文件/目录; 参数fnServerNameCallback
只用于 HTTPS 服务端,此参数指定 SNI 回
调 函 数 指 针 , 如 果 此 参 数 值 为 nullptr 则 使 用 HP-Socket 默 认 SNI 回 调 函 数 :HP_SSL_DefaultServerNameCallback(lpszServerName, pContext)
, 该默认 SNI 回调函数根据BindSSLServerName(lpszServerName, iContextIndex)
设置的域名-证书绑定关系查找证书, 如果找不到则使用默认证书。初始化成功返回 TRUE,失败返回 FALSE,初始化失败可通过SYS_GetLastError() 获取错误代码。(参数详细说明请参考 include/hpsocket/HPSocket-SSL.h或 include/hpsocket/HPSocket4C-SSL.h 头文件)
2.3 增加 SNI 主机证书(SSL Server 组件)
int AddSSLContext(iVerifyMode, lpszPemCertFile, lpszPemKeyFile, lpszKeyPasswod =
nullptr, lpszCAPemCertFileOrPath = nullptr)
int AddSSLContextByMemory(iVerifyMode, lpszPemCert, lpszPemKey, lpszKeyPasswod =
nullptr, lpszCAPemCert = nullptr)
仅用于 SSL Server 组件。 参数 iVerifyMode 指定 SSL 验证模式;参数 lpszPemCert[File]、lpszPemKey[File]、 lpszKeyPasswod
和 lpszCAPemCert[FileOrPath]
分别指定证书文件、 私钥文件、 私钥密码和 CA 证书文件/目录。执行成功返回 SNI 主机证书对应的索引,该索引用于在 SNI 回调函数中定位 SNI 主机;失败返回-1, 可通过 SYS_GetLastError()
获取错误代码。
2.4 绑定服务器主机域名到 SNI 证书(SSL Server 组件)
BOOL BindSSLServerName(lpszServerName, iContextIndex)
仅用于 SSL Server 组件。把服务器主机域名lpszServerName
绑定到iContextIndex
证书索引,默认证书索引为 0, 其他证书索引由AddSSLContext() / AddSSLContextByMemory()
创建。绑定失败返回 FALSE, 可通过 SYS_GetLastError()
获取错误代码。
2.5 默认 SNI 回调函数(SSL Server 组件)
int HP_SSL_DefaultServerNameCallback(lpszServerName, pContext)
仅用于 SSL Server 组件。获取服务器主机域名 lpszServerName
对应的证书索引,如果该主机域名未绑定到任何证书则返回默认证书索引 0。
注意: 服务端应用程序一般在初始化 SSL 运行环境时调用 AddSSLContext[ByMemory]()
加载所有 SNI 主机证书, 并调用BindSSLServerName()
绑定服务器主机域名到相应证书。一些特殊应用可能要动态加载 SNI 主机证书, 此时需要对调用 AddSSLContext()、
BindSSLServerName()
和HP_SSL_DefaultServerNameCallback()
的代码段进行同步处理,避免由于多线程导致的访问冲突。
2.6 获取 SSL 会话信息
BOOL GetSSLSessionInfo([dwConnID, ]enInfo, lppInfo)
SSL Server 和 Agent 组件需要 dwConnID 参数指定目标连接; 参数 enInfo 指定需要获取的信息类型;如果获取成功返回 TRUE,同时 lppInfo 参数保存获取到的信息值。获取失败返回 FALSE, 可通过 SYS_GetLastError()
获取错误代码。(参数 enInfo 详细说明请参考include/hpsocket/HPTypeDef.h 头文件)
2.7 清理 SSL 环境
void CleanupSSLContext()
组件停止通信(调用 Stop()
)时会自动清理 SSL 环境,因此,应用程序只需调用SetupSSLContext()
初始化组件的 SSL 环境参数,而不需要手工调用本函数。
2.8 清理线程局部 SSL 环境资源(全局函数)
void HP_SSL_RemoveThreadLocalState()
任何一个操作 SSL 的线程,在退出时都需要清理线程的局部环境 SSL 资源, 主线程和HP-Socket 工作线程在通信结束时会自动清理线程局部环境 SSL 资源。因此,一般情况下不必手工调用本函数; 特殊情况下,当自定义线程参与 HP-Socket 通信操作(如:通信组件发送策略为 SP_DIRECT 并且该自定义线程调用了 Send() 方法发送数据) 并检查到 SSL 内存泄漏时,需在每次停止组件时在该自定义线程调用函数。
三、SSL握手
SSL组件(包括Https组件)默认情况下在建立连接后会立刻开始SSL握手(OnHandShake
事件会紧接着 OnConnect / OnAccept
事件触发)。 但在某些场景下需要在启动 SSL 通信前执行一些前置操作,例如:通过代理服务器与目标服务器通信就是其中一个典型场景。在此场景中,必须与代理服务器建立连接后才能开始 SSL 通信。
HP-Socket v5.4.2 版本开始,提供了对手工启动 SSL 握手的支持。调用 SSL 组件的SetSSLAutoHandShake(FALSE)
方法把组件设置为手工握手模式,在执行完前置操作后调用StartSSLHandShake()
启动 SSL 通信。
3.1 手工启动SSL握手
BOOL StartSSLHandShake(dwConnID)