iOS 中对 HTTPS 证书链的验证 下载本文

内容发布更新时间 : 2024/5/22 0:40:36星期一 下面是文章的全部内容请认真阅读。

打印 policiesRef 后,你会发现默认的验证策略就包含了域名验证,即“服务器证书上的域名和请求域名是否匹配”。如果你的一个证书需要用来连接不同域名的主机,或者你直接用 IP 地址去连接,那么你可以重设验证策略以忽略域名验证:

NSMutableArray *policies = [NSMutableArray array]; // BasicX509 不验证域名是否相同

SecPolicyRef policy = SecPolicyCreateBasicX509(); [policies addObject:(__bridge_transfer id)policy]; SecTrustSetPolicies(trust, (__bridge CFArrayRef)policies); 然后再调用 serverTrustIsVaild() 验证。

但是如果不验证域名的话,安全性就会大打折扣。拿浏览器举

试想你要传输报文到 https://www.real-website.com ,然而由于域名劫持,把你带到了 https://www.real-website.cn 这个 1.

这个伪造网站的证书是非 CA 颁布的伪造证书的话,那么浏览器会提醒你这个证书不可信; 2.

这个伪造网站也使用了 CA 颁布的证书,由于我们不做域名验证,你的浏览器不会有任何的警告。

你可能会问:公钥证书是每个人都能得到的,钓鱼网站能不能返回真正的公钥证书给我们呢? 我觉得是可以的,然而这并没有什么卵用。没有私钥的钓鱼服务器无法获得第三个随机数,无法生成 Session Key,也就不能对我们传给它的数据进行解密了。 自签名的证书链验证

在 App 中想要防止上面提到的中间人攻击,比较好的做法是将公钥证书打包进 App 中,然后在收到服务端证书链的时候,能够有效地验证服务端是否可信,这也是验证自签名的证书链所必须做的。

假设你的服务器返回:[你的自签名的根证书] -- [你的二级证书] -- [你的客户端证书],系统是不信任这个三个证书的。

所以你在验证的时候需要将这三个的其中一个设置为锚点证书,当然,多个也行。

比如将 [你的二级证书] 作为锚点后,SecTrustEvaluate() 函数只要验证到 [你的客户端证书] 确实是由 [你的二级证书] 签署的,那么验证结果为 kSecTrustResultUnspecified,表明了 [你的客户端证书] 是可信的。下面是设置锚点证书的做法:

NSMutableArray *certificates = [NSMutableArray array];

NSDate *cerData = /* 在 App Bundle 中你用来做锚点的证书数据,证书是 CER 编码的,常见扩展名有:cer, crt...*/

SecCertificateRef cerRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)cerData);

[certificates addObject:(__bridge_transfer id)cerRef]; // 设置锚点证书。

SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)certificates);

只调用 SecTrustSetAnchorCertificates () 这个函数的话,那么就只有作为参数被传入的证书作为锚点证书,连系统本身信任的 CA 证书不能作为锚点验证证书链。要想恢复系统中 CA 证书作为锚点的功能,还要再调用下面这个函数: // true 代表仅被传入的证书作为锚点,false 允许系统 CA 证书也作为锚点 SecTrustSetAnchorCertificatesOnly(trust, false);

这样,再调用 serverTrustIsVaild() 验证证书有效性就能成功了。 CA 证书链的验证

上面说的是没经过 CA 认证的自签证书的验证,而 CA 的证书链的验证方式也是一样,不同点在不可信锚点的证书类型不一样而已:前者的锚点是自签的需要被打包进 App 用于验证,后者的锚点可能本来就存在系统之中了。不过我脑补了这么的一个坑:

假如我们使用的是 CA 根证书签署的数字证书,而且只用这个 CA 根证书作为锚点,在不验证域名的情况下,是不是就会在握手阶段信任被同一个 CA 根证书签名的伪造证书呢?