iOS开发API解读之SSL/TLS连接

(原创内容,转载请注明出处)

本文将通过创建SSL/TLS连接到认证的顺序来一一讲解SSL API的作用和实现。 (TLS是基于SSL的升级,提升了SSL算法,也可以理解为TLS1.0 = SSL 3.0,下文中通用SSL来称呼SSL/TLS)。SSL原理网上一大堆,自行Google。

创建SSL上下文

有SSL连接就需要SSLContext会话上下文来做基石。一个上下文不能重复用于多个会话。

创建

SSLCreateContext(CFAllocatorRef __nullable alloc, SSLProtocolSide protocolSide, SSLConnectionType connectionType)

我们来看这个函数每个参数的具体意义:

CFAllocatorRef用于标识初始化方式。

SSLProtocolSide用来标识这个SSL协议用于服务端还是客户端。

SSLConnectionType用来标识该上下文是基于流还是基于数据的通信,kSSLStreamType用于TCP通信,kSSLDatagramType用于UDP通信。

创建好SSLContext之后,需要设置它的IO数据输入输出函数,此函数也属于配置SSL Session:

SSLSetIOFuncs (SSLContextRef context, SSLReadFunc readFunc, SSLWriteFunc writeFunc)

一个输入输出的回调函数应该是这个样子:

(*SSLReadFunc)(SSLConnectionRef connection, void *data, size_t *dataLength);

其中connection一般用来传递当前使用SSLContext的对象,此函数收到数据的时候,便能通过connection桥接的self来把数据发送给self。

关联

怎么设置connection?这样:

SSLSetConnection(sslContext, (__bridge SSLConnectionRef)self);

设置SSL相关参数

配置SSL Session

初始化和关联处理数据的对象关联起来之后,就该为SSL设置一些常用参数了!下面这个方法就是给SSL填充参数选项了:

SSLSetSessionOption(SSLContextRef context, SSLSessionOption option, Boolean value)

我们来看看SSLSessionOption都有些什么内容!这些选项决定了SSLHandShake什么时候会返回一个状态值。所以,这些参数是很重要的!

typedef CF_ENUM(int, SSLSessionOption) {

// 服务器端认证客户端完成(OSStatus = errSSLServerAuthCompleted),客户端此时可以通过手动验证服务端证书来决定是否继续SSL连接

//如果需要手动验证服务器证书,就把这个选项设置为YES

kSSLSessionOptionBreakOnServerAuth = 0,

// 服务端要求客户端提供证书(OSStatus = errSSLClientCertRequested)

kSSLSessionOptionBreakOnCertRequested = 1,

//客户端已经提供证书,是否允许服务端对其进行验证

kSSLSessionOptionBreakOnClientAuth = 2,

//如果开启此选项,在协商好充足的密码套件(cipher-suite)的前提下,才会开始一个非正确的SSL连接

kSSLSessionOptionFalseStart = 3,

//是否启动应对BEAST攻击的(1/n-1)记录拆分,开启后,TLS1.0会对cipher执行记录拆分

kSSLSessionOptionSendOneByteRecord = 4,

//在SSL连接进行重新协商的时候,是否允许服务器标识发生改变。为了避免三重握手攻击,默认是禁止的

kSSLSessionOptionAllowServerIdentityChange = 5,

//开启此选项后,SSL连接将使用低版本协议进行连接失败后的重试

kSSLSessionOptionFallback = 6,

//握手第一步(clientHello)消息时进行打断以便检查SNI(Service Node Interface:不同域名使用不同证书),该选项一般用在服务端

kSSLSessionOptionBreakOnClientHello = 7,

//是否重新协商密码套件,默认是不允许的

kSSLSessionOptionAllowRenegotiation = 8,

};

以上选项会决定SSLHandShake什么时候会返回对应握手步骤的状态值,光是有这些,是不够的,我们还需要设置一些别的参数!Let‘s go!

SLSetClientSideAuthenticate(SSLContextRef context, SSLAuthenticate auth);

这个函数用在服务端(非必须),用于设定客户端验证标识。默认值是kNeverAuthenticate,既默认不需要客户端提供证书。

