文章內容包括:html
文中所涉及的文件和腳本代碼請看這裏。node
AFNetworking(下面簡稱AF)是一個優秀的網絡框架,從事iOS開發工做的同窗幾乎都用過它。git
同時,AF也是一個簡單,高效的網絡框架。github
AF3.0版本(3.2.1)是對NSURLSession的封裝。NSURLSession是蘋果公司的HTTP協議實現,它儘量完整地實現了全部功能,可是同蘋果的Autolayout有相同的問題,就是API複雜難用。shell
所以在項目實踐中,即便咱們不使用AF,咱們也須要對NSURLSession進行適度封裝纔可以駕輕就熟。AF幫你作了這件事,並且可能作的更好。npm
AF將NSURLSession的複雜調用封裝到框架內部,並向外提供了更加簡單易懂的接口,它主要包含以下功能:json
AF3.0的代碼足夠簡單,各個模塊也很容易理解,就不過多介紹了,咱們着重分析一下AFSecurityPolicy
這個模塊。數組
iOS9.0版本中,包含了一個叫ATS的驗證機制,要求App網絡請求必須是安全的。主要包含2點:xcode
對於其中上面的第二點,在代碼層次沒有強制要求,使用自簽名證書也是能夠正常請求的,可能會在審覈階段有此要求。緩存
AF中實現了對服務端證書的驗證功能,驗證經過以後,便可正常進行網絡請求。
可是它沒有實現客戶端證書,因此若是服務器要求雙向驗證的時候,咱們就須要對AF進行一些擴展了。
關於https的介紹能夠參考這裏。
服務端驗證證書的代碼在:AFURLSessionManager.m
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.sessionDidReceiveAuthenticationChallenge) {
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
} else {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self.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;
}
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
複製代碼
在NSURLSession中,當請求https的接口時,會觸發- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
回調,在這個回調中,你須要驗證服務端發送過來的證書,並返回一個NSURLCredential
對象。
其中 disposition
這個變量用於表示你對證書的驗證結果,NSURLSessionAuthChallengeUseCredential
表示驗證經過,其餘值都表示驗證失敗。
challenge.protectionSpace.authenticationMethod
這個枚舉字符串表示的是回調觸發的緣由,其中,NSURLAuthenticationMethodServerTrust
表示服務端發來證書,NSURLAuthenticationMethodClientCertificate
表示服務端請求驗證客戶端證書。
驗證證書的方法在AFSecurityPolicy.m中
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString *)domain
{
if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
// https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html
// According to the docs, you should only trust your provided certs for evaluation.
// Pinned certificates are added to the trust. Without pinned certificates,
// there is nothing to evaluate against.
//
// From Apple Docs:
// "Do not implicitly trust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors).
// Instead, add your own (self-signed) CA certificate to the list of trusted anchors."
NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
return NO;
}
NSMutableArray *policies = [NSMutableArray array];
if (self.validatesDomainName) {
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
if (self.SSLPinningMode == AFSSLPinningModeNone) {
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
} else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
return NO;
}
switch (self.SSLPinningMode) {
case AFSSLPinningModeNone:
default:
return NO;
case AFSSLPinningModeCertificate: {
NSMutableArray *pinnedCertificates = [NSMutableArray array];
for (NSData *certificateData in self.pinnedCertificates) {
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
if (!AFServerTrustIsValid(serverTrust)) {
return NO;
}
// obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
return YES;
}
}
return NO;
}
case AFSSLPinningModePublicKey: {
NSUInteger trustedPublicKeyCount = 0;
NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
for (id trustChainPublicKey in publicKeys) {
for (id pinnedPublicKey in self.pinnedPublicKeys) {
if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
trustedPublicKeyCount += 1;
}
}
}
return trustedPublicKeyCount > 0;
}
}
return NO;
}
複製代碼
代碼解析:
函數第一行就是一長串的邏輯判斷,乍一看,這裏看的人很懵,它包含的信息不少。但實際上它的做用是用來處理服務端自簽名證書
的。其餘狀況無需考慮此處邏輯。根據後面代碼來看,若是你服務端證書使用的是自簽名證書,AFSecurityPolicy
的allowInvalidCertificates
屬性必須設爲YES,因此這裏判斷會帶上self.allowInvalidCertificates
。
接下來就是驗證服務端證書的過程,SSLPinningMode
有3個值,AFSSLPinningModeNone
表示服務端使用的是CA機構簽發的正式證書
,另外2個值表示服務端使用的是自簽名證書。
AFServerTrustIsValid
這個函數使用的是Security.framework
中的方法,用於驗證服務端發送來的證書是不是可信任的,只要證書鏈中任何一個證書是已經信任的證書,那麼這個服務端證書就是合法的。詳細過程已經被Security.framework
處理了,不須要咱們作額外工做。關於證書鏈能夠參考這裏。
第三部分代碼就是服務端自簽名證書
的驗證了,這種狀況下,須要把服務端證書也放到客戶端中一份。根據SSLPinningMode
,你能夠選擇使用服務端證書
或者服務端證書內的公鑰
。
AFSSLPinningModeCertificate
表示客戶端須要保存一個服務端根證書
,用於驗證服務端證書是否合法。客戶端須要將服務端證書的證書鏈上的任意一個證書拖入xcode工程中。
自簽名證書須要設置pinnedCertificates
屬性,把拖入xcode的證書加載到內存中,保存在pinnedCertificates
數組中。經過SecTrustSetAnchorCertificates
方法把數組中的證書同服務端返回的證書作證書鏈綁定,而後就能夠用AFServerTrustIsValid
方法驗證證書是否合法了,若是服務端證書和咱們客戶端保存的證書能夠正確匹配,這個函數就會返回YES。
AFSSLPinningModePublicKey
表示客戶端須要保存一個服務端根證書公鑰
,用於驗證服務端證書是否合法。客戶端須要將服務端證書鏈上的任意一證書的公鑰拖入xcode工程中。
若使用公鑰驗證,則須要從服務端證書中取出公鑰,同時取出客戶端中保存的公鑰,逐一比較,若是有匹配的就認爲驗證成功。
根據上述分析,客戶端對於證書的使用,有下面的3種狀況:
咱們對上面所述3種證書使用情形進行逐一驗證。
驗證以前,咱們須要作3個準備工做:
https使用的證書都是基於X.509格式的。
CA機構的正式證書通常是要花錢購買的,固然也有免費的,我以前在阿里雲買過免費的證書。通常申請經過後,你能夠把證書下載下來,其中主要包含私鑰和各類格式的證書。
自簽名的證書就比較容易了,在mac中可使用openssl命令來生成。
我寫了一個簡單的腳本,用於生成各類自簽名證書,你能夠把它保存到文件(文件名爲:create.sh)中,在終端裏執行。
腳本會生成3種證書:根證書,客戶端證書,服務端證書。
其中不一樣的證書沒有本質區別,只是用在不一樣的地方而已。
每種證書包含5個文件,分別是:
#!/bin/sh
locale='CN' #地區
province='Beijing' #省份
city=$province #城市
company='xxx' #公司
unit='yyy' #部門
hostname='127.0.0.1' #域名
email='hr@suning.com' #郵箱
#clean
function clean(){
echo '清理文件...'
ls | grep -v create.sh | xargs rm -rf
}
#用法
function usage(){
echo 'usage: ./create.sh
[-l [localevalue]]
[-p [provincevalue]]
[-c [cityvalue]]
[-d [companyvalue]]
[-u [unitvalue]]
[-h [hostnamevalue]]
[-e [emailvalue]]
'
exit
}
#參數
if [ $# -gt 0 ]; then
while getopts "cl:p:c:d:u:h:e" arg;
do
case $arg in
c)
clean && exit
;;
l)
locale=$OPTARG
;;
p)
province=$OPTARG
;;
c)
city=$OPTARG
;;
d)
company=$OPTARG
;;
u)
unit=$OPTARG
;;
h)
hostname=$OPTARG
;;
e)
email=$OPTARG
;;
?)
usage
;;
esac
done
fi
clean
echo '開始建立根證書...'
openssl genrsa -out ca-private-key.pem 1024
openssl req -new -out ca-req.csr -key ca-private-key.pem <<EOF
${locale}
${province}
${city}
${company}
${unit}
${hostname}
${email}
EOF
openssl x509 -req -in ca-req.csr -out ca-cert.pem -outform PEM -signkey ca-private-key.pem -days 3650
openssl x509 -req -in ca-req.csr -out ca-cert.der -outform DER -signkey ca-private-key.pem -days 3650
echo '請輸入根證書p12文件密碼,直接回車表示密碼爲空字符串...'
openssl pkcs12 -export -clcerts -in ca-cert.pem -inkey ca-private-key.pem -out ca-cert.p12
echo '開始建立服務端證書...'
openssl genrsa -out server-private-key.pem 1024
openssl req -new -out server-req.csr -key server-private-key.pem << EOF
${locale}
${province}
${city}
${company}
${unit}
${hostname}
${email}
EOF
openssl x509 -req -in server-req.csr -out server-cert.pem -outform PEM -signkey server-private-key.pem -CA ca-cert.pem -CAkey ca-private-key.pem -CAcreateserial -days 3650
openssl x509 -req -in server-req.csr -out server-cert.der -outform DER -signkey server-private-key.pem -CA ca-cert.pem -CAkey ca-private-key.pem -CAcreateserial -days 3650
echo '請輸入服務端證書p12文件密碼,直接回車表示密碼爲空字符串...'
openssl pkcs12 -export -clcerts -in server-cert.pem -inkey server-private-key.pem -out server-cert.p12
echo '開始建立客戶端證書...'
openssl genrsa -out client-private-key.pem 1024
openssl req -new -out client-req.csr -key client-private-key.pem << EOF
${locale}
${province}
${city}
${company}
${unit}
${hostname}
${email}
EOF
openssl x509 -req -in client-req.csr -out client-cert.pem -outform PEM -signkey client-private-key.pem -CA ca-cert.pem -CAkey ca-private-key.pem -CAcreateserial -days 3650
openssl x509 -req -in client-req.csr -out client-cert.der -outform DER -signkey client-private-key.pem -CA ca-cert.pem -CAkey ca-private-key.pem -CAcreateserial -days 3650
echo '請輸入客戶端證書p12文件密碼,直接回車表示密碼爲空字符串...'
openssl pkcs12 -export -clcerts -in client-cert.pem -inkey client-private-key.pem -out client-cert.p12
echo 'finishied'
複製代碼
你能夠按照步驟操做:
複製腳本內容,保存到文件中,文件名爲create.sh
打開終端,經過cd
命令進入create.sh
所在的文件夾
在終端內輸入:chmod +x create.sh
點擊回車
在終端輸入:./create.sh -h
,此時會打印用法
usage: ./create.sh
[-l [localevalue]]
[-p [provincevalue]]
[-c [cityvalue]]
[-d [companyvalue]]
[-u [unitvalue]]
[-h [hostnamevalue]]
[-e [emailvalue]]
複製代碼
腳本有下面幾種用法:
./create.sh -h
打印用法./create.sh -c
會清空生成的全部文件./create.sh
直接回車,會使用默認參數生成證書./create.sh + 用法中所述選項
會使用自定義的參數生成證書腳本執行成功後,應該會生成下面的文件:
咱們使用nodejs來搭建https服務器,請按照以下步驟操做:
package.json
,內容以下:{
"name": "test-https",
"version": "1.0.0",
"main": "app.js",
"scripts": {
"start": "node app.js"
},
"debug": true,
"dependencies": {
"koa": "2.5.2",
"koa-router": "7.4.0"
}
}
複製代碼
app.js
,內容以下:const Koa = require('koa');
const https = require('https');
const fs = require('fs');
const router = require('koa-router')();
const app = new Koa();
//路由
router.get('/', (ctx, next) => {
ctx.response.body = 'this is a simple node js https server response';
})
app.use(router.routes());
//https
https.createServer({
key: fs.readFileSync('./yourServerCertPrivatekey.key'),
cert: fs.readFileSync('./yourServerCert.pem'),
requestCert: true,
ca:[fs.readFileSync('./yourClientCert.pem')]
}, app.callback()).listen(3000);
console.log(`https app started at port 3000`)
複製代碼
cd
命令進入咱們建立的服務器文件夾,而後執行命令:npm install
,等待命令完成(可能會比較慢,根據網絡狀況而定)。如出現下列字樣表示安裝成功(不必定徹底相同):added 40 packages from 21 contributors and audited 53 packages in 8.446s
found 0 vulnerabilities
複製代碼
node app.js
來啓動服務器。可是你會發現會報錯,這是由於fs.readFileSync(filename)
這句代碼表示要讀取一個證書文件,要確保文件存在才能夠。咱們後續根據需求來修改此處文件路徑便可。https app started at port 3000
複製代碼
這個比較簡單,就很少說了。咱們使用下列基本代碼來作證書測試。
-(void) test{
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] init];
//HTTPS驗證代碼,咱們主要修改這裏
AFSecurityPolicy *policy = [AFSecurityPolicy defaultPolicy];
policy.validatesDomainName = NO;//不驗證域名,是爲了測試方便,不然你須要修改host文件了
manager.securityPolicy = policy;
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
//請求地址就寫這個
[manager GET:@"https://127.0.0.1:3000/" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"succ and response = [%@]", [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"fail");
}];
}
複製代碼
這個是最簡單的狀況,AF已經支持,咱們不須要作任何額外工做就可以支持。
首先,咱們將服務端的代碼中的證書路徑指向咱們在CA機構申請好的服務端證書路徑,其中key
表示證書私鑰,cert
表示pem格式證書。另外將requestCert
和ca
這兩個字段先刪除,而後從新啓動服務器。像下面這樣:
... ...
//https
https.createServer({
key: fs.readFileSync(這裏改爲你的私鑰路徑),
cert: fs.readFileSync(這裏改爲你的pem格式證書路徑)
}, app.callback()).listen(3000);
... ...
複製代碼
而後,客戶端的代碼不須要修改。直接運行xcode,正常狀況下你能夠看到以下輸出:
succ and response = [this is a simple node js https server response]
複製代碼
服務端代碼不變,只是將證書和私鑰路徑修改成咱們自簽名的證書路徑。
上文中,咱們已經建立過自簽名的證書。
首先把證書文件夾的私鑰文件server-private-key.pem
和證書文件server-cert.pem
複製到服務器文件夾下。
而後服務器代碼修改以下:
... ...
//https
https.createServer({
key: fs.readFileSync('./server-private-key.pem'),
cert: fs.readFileSync('./server-cert.pem')
}, app.callback()).listen(3000);
... ...
複製代碼
重啓服務器。
客戶端須要把證書文件夾內的server-cert.der
文件拖入xcode中,而後將xcode中的證書修更名字爲server-cert.cer
。
客戶端代碼作以下修改(請看註釋):
-(void) test{
//使用服務器自簽名證書,須要指定baseUrl屬性。
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://127.0.0.1:3000"]];
//AFSSLPinningModeCertificate表示使用自簽名證書
AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
//爲了測試方便不驗證域名,若要驗證域名,則請求時的域名要和建立證書(建立證書的腳本執行時可指定域名)時的域名一致
policy.validatesDomainName = NO;
//自簽名服務器證書須要設置allowInvalidCertificates爲YES
policy.allowInvalidCertificates = YES;
//指定本地證書路徑
policy.pinnedCertificates = [AFSecurityPolicy certificatesInBundle:[NSBundle mainBundle]];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[manager GET:@"https://127.0.0.1:3000/" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"succ and response = [%@]", [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"fail");
}];
}
複製代碼
運行工程,正常狀況下,能夠看到正確輸出。
這叫作雙向驗證,客戶端驗證服務端無誤以後,服務端也能夠驗證客戶端證書,這樣能夠保證數據傳輸雙方都是本身想要的目標。
首先,把證書文件夾內的client-cert.pem
文件複製到服務器文件夾內。
而後修改服務端代碼:
... ...
//https
https.createServer({
key: fs.readFileSync('./server-private-key.pem'),
cert: fs.readFileSync('./server-cert.pem'),
requestCert: true,//表示客戶端須要證書
ca:[fs.readFileSync('./client-cert.pem')]//用於匹配客戶端證書
}, app.callback()).listen(3000);
... ...
複製代碼
重啓服務器。
客戶端須要把證書文件夾內的client-cert.p12
文件拖到xcode中。
客戶端請求代碼不須要修改。
由於AF3.0並無提供對客戶端證書的支持,因此咱們須要修改AF的代碼。
找到AFURLSessionManager.m
文件,在- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
方法。
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.sessionDidReceiveAuthenticationChallenge) {
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
} else {
NSString *authMethod = challenge.protectionSpace.authenticationMethod;
if ([authMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self.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 if([authMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]){
NSData *p12Data = [NSData dataWithContentsOfFile:[NSBundle pathForResource:@"client-cert" ofType:@"p12" inDirectory:[NSBundle mainBundle].bundlePath]];
if([p12Data isKindOfClass:[NSData class]]){
SecTrustRef trust = NULL;
SecIdentityRef identity = NULL;
[[self class] extractIdentity:&identity andTrust:&trust fromPKCS12Data:p12Data];
if(identity){
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;
}else{
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}else{
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
+ (BOOL)extractIdentity:(SecIdentityRef*)outIdentity andTrust:(SecTrustRef *)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {
OSStatus securityError = errSecSuccess;
//客戶端證書密碼
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;
return YES;
} else {
NSLog(@"SecPKCS12Import is failed with error code %d", (int)securityError);
return NO;
}
}
複製代碼
上述代碼參考自這裏。
值得注意的有2個地方:
p12
文件的文件名,咱們這裏寫死了client-cert.p12
,能夠根據具體狀況作修改。p12
文件的密碼,在extractIdentity:
方法的第三行,能夠改爲你的p12文件密碼,密碼能夠爲空。代碼修改好以後,運行工程,能夠獲得正確的服務端返回。
文中內容均已通過測試,但仍然可能有錯誤之處,如發現請留言。
文中所涉及的腳本
,證書
,服務器代碼
, 客戶端代碼
,已經上傳到github中,點這裏,都已經包含了安裝環境,下載後直接打開就能使用。
--完--