iOS Facebook/SocketRocket SSL雙向認(rèn)證處理

第一次寫簡(jiǎn)書,記錄一下最近開發(fā)的項(xiàng)目關(guān)于使用websocket遇到的問題。

因?yàn)楹笈_(tái)使用的是Websocket協(xié)議,所以我就在GitHub上面找到了用戶量比較多Objecttive-C的框架,F(xiàn)acebook開源的SocketRocket,具體Websocket與Socket的區(qū)別自行百度,網(wǎng)上資料還是比較多的。

(簡(jiǎn)單介紹:WebSocket同HTTP一樣也是應(yīng)用層的協(xié)議,但是它是一種雙向通信協(xié)議,是建立在TCP之上,Socket其實(shí)并不是一個(gè)協(xié)議,而是為了方便使用TCP或UDP而抽象出來的一層,是位于應(yīng)用層和傳輸控制層之間的一組接口)

此處省略一火車的文字...直接說處理方式,希望對(duì)跟我遇到一樣的問題的人有幫助。

從GitHub下載代碼,pod安裝代碼比較舊,所以下載后將SocketRocket文件夾手動(dòng)拖入工程。

需要添加 libicucore.A.tdb 庫。<方法:點(diǎn)擊工程--->Build Phases --->Link Binary With Libraries >

可能會(huì)有一些頭文件的報(bào)未找到文件的錯(cuò),將import <>改成import "" 即可。

一、證書準(zhǔn)備工作

證書需要:服務(wù)器提供三個(gè)證書文件分別為: CA根證書(必須是der編碼格式,后綴有cer、der等,可用OpenSSL做轉(zhuǎn)換)、公鑰、私鑰 ,其中<公鑰、私鑰 使用OpenSSL合并成一個(gè)p12文件,合并的時(shí)候會(huì)提示設(shè)置密碼,需記住密碼,代碼里面會(huì)使用>,下面是我的轉(zhuǎn)換方法,因?yàn)榉?wù)器給的是pem格式,所以具體格式具體自行搜索轉(zhuǎn)換方法:

假設(shè)后臺(tái)給的三個(gè)證書命名為以下:
根證書命名為:CAcert.pem
公鑰證書命名為:client-cert.pem
私鑰證書命名為:client-key.pem

1、將CA根證書pem格式轉(zhuǎn)成der格式:(以下命令都在終端中執(zhí)行,需要cd到存放證書的目錄下)

openssl x509 -inform PEM -outform DER -in CAcert.pem -out CAcert.der

2、將公鑰與私鑰合并成一個(gè) p12 ,合成時(shí)需要記住設(shè)置的密碼

openssl?pkcs12?-export?-clcerts?-in client-cert.pem?-inkey client-key.pem?-out client.p12

OK,到這里證書的準(zhǔn)備工作已經(jīng)完成,后面就是導(dǎo)入工程,在項(xiàng)目里面使用了。

二、將證書導(dǎo)入工程,然后代碼使用

使用子類 SRPinningSecurityPolicy 重寫 父類SRSecurityPolicy 的updateSecurityOptionsInStream方法,注:SRPinningSecurityPolicy 和 SRSecurityPolicy 類都是SocketRocket框架里面的文件,作者提供子類SRPinningSecurityPolicy給開發(fā)者重寫處理證書相關(guān)的操作,如下

SRPinningSecurityPolicy.m:
- (void)updateSecurityOptionsInStream:(NSStream*)stream{

? ? // Enforce TLS 1.2

? ? [streamsetProperty:(__bridge id)CFSTR("kCFStreamSocketSecurityLevelTLSv1_2") forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel];

? ? // Validate certificate chain for this stream if enabled.

? ? NSDictionary *sslOptions = [self defaultSetting];

? ? [streamsetProperty:sslOptions forKey:(__bridge NSString *)kCFStreamPropertySSLSettings];

}


//分割線內(nèi)的方法可以抽出來寫到類里面,簡(jiǎn)書不知道怎么傳文件,所以把代碼貼出來了。
//思路:例如抽到類SocketSSLSetting,可以將defaultSetting寫成類方法
//+?(nonnullNSDictionary?*)defaultSetting,在SRPinningSecurityPolicy使用的時(shí)候?qū)self defaultSetting]改成[SocketSSLSetting defaultSetting]即可

- (nonnullNSDictionary *)defaultSetting {

? ? NSDictionary*settings =nil;

? ? CFTypeRefcerts[2] = {getIdentityRef(),CFArrayGetValueAtIndex(getSecCertificateRefs(), 0)};

? ? CFArrayRefcertsArray =CFArrayCreate(NULL, (void*)certs,2,NULL);

? ? settings =@{(__bridgeid)kCFStreamSSLCertificates:CFBridgingRelease(certsArray)};

? ? return settings;

}

CFArrayRefgetSecCertificateRefs() {

? ? NSString *thePath = [[NSBundle mainBundle] pathForResource:@"CAcert" ofType:@"der"];

? ? NSData*certData = [[NSDataalloc]initWithContentsOfFile:thePath];

? ? CFDataRefcertCFData = (__bridgeCFDataRef)certData;

? ? SecCertificateRef cert = NULL;

? ? cert =SecCertificateCreateWithData(NULL, certCFData);

? ? SecCertificateRefcertArray[1] = { cert };

? ? CFArrayRefcerts =CFArrayCreate(NULL, (void*)certArray,1,NULL);

? ? SecPolicyRef myPolicy = SecPolicyCreateBasicX509();

? ? SecTrustRefmyTrust =NULL;

? ? OSStatusstatus =SecTrustCreateWithCertificates(certs, myPolicy, &myTrust);

? ? if(myPolicy)
? ? ? ? CFRelease(myPolicy);

? ? if(myTrust){
? ? ? ? CFRelease(myTrust)
? ? }

? ? return status==noErr? certs :NULL;

}

