详解iOS开发 – 用AFNetworking实现https单向验证,双向验证

2025-05-29 0 49

自苹果宣布2017年1月1日开始强制使用https以来,htpps慢慢成为大家讨论的对象之一,不是说此前https没有出现,只是这一决策让得开发者始料未及,博主在15年的时候就做过https的接口,深知此坑之深,原因就是自身对这方面知识不了解加上网上的资料少,除此外还有博客不知对错就互相转载,导致当时网上几乎找不到能用的代码,这一点,博主说的毫不夸张。

鉴于此,博主一直想填一下这个坑,多增加一些正确的代码,来供广大开发者使用,后来一直被搁置,经过尝试后,博主现将整理好的代码发布在这里,希望能帮到焦急寻找的开发者。

1.先来说说老的afnetworking2.x怎么来实现的

博主在网上看过几篇帖子,其中说的一些方法是正确的,但是却并不全对,由于那几篇博客几乎一样,博主不能确定最早的那篇是谁写的,所以就重新在下面说明下方法:

1)倒入client.p12证书;

2)在plist文件做如图配置:

详解iOS开发 - 用AFNetworking实现https单向验证,双向验证

3)在afnetworking中修改一个类:

详解iOS开发 - 用AFNetworking实现https单向验证,双向验证

找到这个文件,在里面增加一个方法:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20
- (osstatus)extractidentity:(cfdataref)inp12data toidentity:(secidentityref*)identity {

osstatus securityerror = errsecsuccess;

cfstringref password = cfstr("证书密码");

const void *keys[] = { ksecimportexportpassphrase };

const void *values[] = { password };

cfdictionaryref options = cfdictionarycreate(null, keys, values, 1, null, null);

cfarrayref items = cfarraycreate(null, 0, 0, null);

securityerror = secpkcs12import(inp12data, options, &items);

if (securityerror == 0)

{

cfdictionaryref ident = cfarraygetvalueatindex(items,0);

const void *tempidentity = null;

tempidentity = cfdictionarygetvalue(ident, ksecimportitemidentity);

*identity = (secidentityref)tempidentity;

}

if (options) {

cfrelease(options);

}

return securityerror;

}

再修改一个方法:

用下面的这段代码替换nsurlconnectiondelegate中的同名代码,

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26
- (void)connection:(nsurlconnection *)connection

willsendrequestforauthenticationchallenge:(nsurlauthenticationchallenge *)challenge

{

nsstring *thepath = [[nsbundle mainbundle] pathforresource:@"client" oftype:@"p12"];

//倒入证书 nslog(@"thepath===========%@",thepath);

nsdata *pkcs12data = [[nsdata alloc] initwithcontentsoffile:thepath];

cfdataref inpkcs12data = (__bridge cfdataref)pkcs12data;

secidentityref identity = null;

// extract the ideneity from the certificate

[self extractidentity :inpkcs12data toidentity:&identity];

seccertificateref certificate = null;

secidentitycopycertificate (identity, &certificate);

const void *certs[] = {certificate};

// cfarrayref certarray = cfarraycreate(kcfallocatordefault, certs, 1, null);

// create a credential from the certificate and ideneity, then reply to the challenge with the credential

//nslog(@"identity=========%@",identity);

nsurlcredential *credential = [nsurlcredential credentialwithidentity:identity certificates:nil persistence:nsurlcredentialpersistencepermanent];

// credential = [nsurlcredential credentialwithidentity:identity certificates:(__bridge nsarray*)certarray persistence:nsurlcredentialpersistencepermanent];

[challenge.sender usecredential:credential forauthenticationchallenge:challenge];

}

4)发起请求

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16
nsstring *url = @"xxxxxxxxxx";

// 1.获得请求管理者

afhttprequestoperationmanager *mgr = [afhttprequestoperationmanager manager];

//2设置https 请求

afsecuritypolicy *securitypolicy = [afsecuritypolicy policywithpinningmode:afsslpinningmodecertificate];

securitypolicy.allowinvalidcertificates = yes;

mgr.securitypolicy = securitypolicy;

// 3.发送post请求

[mgr post:url parameters:nil success:^(afhttprequestoperation * _nonnull operation, id _nonnull responseobject) {

nslog(@"responseobject: %@", responseobject);

} failure:^(afhttprequestoperation * _nonnull operation, nserror * _nonnull error) {

nslog(@"error: %@", error);

}];

到此,老版的afnetworking请求https接口的双向验证就做完了,但是有一个问题,这里需要改动afnetworking的代码,何况新的afnetworking已经有了,为了保持代码的活力,老的应该摒弃的,而且更新pods后肯定替换的代码就没了,也是一个问题,不要急,下面来说说怎么用新的afnetworking,并解决被pods更新替换代码的问题。

最后再说一点,使用老的af来请求,只用到了client.p12文件,并没有用到server.cer,在新的里面是有用到的,猜测可能是客户端选择信任任何证书导致的,就变成了单向的验证。