SSLSetProtocolVersionMin(SSLContextRef context, SSLProtocol minVersion)

SSLSetProtocolVersionMin函数用来设定支持的SSL协议最小版本

SSLSetProtocolVersionMax(SSLContextRef context, SSLProtocol maxVersion)

这个函数用来设定支持的SSL协议最大版本。 设置最大或者最小的协议版本号,合法的版本号有这么几个:kSSLProtocol3(SSL v3.0)、kTLSProtocol1(TLS v1.0)、kTLSProtocol11(TLS v1.1)、kTLSProtocol12(TLS v1.2)以及kDTLSProtocol1(DTLS v1.0)

SSLSetPeerID(SSLContextRef context, const void *peerID, size_t peerIDLen);

SSLSetPeerID设置一个c字符串的data来作为当前会话的唯一标识符,比如IP地址+端口,在SSLHandShake握手之前是可选设置的。但是要想成功恢复某个会话时,这个设置则是必须的,尝试恢复SSL会话的时候会检查上一个会话是否使用的是相同的peerID。

SSLSetPeerDomainName(SSLContextRef context, const char *peerName, size_t peerNameLen);

SSLSetPeerDomainName这个函数用来验证另一端的证书通用名称字段,比如可以设置成“store.apple.com”,如果证书中的通用名称与设置的PeerDomainName不匹配时,则会握手失败,返回errSSLXCertChainInvalid状态值。此功能是可选的,并且只能在会话没有开始时设置。(苹果官方很推荐设置这个参数~)

SSLSetEnabledCiphers(SSLContextRef context,const SSLCipherSuite *ciphers, size_t numCiphers)

SetEnabledCiphers用来指定SSL会话使用的密码套件,会话活跃的时候是不能设置的!如果要查询会话正在使用的密码套件,可以调用函数SSLGetEnabledCiphers来查询到。

SSLSetCertificate(SSLContextRef context, CFArrayRef certRefs);

SSLSetCertificate这个函数很重要!!!设置客户端证书是可选的,如果服务端要求客户端提供证书,则必须调用此函数设置好证书,设置证书的函数必须在SSLHandShake握手开始前或者返回errSSLClientCertRequested后调用。这个证书会在当前会话有效!certRefs数组必须在[0]位中放置一个SecIdentityRef对象,该对象标识叶证书及其相应的私钥。

还有很多很多的配置函数,不便一一列出。具体可以查看和官方文档。

接下来就是重头戏了!

握手

都配置好了,就需要执行SSL握手了!执行握手的方法如下:

OSStatus SSLHandshake(SSLContextRef context)

结果将会返回给OSStatus!一些比较常见的状态值有这些:

errSSLUnknownRootCert(对方证书链的根证书未知)

errSSLCertExpired(证书过期)

errSSLXCertChainInvalid(另一端的证书链无效或者没有证书)

errSSLClientCertRequested:(服务器需要客户端提供证书,客户端可以选择检查服务器的证书和可分辨名称列表,然后根据验证结果来决定是否继续握手。如果之前没有设置本地证书,则调用SSLSetCertificate设置证书,然后再次调用SSLHandshake来恢复握手)

errSSLWouldBlock的返回值表示SSLHandshake必须再次调用(并且一次又一次地,直到返回其他的东西)

握手过程中,如果要手动验证另一端证书,在对方已经发送证书的前提下可以通过SSLCopyPeerTrustSSLCopyPeerCertificates来得到对方的证书了! 验证完对方证书并且通过后,再次调用SSLHandShake()来继续刚才的握手。 当SSLHandShake返回noErr时,就是握手成功完成啦!

读写

握手成功以后,就可以通过

SSLWrite(SSLContextRef context, const void * _nullable data, sizet dataLength, size_t*processed)

来进行数据写入了。 数据读取则是这个方法:

SSLRead(SSLContextRef context, const void * _nullable data, sizet dataLength, size_t *processed)

还有获取缓存的数据等方法,已经不属于建立SSL连接的范畴,此处不再赘述。 比如:

SSLGetBufferedReadSize

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容