iOS用自签名证书实现HTTPS请求

HTTPS:

简单来说,HTTPS就是HTTP协议上再加一层加密处理的SSL协议,即HTTP安全版。相比HTTP,HTTPS可以保证内容在传输过程中不会被第三方查看、及时发现被第三方篡改的传输内容、防止身份冒充,从而更有效的保证网络数据的安全。

HTTPS客户端与服务器交互过程:
1、 客户端第一次请求时,服务器会返回一个包含公钥的数字证书给客户端;
2、 客户端生成对称加密密钥并用其得到的公钥对其加密后返回给服务器;
3、 服务器使用自己私钥对收到的加密数据解密,得到对称加密密钥并保存;
4、 然后双方通过对称加密的数据进行传输。


iOS用自签名证书实现HTTPS请求_第1张图片

IOS创建自签名https证书的步骤

注意:以下步骤仅用于配置内部使用或测试需要的SSL证书。如果想生成双向认证证书请参考:http://www.2cto.com/article/201411/347512.html里面证书的制作

iOS用自签名证书实现HTTPS请求_第2张图片

第1步:生成私钥

使用openssl工具生成一个RSA私钥


$ openssl genrsa -des3 -out server.key 2048

说明:生成rsa私钥,des3算法,2048位强度,server.key是秘钥文件名。


注意:生成私钥,需要提供一个至少4位的密码。


第2步:生成CSR(证书签名请求)

生成私钥之后,便可以创建csr文件了。


此时可以有两种选择。理想情况下,可以将证书发送给证书颁发机构(CA),CA验证过请求者的身份之后,会出具签名证书(很贵)。另外,如果只是内部或者测试需求,也可以使用OpenSSL实现自签名,具体操作如下:




$ openssl req -new -key server.key -out server.csr

说明:需要依次输入国家,地区,城市,组织,组织单位,Common Name和Email。其中Common Name,可以写自己的名字或者域名,如果要支持https,Common Name应该与域名保持一致,否则会引起浏览器警告。



Country Name (2 letter code) [AU]:CN

State or Province Name (full name) [Some-State]:Beijing

Locality Name (eg, city) []:Beijing

Organization Name (eg, company) [Internet Widgits Pty Ltd]:joyios

Organizational Unit Name (eg, section) []:info technology

Common Name (e.g. server FQDN or YOUR name) []:demo.joyios.com

Email Address []:liufan@joyios.com



第3步:删除私钥中的密码

在第1步创建私钥的过程中,由于必须要指定一个密码。而这个密码会带来一个副作用,那就是在每次Apache启动Web服务器时,都会要求输入密码,这显然非常不方便。要删除私钥中的密码,操作如下:



cp server.key server.key.org

openssl rsa -in server.key.org -out server.key



第4步:生成自签名证书

如果你不想花钱让CA签名,或者只是测试SSL的具体实现。那么,现在便可以着手生成一个自签名的证书了。


需要注意的是,在使用自签名的临时证书时,浏览器会提示证书的颁发机构是未知的。



$ openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

说明:crt上有证书持有人的信息,持有人的公钥,以及签署者的签名等信息。当用户安装了证书之后,便意味着信任了这份证书,同时拥有了其中的公钥。证书上会说明用途,例如服务器认证,客户端认证,或者签署其他证书。当系统收到一份新的证书的时候,证书会说明,是由谁签署的。如果这个签署者确实可以签署其他证书,并且收到证书上的签名和签署者的公钥可以对上的时候,系统就自动信任新的证书。


第5步:安装私钥和证书

将私钥和证书文件复制到Apache的配置目录下即可,在Mac 10.10系统中,复制到/etc/apache2/目录中即可。




第6步:客户端利用AF3.0使用自定义证书


复制代码

// 1.初始化单例类

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

manager.securityPolicy.SSLPinningMode = AFSSLPinningModeCertificate;

// 2.设置证书模式

NSString * cerPath = [[NSBundle mainBundle] pathForResource:@”xxx” ofType:@”cer”];

NSData * cerData = [NSData dataWithContentsOfFile:cerPath];

manager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:[[NSSet alloc] initWithObjects:cerData, nil]];

// 客户端是否信任非法证书

mgr.securityPolicy.allowInvalidCertificates = YES;

// 是否在证书域字段中验证域名

[mgr.securityPolicy setValidatesDomainName:NO];

复制代码

2.使用AFNetworking进行请求


AFNetworking首先需要配置AFSecurityPolicy类,AFSecurityPolicy类封装了证书校验的过程。


复制代码

/**

AFSecurityPolicy分三种验证模式:

AFSSLPinningModeNone:只是验证证书是否在信任列表中

AFSSLPinningModeCertificate:该模式会验证证书是否在信任列表中,然后再对比服务端证书和客户端证书是否一致

AFSSLPinningModePublicKey:只验证服务端证书与客户端证书的公钥是否一致

*/


AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];

securityPolicy.allowInvalidCertificates = YES;//是否允许使用自签名证书

securityPolicy.validatesDomainName = NO;//是否需要验证域名,默认YES


AFHTTPSessionManager *_manager = [AFHTTPSessionManager manager];

_manager.responseSerializer = [AFHTTPResponseSerializer serializer];

_manager.securityPolicy = securityPolicy;

//设置超时

[_manager.requestSerializer willChangeValueForKey:@”timeoutinterval”];

_manager.requestSerializer.timeoutInterval = 20.f;

[_manager.requestSerializer didChangeValueForKey:@”timeoutinterval”];

_manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringCacheData;

_manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@”application/xml”,@”text/xml”,@”text/plain”,@”application/json”,nil];


__weak typeof(self) weakSelf = self;

[_manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing *_credential) {


SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];

/**

* 导入多张CA证书

*/

NSString *cerPath = [[NSBundle mainBundle] pathForResource:@”ca” ofType:@”cer”];//自签名证书

NSData* caCert = [NSData dataWithContentsOfFile:cerPath];

NSArray *cerArray = @[caCert];

weakSelf.manager.securityPolicy.pinnedCertificates = cerArray;


SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCert);

NSCAssert(caRef != nil, @”caRef is nil”);


NSArray *caArray = @[(__bridge id)(caRef)];

NSCAssert(caArray != nil, @”caArray is nil”);


OSStatus status = SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)caArray);

SecTrustSetAnchorCertificatesOnly(serverTrust,NO);

NSCAssert(errSecSuccess == status, @”SecTrustSetAnchorCertificates failed”);


NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;

__autoreleasing NSURLCredential *credential = nil;

if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {

if ([weakSelf.manager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {

credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];

if (credential) {

disposition = NSURLSessionAuthChallengeUseCredential;

} else {

disposition = NSURLSessionAuthChallengePerformDefaultHandling;

}

} else {

disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;

}

} else {

disposition = NSURLSessionAuthChallengePerformDefaultHandling;

}


return disposition;

}];

复制代码

上述代码通过给AFHTTPSessionManager重新设置证书验证回调来自己验证证书,然后将自己的证书加入到可信任的证书列表中,即可通过证书的校验。