前言
一.單向認(rèn)證
Https在建立Socket連接之前,需要進(jìn)行握手把敢。
1.客戶端向服務(wù)端發(fā)送SSL協(xié)議版本號(hào)、加密算法種類谅辣、隨機(jī)數(shù)等信息修赞。
2.服務(wù)端給客戶端返回SSL協(xié)議版本號(hào)、加密算法種類屈藐、隨機(jī)數(shù)等信息榔组,同時(shí)也返回服務(wù)器端的證書,即公鑰證書
3.客戶端使用服務(wù)端返回的信息驗(yàn)證服務(wù)器的合法性联逻,包括:
證書是否過(guò)期
發(fā)型服務(wù)器證書的CA是否可靠
返回的公鑰是否能正確解開返回證書中的數(shù)字簽名
服務(wù)器證書上的域名是否和服務(wù)器的實(shí)際域名相匹配
驗(yàn)證通過(guò)后搓扯,將繼續(xù)進(jìn)行通信,否則包归,終止通信
4.客戶端向服務(wù)端發(fā)送自己所能支持的對(duì)稱加密方案锨推,供服務(wù)器端進(jìn)行選擇
5.服務(wù)器端在客戶端提供的加密方案中選擇加密程度最高的加密方式。
6.服務(wù)器將選擇好的加密方案通過(guò)明文方式返回給客戶端
7.客戶端接收到服務(wù)端返回的加密方式后公壤,使用該加密方式生成產(chǎn)生隨機(jī)碼换可,用作通信過(guò)程中對(duì)稱加密的密鑰,使用服務(wù)端返回的公鑰進(jìn)行加密厦幅,將加密后的隨機(jī)碼發(fā)送至服務(wù)器
8.服務(wù)器收到客戶端返回的加密信息后沾鳄,使用自己的私鑰進(jìn)行解密,獲取對(duì)稱加密密鑰确憨。
9.在接下來(lái)的會(huì)話中译荞,服務(wù)器和客戶端將會(huì)使用該密碼進(jìn)行對(duì)稱加密瓤的,保證通信過(guò)程中信息的安全。
//AF加上這句和下面的方法
_manager.securityPolicy = [self customSecurityPolicy];
/**** SSL Pinning ****/
- (AFSecurityPolicy*)customSecurityPolicy {
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"server" ofType:@"cer"];
NSData *certData = [NSData dataWithContentsOfFile:cerPath];
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];//這個(gè)mode不行的話可以換吞歼,看枚舉
[securityPolicy setAllowInvalidCertificates:YES];
NSSet *set = [NSSet setWithObjects:certData, nil];
[securityPolicy setPinnedCertificates:@[certData]];
/**** SSL Pinning ****/
return securityPolicy;
}
二.雙向認(rèn)證
雙向認(rèn)證和單向認(rèn)證原理基本差不多圈膏,只是除了客戶端需要認(rèn)證服務(wù)端以外,增加了服務(wù)端對(duì)客戶端的認(rèn)證篙骡。
溫馨提示:
證書需要提前準(zhǔn)備好分別是.cer的 和 .p12 的稽坤,別的就很簡(jiǎn)單了。
示例代碼如下:
在UIAHttps.h中
//
// UIAHttps.h
// UIADemo
//
// Created by gupeng on 17/02/22.
// Copyright ? 2017年 gupeng. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIAHttps : NSObject
+ (UIAHttps *)shared;
- (void)POST:(NSString *)urlString
Dictionary:(NSDictionary *)dic
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
@end
NS_ASSUME_NONNULL_END
在UIAHttps.m中
//
// UIAHttps.m
// UIADemo
//
// Created by gupeng on 17/02/22.
// Copyright ? 2017年 gupeng. All rights reserved.
//
#import "UIAHttps.h"
#import <AFNetworking.h>
@implementation UIAHttps
static UIAHttps *https = nil;
+ (UIAHttps *)shared {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if(https == nil) {
https = [[UIAHttps alloc] init];
}
});
return https;
}
- (void)POST:(NSString *)urlString
Dictionary:(NSDictionary *)dic
progress:(void (^)(NSProgress * _Nonnull))uploadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 設(shè)置超時(shí)時(shí)間
[manager.requestSerializer willChangeValueForKey:@"timeoutInterval"];
manager.requestSerializer.timeoutInterval = 30.f;
[manager.requestSerializer didChangeValueForKey:@"timeoutInterval"];
[manager.requestSerializer setValue:@"Content-Type" forHTTPHeaderField:@"application/json; charset=utf-8"];
[manager setSecurityPolicy:[self customSecurityPolicy]];
[self checkCredential:manager];
NSData *data = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:nil];
NSString *jsonString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
[manager POST:urlString parameters:jsonString progress:uploadProgress success:success failure:failure];
}
- (AFSecurityPolicy*)customSecurityPolicy {
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
//tomcat1(1)
NSString * cerPath = [[NSBundle mainBundle] pathForResource:@"server" ofType:@"cer"];
NSData *certData = [NSData dataWithContentsOfFile:cerPath];
NSSet *dataSet = [NSSet setWithArray:@[certData]];
[securityPolicy setAllowInvalidCertificates:YES];
[securityPolicy setPinnedCertificates:dataSet];
[securityPolicy setValidatesDomainName:YES];
return securityPolicy;
}
//校驗(yàn)證書
- (void)checkCredential:(AFURLSessionManager *)manager
{
[manager setSessionDidBecomeInvalidBlock:^(NSURLSession * _Nonnull session, NSError * _Nonnull error) {
}];
__weak typeof(manager)weakManager = manager;
[manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession*session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing*_credential) {
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__autoreleasing NSURLCredential *credential =nil;
NSLog(@"authenticationMethod=%@",challenge.protectionSpace.authenticationMethod);
//判斷是核驗(yàn)客戶端證書還是服務(wù)器證書
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
// 基于客戶端的安全策略來(lái)決定是否信任該服務(wù)器糯俗,不信任的話尿褪,也就沒必要響應(yīng)挑戰(zhàn)
if([weakManager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
// 創(chuàng)建挑戰(zhàn)證書(注:挑戰(zhàn)方式為UseCredential和PerformDefaultHandling都需要新建挑戰(zhàn)證書)
NSLog(@"serverTrust=%@",challenge.protectionSpace.serverTrust);
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
// 確定挑戰(zhàn)的方式
if (credential) {
//證書挑戰(zhàn) 設(shè)計(jì)policy,none,則跑到這里
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
// client authentication
SecIdentityRef identity = NULL;
SecTrustRef trust = NULL;
NSString *p12 = [[NSBundle mainBundle] pathForResource:@"app"ofType:@"p12"];
NSFileManager *fileManager =[NSFileManager defaultManager];
if(![fileManager fileExistsAtPath:p12])
{
NSLog(@"client.p12:not exist");
}
else
{
NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
if ([self extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data])
{
SecCertificateRef certificate = NULL;
SecIdentityCopyCertificate(identity, &certificate);
const void*certs[] = {certificate};
CFArrayRef certArray =CFArrayCreate(kCFAllocatorDefault, certs,1,NULL);
credential =[NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
disposition =NSURLSessionAuthChallengeUseCredential;
}
}
}
*_credential = credential;
return disposition;
}];
}
//讀取p12文件中的密碼
- (BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {
OSStatus securityError = errSecSuccess;
//client certificate password
NSDictionary *optionsDictionary = [NSDictionary dictionaryWithObject:@"123456"
forKey:(__bridge id)kSecImportExportPassphrase];
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
securityError = SecPKCS12Import((__bridge CFDataRef)inPKCS12Data,(__bridge CFDictionaryRef)optionsDictionary,&items);
if(securityError == 0) {
CFDictionaryRef myIdentityAndTrust =CFArrayGetValueAtIndex(items,0);
const void*tempIdentity =NULL;
tempIdentity= CFDictionaryGetValue (myIdentityAndTrust,kSecImportItemIdentity);
*outIdentity = (SecIdentityRef)tempIdentity;
const void*tempTrust =NULL;
tempTrust = CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemTrust);
*outTrust = (SecTrustRef)tempTrust;
} else {
NSLog(@"Failedwith error code %d",(int)securityError);
return NO;
}
return YES;
}
@end
在viewController.m中即可調(diào)用
// ViewController.m
// HttpsDemo
//
// Created by gupeng on 17/02/22.
// Copyright ? 2017年 gupeng. All rights reserved.
//
#import "ViewController.h"
#import "UIAHttps.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSDictionary *dic = @{
@"cardno":@"411321199311083311",
@"mobile":@"15726618328",
@"msgtype":@"0002",
@"name":@"小張",
@"password":@"FCE98C9B0907B94978AF97358F9FA442",
@"signature":@"B1E0507EA0C356719E0D5F900AAB9B0D85D3C62F2881C89777A0F7894C57DE5FB1C6B4D87E452D0053A9B0C69F87F4FA91B7B6ECE3ABAE816CF7EEADBBE5722C8F1BB01406C61073D9941E985CED39B01E95D141D3E81FBD285A410E52806601B9C0C8D470932FD71CA3C660CCEF14C57DCF089B877B7213B5DF2D7BA18488BA5B3743",
@"username":@"1001100112"
};
//此字典格式為我們公司 服務(wù)器需要的格式叶骨,只需按照自己的需求改動(dòng)即可
[[UIAHttps shared]POST:@"https://172.16.40.71:15025" Dictionary:dic progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"發(fā)送成功");
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"發(fā)送失敗");
}];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end