前言
通常SSL连接可以使用OpenSSL库来实现,但OpenSSL库体积比较大,完整动态库大小在2M左右。由于项目希望尽量控制iOS端应用包的大小,因此尝试使用iOS原生API实现SSL连接的功能。
另外,工程中已经存在异步网络IO的框架,因此希望直接在当前的IO框架上实现SSL功能,而不是再引入另一个IO框架。
总结
总的来说,iOS下实现SSL通信能力非常简单,iOS提供的API在使用上和OpenSSL有异曲同工之处,并且比OpenSSL绑定BIO的方法更加合理
主要API列表
所有API都通过头文件<Security/Security.h>引入
只需要数个API就可以实现SSL的读写能力,这里先全部列举出来
- SSLCreateContext 创建SSL上下文
- SSLSetIOFuncs 设置IO读写回调函数
- SSLSetConnection 设置回调函数中使用的自定义参数
- SSLHandshake 发起SSL握手过程
- SSLRead 读取数据
- SSLWrite 写入数据
初始化SSL上下文
创建SSL上下文句柄 ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType)
设置读写回调 SSLSetIOFuncs(ctx, SSLReadCallback, SSLWriteCallback)
设置其他选项,可以给SSL上下文设置其他额外选项,需要在调用SSLHandshake之前完成,具体可参考apple的文档。
SSL握手/数据发送/数据接收
完成SSL上下文的初始化以后就可以开始握手流程,握手、发送、接收的处理流程区别不大,本文以握手流程为例进行说明。
- 调用SSLHandshake方法,在返回前SSLReadCallback或SSLWriteCallback会被立即调用,SSL组件通过这两个回调获取相应的通信数据从而与实际的网络socket和网络框架解耦。
- SSLReadCallback会告诉我们SSL组件需要读取多少字节数据,如果我们可以立即从socket或缓冲区(取决于实现)返回相应大小的数据,那么返回noErr,否则返回errSSLWouldBlock。
- SSLWriteCallback会把发送数据的缓冲区与其大小通过参数传给我们,如果发送可以立即完成(比如成功写到了socket缓存中),那么返回noErr,否则返回errSSLWouldBlock。
- 当我们在回调函数中返回errSSLWouldBlock后,SSLHandshake也会返回errSSLWouldBlack。这时我们需要判断是读操作未完成或是写操作未完成,然后开始同步或异步的从socket中获取数据。
- 成功获取数据后,我们需要回到步骤1,再次调用SSLHandshake,直到SSLHandshake返回noErr或返回其他错误
使用伪代码说明流程
void handshake()
{
err = SSLHandshake();
if (err == noErr) {
//handshake successfully done
}
else if (err == errSSLWouldBlock) {
handlePendingIO(onFinished() {
handshake() //call SSLHandshake again
});
}
else {
handleError();
}
}
void handlePendingIO()
{
if (haveDataToWrite()) {
asyncWriteDataToSocket();
}
else (haveDataToRead()) {
asyncReadDataFromSocket();
}
else {
handleError();
}
}