demo放在最后

2.来说说新的afnetworking3.x怎么来实现的

1)倒入client.p12和server.cer文件

2)plist内的设置,这是和上面一样的:

详解iOS开发 - 用AFNetworking实现https单向验证,双向验证

3)这里可不需要修改类里面的代码,但是这里需要重写一个方法:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64
nsstring *url = @"https://test.niuniuhaoguanjia.com/3.0.0/?service=city.getcitylist";

nsstring *certfilepath = [[nsbundle mainbundle] pathforresource:@"server" oftype:@"cer"];

nsdata *certdata = [nsdata datawithcontentsoffile:certfilepath];

nsset *certset = [nsset setwithobject:certdata];

afsecuritypolicy *policy = [afsecuritypolicy policywithpinningmode:afsslpinningmodecertificate withpinnedcertificates:certset];

policy.allowinvalidcertificates = yes;

policy.validatesdomainname = no;

_manager = [afhttpsessionmanager manager];

_manager.securitypolicy = policy;

_manager.requestserializer = [afhttprequestserializer serializer];

_manager.responseserializer = [afhttpresponseserializer serializer];

_manager.responseserializer.acceptablecontenttypes = [nsset setwithobjects:@"application/json", @"text/json", @"text/javascript",@"text/plain", nil];

//关闭缓存避免干扰测试r

_manager.requestserializer.cachepolicy = nsurlrequestreloadignoringlocalcachedata;

[_manager setsessiondidbecomeinvalidblock:^(nsurlsession * _nonnull session, nserror * _nonnull error) {

nslog(@"setsessiondidbecomeinvalidblock");

}];

//客户端请求验证 重写 setsessiondidreceiveauthenticationchallengeblock 方法

__weak typeof(self)weakself = self;

[_manager setsessiondidreceiveauthenticationchallengeblock:^nsurlsessionauthchallengedisposition(nsurlsession*session, nsurlauthenticationchallenge *challenge, nsurlcredential *__autoreleasing*_credential) {

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 {

// client authentication

secidentityref identity = null;

sectrustref trust = null;

nsstring *p12 = [[nsbundle mainbundle] pathforresource:@"client"oftype:@"p12"];

nsfilemanager *filemanager =[nsfilemanager defaultmanager];

if(![filemanager fileexistsatpath:p12])

{

nslog(@"client.p12:not exist");

}

else

{

nsdata *pkcs12data = [nsdata datawithcontentsoffile:p12];

if ([[weakself class]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;

}];

4)发起请求

?

1

2

3

4

5

6

7

8

9

10

11

12

13
//第三步和这一步代码是放在一起的,请注意哦

[_manager get:url parameters:nil progress:^(nsprogress * _nonnull downloadprogress) {

} success:^(nsurlsessiondatatask * _nonnull task, id _nullable responseobject) {

nsdictionary *dic = [nsjsonserialization jsonobjectwithdata:responseobject options:nsjsonreadingmutablecontainers error:nil];

nslog(@"json: %@", dic);

} failure:^(nsurlsessiondatatask * _nullable task, nserror * _nonnull error) {

nslog(@"error: %@", error);

nsdata *data = [error.userinfo objectforkey:@"com.alamofire.serialization.response.error.data"];

nsstring *str = [[nsstring alloc]initwithdata:data encoding:nsutf8stringencoding];

nslog(@"%@",str);

}];

另外还要加上一个方法:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23
+(bool)extractidentity:(secidentityref*)outidentity andtrust:(sectrustref *)outtrust frompkcs12data:(nsdata *)inpkcs12data {

osstatus securityerror = errsecsuccess;

//client certificate password

nsdictionary*optionsdictionary = [nsdictionary dictionarywithobject:@"证书密码"

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;

}

没错,我们是要封装一下,可是要怎么封装呢?博主尝试了集中都失败了,真是百思不得解,相信主动去封装的开发者也会碰到封装后请求失败的问题,也许你成功了,但是这里需要注意一个在block内使用变量的问题,具体的可以去看博主怎么封装的。

到这里,新的af请求https就已经结束了,想看封装的,demo放在最后。

3.单向验证

说到这个,不得不说一下网上的很多方法,都把单向验证当作双向的,其实也是并不理解其原理,关于原理,请看这里
代码实现af都是一样的:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16
//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];

[securitypolicy setallowinvalidcertificates:yes];

nsset *set = [nsset setwithobjects:certdata, nil];

[securitypolicy setpinnedcertificates:@[certdata]];

/**** ssl pinning ****/

return securitypolicy;

}

4.demo下载福利

因为证书安全问题,demo 里的证书博主删除了,请见谅,请大家放入自己的证书。

老的af访问httpsdemo

新的af访问httpsdemo

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持快网idc。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 详解iOS开发 – 用AFNetworking实现https单向验证,双向验证 https://www.kuaiidc.com/91534.html

相关文章

发表评论
暂无评论