1.HTTPS传输流程
2.常用加密算法
3.AFN证书校验策略及核心方法
4.SSL Pinning
5.CA证书申请流程
HTTPS经由超文本传输协议进行通信,但利用SSL/TLS来对数据包进行加密。HTTPS开发的主要目的,是提供对网络服务器的身份认证,保护交换数据的隐私与完整性
1.HTTPS传输流程
由于不相信服务器生成的随机数,所以需要客户端生成pre-master-securet,然后再由服务端加密,最后得到master-securet(主密钥),建立密道通信。(减少被猜中的可能)
2.常用加密算法
非对称加密算法:RSA,DSA/DSS
对称加密算法:AES,RC4,3DES
HASH算法:MD5,SHA1,SHA256
非对称加密算法用于握手过程中的加密生成的密码,
对称加密算法用于对真正传输的数据进行加密,
HASH算法用于验证数据的完整性,是否被篡改等
非对称加密生成密码是整个加密过程的关键,非对称加密会生成公钥和私钥,
公钥负责数据加密,(可以随意传输)
私钥负责解密,(重中之重)
3.AFN证书校验策略及核心方法
NSURLSession 封装了HTTPS链接的建立加密解密功能,但并没有验证证书的合法性,无法避免中间人攻击,要真正的安全通讯,需要我们手动去验证证书,
客户端校验策略
sessionManager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
* AFSSLPinningModeNone 不做SSL pinning 只信任证书颁发机构证书,自己生成证书不通过
* AFSSLPinningModeCertificate 客户端保存证书拷贝 第一步验证证书的域名/有效期等信息,第二步是对比服务端返回的证书跟客户端返回的是否一致。
* AFSSLPinningModePublicKey 客户端保存证书拷贝 只是验证时只验证证书里的公钥,不验证证书的有效期等信息
核心证书校验方法
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString *)domain
{
**** AFSSLPinningModeNone 不做SSL Pinning 当需要使用证书验证域名时,需要使用AFSSLPinningModePublicKey或AFSSLPinningModeCertificate
if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
// According to the docs, you should only trust your provided certs for evaluation.
Pinned certificates are added to the trust. Without pinned certificates,
there is nothing to evaluate against.
From Apple Docs:
"Do not implicitly trust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors).
Instead, add your own (self-signed) CA certificate to the list of trusted anchors."
NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
return NO;
}
NSMutableArray *policies = [NSMutableArray array];
**** 需要验证域名时,添加一个域名验证策略
if (self.validatesDomainName) {
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
**** 设置验证策略
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
if (self.SSLPinningMode == AFSSLPinningModeNone) {
**** AFSSLPinningModeNone 时,allowInvalidCertificates为YES ,则代表服务器任何证书都能验证
**** 如果是NO,则需要判断此服务器是否是系统信任证书
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
} else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
**** 如果服务器证书不是系统信任证书,且不允许不信任证书通过验证则返回NO
return NO;
}
switch (self.SSLPinningMode) {
case AFSSLPinningModeNone:
default:
return NO;
case AFSSLPinningModeCertificate: {
**** AFSSLPinningModeCertificate 是直接将本地证书设置为信任的根证书,然后来进行判断,并且比较本地证书内容和服务器证书内容是否一致,如果有一个相同则返回YES
NSMutableArray *pinnedCertificates = [NSMutableArray array];
for (NSData *certificateData in self.pinnedCertificates) {
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
**** 设置本地证书为根证书
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
**** 通过本地证书来判断服务器证书是否可信,不可信则不通过
if (!AFServerTrustIsValid(serverTrust)) {
return NO;
}
// obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
**** 判断本地证书和服务器证书是否相同
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
return YES;
}
}
return NO;
}
case AFSSLPinningModePublicKey: {
NSUInteger trustedPublicKeyCount = 0;
NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
**** AFSSLPinningModePublicKey 是通过比较证书中公钥部分来进行校验。通过SecTrustCopyPublicKey方法获取本地证书和服务器证书,进行比较,如果有一个相同则验证通过
for (id trustChainPublicKey in publicKeys) {
for (id pinnedPublicKey in self.pinnedPublicKeys) {
if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
trustedPublicKeyCount += 1;
}
}
}
return trustedPublicKeyCount > 0;
}
}
return NO;
}
4.SSL Pinning
SSL Pinning
SSL Pinning 即证书绑定,客户端直接保存服务端证书,建立HTTPS连接时会校验服务端返回证书和客户端证书是否一致,一致则不再去信任证书机构里验证。
SSL Pinning 为什么安全
其实如果中间人从客户端取出证书(公钥),并使用证书冒充服务器与客户端进行通信时,也可以通过证书验证,但后续流程走不下去,因为客户端会用公钥加密,中间人从客户端截取的证书是公钥,缺少对应私钥即使截获了信息也无法解密。所以能够最大程度保护信息安全
PS: 从上面的通信过程中,最重要的是存储在服务器的私钥。因为只有私钥生成了在通信过程中传递的证书(公钥),且只有通过私钥才能对公钥加密的信息进行解密,所以在开发过程中保护好私钥的安全。
什么时候使用SSL Pinning
如果证书是从受信任的CA机构颁布的,验证是没有问题的,如果是自己颁发证书,无法通过系统受信任的CA机构列表验证证书时,需要通过SSL Pinnig的方式来验证
如何使用SSL Pinning
1.将.cer 证书放进工程中
2.设置securityPolicy证书校验策略
#pragma mark - SecurityPolicy
+ (void)securityPolicy:(AFHTTPSessionManager *)sessionManager{
sessionManager.securityPolicy = [AFSecurityPolicy defaultPolicy];
sessionManager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
/** allowInvalidCertificates 是否允许不信任的证书(证书无效、证书时间过期)通过验证 ,默认为NO */
[sessionManager.securityPolicy setAllowInvalidCertificates:NO];
/** validatesDomainName 是否验证域名证书的CN(common name)字段 默认YES*/
sessionManager.securityPolicy.validatesDomainName = YES;
/** NSSet<NSData*> *pinnedCertificates 根据验证模式来返回用于验证服务器的证书。*/
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"XXXX" ofType:@"cer"];
NSData *certData = [NSData dataWithContentsOfFile:cerPath];
[sessionManager.securityPolicy setPinnedCertificates:[NSSet setWithArray:@[certData]]];
}
iOS设备已有哪些CA证书
每个版本的iOS设备中,都会包含一些既有的CA根证书。如果接收到的证书是iOS信任的CA根证书签名的,那么则为合法证书;否则则为“非法”证书
iOS已有的CA根证书:https://support.apple.com/en-us/HT204132
5.CA证书申请流程
补充:SSL 与 TLS 协议
SSL协议(传输层安全协议)工作方式:客户端要收发几个握手信号:
1.发送一个“ClientHello”消息。内容包括支持的协议版本,比如TLS1.0版
2.收到一个“ServerHello”消息。内容包括支持的协议版本,
3.客户端与服务端交换证书。(依靠被选择的公钥系统)
4.服务端请求客户端公钥。客户端有证书即双向身份认证,没证书时随机生成公钥
5.客户端与服务端通过公钥保密协议共同的主私钥(伪随机数)
在SSL 3.0中发现设计缺陷后,SSL 被禁用,之后 ETF将SSL标准化,即 RFC 2246 ,并将其称为TLS(Transport Layer Security),即TLS 1.0
TLS 1.0包括可以降级到SSL 3.0的实现,这削弱了连接的安全性
TLS 1.1 添加对CBC攻击的保护:隐式IV被替换成一个显式的IV。更改分组密码模式中的填充错误。支持IANA登记的参数。
TLS 1.2 可使用密码组合选项指定伪随机函数使用SHA-256替换MD5-SHA-1组合。AES加密的支持
TLS 1.3 草案 2016年1月
相关链接:
http://www.jianshu.com/p/668b263befc8
https://runningyoung.github.io/2016/05/16/2016-05-16-HTTPS/