HTTPS连接过程及证书自定义认证
前言
-
本文主要是为了afnetworking下配置https证书验证。
-
最近公司另一个APP要做SSL自定义验证,参考了另一个APP已由其他同事写好的代码,顺便总结一下相关知识。
-
参考1:http://blog.csdn.net/dr19901106/article/details/50294393
-
参考2:https://www.cnblogs.com/jyking/p/6737295.html
原理
HTTPS 简述
-
HTTPS是运行在 TLS/SSL 之上的 HTTP,与普通的 HTTP 相比,在数据传输的安全性上有很大的提升。要了解它安全性的巧妙之处,需要先简单地了解对称加密和非对称加密的区别!
-
对称加密只有一个密钥,加密和解密都用这个密钥;
-
非对称加密有公钥和私钥,私钥加密后的内容只有公钥才能解密,公钥加密的内容只有私钥才能解密。
-
为了提高安全性,我们常用的做法是使用对称加密的手段加密数据。可是只使用对称加密的话,双方通信的开始总会以明文的方式传输密钥。那么从一开始这个密钥就泄露了,谈不上什么安全。所以 TLS/SSL 在握手的阶段,结合非对称加密的手段,保证只有通信双方才知道对称加密的密钥。大概的流程如下:
数字证书的内容
X.509 应该是比较流行的 SSL 数字证书标准,包含(但不限于)以下的字段:
字段 | 值说明 |
---|---|
对象名称(Subject Name) | 用于识别该数字证书的信息 |
共有名称(Common Name) | 对于客户证书,通常是相应的域名 |
证书颁发者(Issuer Name) | 发布并签署该证书的实体的信息 |
签名算法(Signature Algorithm) | 签名所使用的算法 |
序列号(Serial Number) | 数字证书机构(Certificate Authority, CA)给证书的唯一整数,一个数字证书一个序列号 |
生效期(Not Valid Before) | (`・ω・´) |
失效期(Not Valid After) | (╯°口°)╯(┴—┴ |
公钥(Public Key) | 可公开的密钥 |
签名(Signature) | 通过签名算法计算证书内容后得到的数据,用于验证证书是否被篡改 |
数字证书的生成及验证
-
数字证书的生成是分层级的,下一级的证书需要其上一级证书的私钥签名。 所以后者是前者的证书颁发者,也就是说上一级证书的 Subject Name 是其下一级证书的 Issuer Name。
-
在得到证书申请者的一些必要信息(对象名称,公钥私钥)之后,证书颁发者通过 SHA-256 哈希得到证书内容的摘要,再用自己的私钥给这份摘要加密,得到数字签名。综合已有的信息,生成分别包含公钥和私钥的两个证书。
使用
使用原理简述
- 先从服务器获取证书,然后分级验证证书,字符串内容是否相等。最后通过afnetworking的AFHTTPSessionManager的block方法校验后,返回是否通过验证。
- (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
- 最终返回状态
数值 | 中文说明 |
---|---|
NSURLSessionAuthChallengeUseCredential = 0, | 使用证书 |
NSURLSessionAuthChallengePerformDefaultHandling = 1, | 忽略证书(默认的处理方式) |
NSURLSessionAuthChallengeCancelAuthenticationChallenge = 2, | 忽略书证, 并取消这次请求 |
NSURLSessionAuthChallengeRejectProtectionSpace = 3, | 拒绝当前这一次, 下一次再询问 |
- 注意,对于afnetworking库,要使用自认证,必须要设置如下选项。
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
securityPolicy.allowInvalidCertificates = NO;//是否允许使用自签名证书
securityPolicy.validatesDomainName = YES;//是否需要验证域名,默认YES
[manager setSecurityPolicy:securityPolicy];
代码
+ (AFHTTPSessionManager *)addHeaderForSignWithRequestURL:(NSString *)requestURL mathod:(NSString *)method needToDecrypt:(BOOL)needToDecrypt {
static AFHTTPSessionManager *manager = nil;
static dispatch_once_t oneToken;
dispatch_once(&oneToken, ^{
manager = [[AFHTTPSessionManager manager] initWithBaseURL:[NSURL URLWithString:OnLineURL]];
[manager.responseSerializer setAcceptableContentTypes:[NSSet setWithObjects:
TTVNetWork_AcceptableContentType_ApplicationJson,TTVNetWork_AcceptableContentType_TextHtml,
TTVNetWork_AcceptableContentType_TextJvascript,TTVNetWork_AcceptableContentType_TextJson,
TTVNetWork_AcceptableContentType_TextPlain,TTVNetWork_AcceptableContentType_urlencoded,nil]];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
// 设置超时时间
[manager.requestSerializer willChangeValueForKey:TTVNewWork_timeoutInterValKey];
manager.requestSerializer.timeoutInterval = TTVNetWork_TimeoutInterval;
[manager.requestSerializer didChangeValueForKey:TTVNewWork_timeoutInterValKey];
// DeviceID 0.
[manager.requestSerializer setValue:NetworkOptionShareIntance.prefixDeviceID forHTTPHeaderField:TTVNetWork_DeviceIDKey];
// // SSL Pinning
// [manager setSecurityPolicy:[self customSecurityPolicy]];
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
securityPolicy.allowInvalidCertificates = NO;//是否允许使用自签名证书
securityPolicy.validatesDomainName = YES;//是否需要验证域名,默认YES
[manager setSecurityPolicy:securityPolicy];
// 自定义验证证书
[manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing *_credential)
{
BOOL isServerTrust = NO;//所有证书验证通过
BOOL isCertRootTrust = NO;//根证书验证通过
BOOL isCertSecondTrust = NO;//二级证书验证通过
BOOL isCertClientTrust = NO;//最后一级,本地证书验证通过
//取得服务器返回的三级证书
SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
//遍历服务器返回的证书
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
for (CFIndex i = certificateCount - 1; i >= 0; i--) {
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
NSString *subjectSummary = (__bridge_transfer NSString *)SecCertificateCopySubjectSummary(certificate);
//证书NSData转为NSString
CFDataRef certData = SecCertificateCopyData(certificate);
NSString *certificateBase64String = [(__bridge_transfer NSData *)certData base64EncodedStringWithOptions:0];
//比较本地保存的一级和二级证书NSString
if (i == certificateCount - 1) {//一级根证书
if ([certificateBase64String isEqualToString:certStringForRoot]) {
isCertRootTrust = YES;
}
} else if (i == certificateCount - 2) {//第二级证书
if ([certificateBase64String isEqualToString:certStringForSecondLevel]) {
isCertSecondTrust = YES;
}
} else if (i == 0) {//第三级:要部分验证的本地证书
// //服务器证书的颁发机构序列号
// CFDataRef issuerSequenceData = SecCertificateCopyNormalizedIssuerSequence(certificate);//iOS 10.3 后,才能使用此方法
// NSString *issuerSequenceBase64String = [(__bridge_transfer NSData *)issuerSequenceData base64EncodedStringWithOptions:0];
//获取本地证书
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"itouchtv_app" ofType:@"cer"];
NSData *certData = [NSData dataWithContentsOfFile:cerPath];
SecCertificateRef certificateLocal = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData);
//域名
NSString *subjectSummaryLocal = (__bridge_transfer NSString *)SecCertificateCopySubjectSummary(certificateLocal);
// //颁发机构序列号
// CFDataRef issuerSequenceDataLocal = SecCertificateCopyNormalizedIssuerSequence(certificateLocal);
// NSString *issuerSequenceBase64StringLocal = [(__bridge_transfer NSData *)issuerSequenceDataLocal base64EncodedStringWithOptions:0];
//验证
// if ([subjectSummary isEqualToString:subjectSummaryLocal]
// && [issuerSequenceBase64String isEqualToString:issuerSequenceBase64StringLocal])
// {
// isCertClientTrust = YES;
// }
if ([subjectSummary isEqualToString:subjectSummaryLocal])
{
isCertClientTrust = YES;
}
}
}
DLog(@"一级根证书是否验证通过:%@,第二级证书是否验证通过:%@,第三级本地证书是否验证通过:%@", @(isCertRootTrust), @(isCertSecondTrust), @(isCertClientTrust));
//判断所有证书是否验证通过
if (isCertRootTrust && isCertSecondTrust && isCertClientTrust) {
isServerTrust = YES;
}
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__autoreleasing NSURLCredential *credential = nil;
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
// if ([manager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
if (isServerTrust) {//使用自己的判断逻辑
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
return disposition;
}];
[TTVPlayerMoniter sharedMointerManager];
});
// USERID & Authorization
if ([TTVUserInfo sharedTTVUserInfo].isLogin) {//1.
[manager.requestSerializer setValue:[TTVUserInfo sharedTTVUserInfo].currentUser.userId forHTTPHeaderField:TTVNetWork_UserIDKey];
NSString *authorization = [NSString stringWithFormat:@"Bearer %@",[TTVUserInfo sharedTTVUserInfo].currentUser.jwt];//2.
[manager.requestSerializer setValue:authorization forHTTPHeaderField:TTVNetWork_AuthorizationKey];
}else {
[manager.requestSerializer setValue:NetworkOptionShareIntance.prefixDeviceID forHTTPHeaderField:TTVNetWork_DeviceIDKey];
[manager.requestSerializer setValue:nil forHTTPHeaderField:TTVNetWork_UserIDKey];
[manager.requestSerializer setValue:nil forHTTPHeaderField:TTVNetWork_AuthorizationKey];
}
return manager;
}