SecIdentityRefgetIdentityRef() {

? ? NSString *thePath = [[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"];

? ? NSData*PKCS12Data = [[NSDataalloc]initWithContentsOfFile:thePath];

? ? CFDataRefinPKCS12Data = (__bridgeCFDataRef)PKCS12Data;

? ? CFStringRefpassword =CFSTR("123");//合成p12文件的時(shí)候設(shè)置的密碼

? ? SecIdentityRefmyIdentity =NULL;

? ? SecTrustRefmyTrust =NULL;

? ? OSStatusstatus =noErr;

? ? status =extractIdentityAndTrust(inPKCS12Data, &myIdentity, &myTrust, password);

? ? if(myTrust)
? ? ? ? CFRelease(myTrust);

? ? return status==noErr? myIdentity :NULL;

}

OSStatusextractIdentityAndTrust(CFDataRefinPKCS12Data,SecIdentityRef*outIdentity,SecTrustRef*outTrust,CFStringRefkeyPassword) {

? ? OSStatussecurityError =errSecSuccess;

? ? const void*keys[] =? {kSecImportExportPassphrase };

? ? constvoid*values[] = { keyPassword };

? ? CFDictionaryRefoptionsDictionary =NULL;

? ? optionsDictionary =CFDictionaryCreate(NULL, keys, values, (keyPassword ?1:0),NULL,NULL);

? ? CFArrayRefitems =NULL;

? ? securityError =SecPKCS12Import(inPKCS12Data,optionsDictionary,&items);

? ? if(securityError ==noErr) {
? ? ? ? CFDictionaryRefmyIdentityAndTrust =CFArrayGetValueAtIndex(items,0);

? ? ? ? constvoid*tempIdentity =NULL;

? ? ? ? tempIdentity =? CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemIdentity);

? ? ? ? *outIdentity = (SecIdentityRef)CFRetain(tempIdentity);

? ? ? ? constvoid*tempTrust =NULL;

? ? ? ? tempTrust =CFDictionaryGetValue(myIdentityAndTrust,kSecImportItemTrust);

? ? ? ? *outTrust = (SecTrustRef)CFRetain(tempTrust);

? ? }

? ?if(optionsDictionary)
? ? ? ? CFRelease(optionsDictionary);

? ? if(items)
? ? ? ? CFRelease(items);

? ?return securityError;

}



下面就是初始化Websocket的方法:

NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];

//這里面就是處理證書認(rèn)證
SRPinningSecurityPolicy * cer = [[SRPinningSecurityPolicy alloc] init];

self.socket = [[SRWebSocket alloc] initWithURLRequest:request protocols:@[@"這里是子協(xié)議參數(shù),后臺(tái)沒有定義子協(xié)議參數(shù)的話可以用框架提供的其他方式初始化"] securityPolicy:cer];

self.socket.delegate = self;? //SRWebSocketDelegate 協(xié)議

[self.socketopen];? ? ? ? ? ? //開始連接


到這里就已經(jīng)完成了與后臺(tái)的雙向認(rèn)證了。

關(guān)于雙向認(rèn)證的原理推薦兩個(gè)博客鏈接:
https://blog.csdn.net/oldmtn/article/details/52208747
https://blog.csdn.net/liuchunming033/article/details/48467587

--------------------

第一次寫,可能寫的比較粗糙,忘見諒 -。-!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 在iOS中使用RSA加密解密,需要用到.der和.p12后綴格式的文件,其中.der格式的文件存放的是公鑰(Pub...
    對(duì)面來個(gè)小胖子閱讀 533評(píng)論 0 0
  • 根據(jù)廣大開發(fā)者的傳聞,2017年1月1號(hào),蘋果公司要執(zhí)行ATS政策了。所有app必須強(qiáng)制支持https(不包括一些...
    簡(jiǎn)單日記閱讀 2,234評(píng)論 2 7
  • 北風(fēng)吹的樹葉發(fā)抖 也吹的太陽發(fā)涼 這個(gè)季節(jié)是油菜花開的時(shí)候 滿山遍野從山腰開到了平原 總說這個(gè)時(shí)候 感覺會(huì)有一張車...
    雨安陽閱讀 154評(píng)論 0 1
  • 讀簡(jiǎn)書,有一年多的時(shí)間了,無論是工作還是生活,這里有很多好文,總是能給我一點(diǎn)點(diǎn)提點(diǎn),讓我這一路走的更穩(wěn)。不過這里也...
    張夓閱讀 233評(píng)論 0 1
  • 食材:帶皮羊肉、啤酒、大蒜仔、生姜、干紅椒、白蘿卜; 紅燒羊肉的做法 1、羊肉切塊,在清水中泡一會(huì),濾去血水,冷水...
    忽啦啦閱讀 347評(píng)論 0 0

友情鏈接更多精彩內(nèi)容