前言
一.單向認(rèn)證
Https在建立Socket連接之前陨帆,需要進(jìn)行握手蛙酪。
1.客戶端向服務(wù)端發(fā)送SSL協(xié)議版本號、加密算法種類引镊、隨機(jī)數(shù)等信息。
2.服務(wù)端給客戶端返回SSL協(xié)議版本號吃嘿、加密算法種類祠乃、隨機(jī)數(shù)等信息,同時也返回服務(wù)器端的證書兑燥,即公鑰證書
3.客戶端使用服務(wù)端返回的信息驗證服務(wù)器的合法性,包括:
證書是否過期
發(fā)型服務(wù)器證書的CA是否可靠
返回的公鑰是否能正確解開返回證書中的數(shù)字簽名
服務(wù)器證書上的域名是否和服務(wù)器的實際域名相匹配
驗證通過后琴拧,將繼續(xù)進(jìn)行通信降瞳,否則,終止通信4.客戶端向服務(wù)端發(fā)送自己所能支持的對稱加密方案蚓胸,供服務(wù)器端進(jìn)行選擇
5.服務(wù)器端在客戶端提供的加密方案中選擇加密程度最高的加密方式挣饥。
6.服務(wù)器將選擇好的加密方案通過明文方式返回給客戶端
7.客戶端接收到服務(wù)端返回的加密方式后,使用該加密方式生成產(chǎn)生隨機(jī)碼沛膳,用作通信過程中對稱加密的密鑰扔枫,使用服務(wù)端返回的公鑰進(jìn)行加密,將加密后的隨機(jī)碼發(fā)送至服務(wù)器
8.服務(wù)器收到客戶端返回的加密信息后锹安,使用自己的私鑰進(jìn)行解密短荐,獲取對稱加密密鑰倚舀。
9.在接下來的會話中,服務(wù)器和客戶端將會使用該密碼進(jìn)行對稱加密忍宋,保證通信過程中信息的安全痕貌。
二.雙向認(rèn)證
雙向認(rèn)證和單向認(rèn)證原理基本差不多,只是除了客戶端需要認(rèn)證服務(wù)端以外糠排,增加了服務(wù)端對客戶端的認(rèn)證舵稠。
下面以一個簡單的例子說明:點此下載Demo
溫馨提示:
證書需要提前準(zhǔn)備好分別是.cer的 和 .p12 的,別的就很簡單了入宦。
示例代碼如下:
在UIAHttps.h中
//
// UIAHttps.h
// UIADemo
//
// Created by zhangchao on 16/12/13.
// Copyright ? 2016年 王龍. 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 zhangchao on 16/12/13.
// Copyright ? 2016年 王龍. 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è)置超時時間
[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;
}
//校驗證書
- (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);
//判斷是核驗客戶端證書還是服務(wù)器證書
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
// 基于客戶端的安全策略來決定是否信任該服務(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è)計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 王龍 on 16/12/15.
// Copyright ? 2016年 王龍. 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ù)器需要的格式落追,只需按照自己的需求改動即可
[[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