標籤:des blog http java os 檔案
什麼是雙向認證呢?簡而言之,就是伺服器端對請求它的用戶端要進行身分識別驗證,用戶端對自己所請求的伺服器也會做身分識別驗證。服務端一旦驗證到請求自己的用戶端為不可信任的,服務端就拒絕繼續通訊。用戶端如果探索服務端為不可信任的,那麼也中止通訊。
雙向認證的演算法理論是RSA,(點擊此處瞭解RSA演算法原理)。 雙向認證具體又是通過安全性憑證的方式來實現的,安全性憑證可用openssl或java程式來產生,用於雙向認證的安全性憑證中儲存了金鑰組,憑證授權單位信 息,簽名資訊,簽名演算法,頒發對象,有效期間等資訊。雙向認證中安全性憑證分為伺服器端認證和用戶端認證,用伺服器端認證中的私密金鑰對用戶端認證進行簽名,並把 簽名資訊寫到用戶端認證中,就得到了被服務端信任的認證。當用戶端請求該服務端時,服務端為拿到用戶端認證資訊,然後取出認證中的簽名資訊,用伺服器端證 書的公開金鑰驗證,如果發現這個用戶端認證確實是伺服器端認證簽名頒發的,那麼通訊就可以繼續進行,否則中斷。
上面簡單介紹了一下雙向認證和安全性憑證,那麼我們現在開始正題。
首先,我們用java產生一個伺服器端認證庫myserverdomain和用戶端認證庫wenfeng.xu,取出伺服器端的認證庫中的認證為用戶端證 書庫簽名並產生PKCS12格式的認證檔案wenfeng.xu.pfx。然後我們將伺服器端認證配置在應用伺服器中,並啟用用戶端認證。以jetty為 例,以下為配置方法:
啟動應用伺服器,並用與產生服務端認證一致的網域名稱訪問應用(注意這點非常重要,ASIHTTPRequest如果不這麼做是會報錯的,這個網域名稱可以隨便 取,只要更改系統的host配置,讓網域名稱指向服務端ip就行了)。如果你用瀏覽器訪問已啟動的應用,如果看到以下資訊,就可以開始oc用戶端的編碼了。
在引入了ASIHTTPRequest架構的項目中建立測試類別Https.m
C代碼
- @implementation Https
-
- + (void)testClientCertificate {
- NSURL *httpsUrl = [NSURL URLWithString:@"https://www.myserverdomain.com:8443/smvcj"];//訪問路徑
-
- ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:httpsUrl];
-
- SecIdentityRef identity = NULL;
- SecTrustRef trust = NULL;
-
- //綁定認證,認證放在Resources檔案夾中
- NSData *PKCS12Data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"wenfeng.xu" ofType:@"pfx"]];//認證檔案名稱和檔案類型
- [Https extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data];
-
- request = [ASIHTTPRequest requestWithURL:httpsUrl];
-
- [request setClientCertificateIdentity:identity];//設定訪問路徑
- [request setValidatesSecureCertificate:NO];//是否驗證伺服器端認證,如果此項為yes那麼伺服器端認證必須為合法的認證機構頒發的,而不能是自己用openssl 或java產生的認證
-
-
- [request startSynchronous];
-
- NSError *error = [request error];
- if (!error) {
- NSString *response = [request responseString];
- NSLog(@"response is : %@",response);
- } else {
- NSLog(@"Failed to save to data store: %@", [error localizedDescription]);
- NSLog(@"%@",[error userInfo]);
- }
- }
-
- + (BOOL)extractIdentity:(SecIdentityRef *)outIdentity andTrust:(SecTrustRef*)outTrust fromPKCS12Data:(NSData *)inPKCS12Data {
-
-
- OSStatus securityError = errSecSuccess;
-
- CFStringRef password = CFSTR("[email protected]"); //認證密碼
- const void *keys[] = { kSecImportExportPassphrase };
- const void *values[] = { password };
-
- CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys,values, 1,NULL, NULL);
-
- CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
- //securityError = SecPKCS12Import((CFDataRef)inPKCS12Data,(CFDictionaryRef)optionsDictionary,&items);
- securityError = SecPKCS12Import((CFDataRef)inPKCS12Data,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(@"Failed with error code %d",(int)securityError);
- return NO;
- }
- return YES;
- }
-
- @end
在項目中調用 testClientCertificate方法,發現會報以下錯誤
C代碼
- 2014-01-04 15:49:51.194 Mac[661:303] CFNetwork SSLHandshake failed (-9807)
- 2014-01-04 15:49:51.203 Mac[661:303] Failed to save to data store: A connection failure occurred: SSL problem (Possible causes may include a bad/expired/self-signed certificate, clock set to wrong date)
- 2014-01-04 15:49:51.204 Mac[661:303] {
- NSLocalizedDescription = "A connection failure occurred: SSL problem (Possible causes may include a bad/expired/self-signed certificate, clock set to wrong date)";
- NSUnderlyingError = "Error Domain=NSOSStatusErrorDomain Code=-9807 \"The operation couldn\U2019t be completed. (OSStatus error -9807.)\" (errSSLXCertChainInvalid: Invalid certificate chain )";
- }
怎麼會這樣?分析最後一句“Invalid certificate chain” 意思是無效的憑證鏈結。因為每一個認證中都有一個憑證鏈結,來表示這個認證的階層。報這個錯是因為這個用戶端認證的最頂層是我們自己建立的認證,而不是合 法的認證機構頒發的。每個作業系統預設會把一些公認的認證機構頒發的密鑰憑證存在系統信認的根憑證庫中,以便信任由這些公認的認證機構簽名給其它使用者的證 書。那麼如何在測試環境中避免這個錯?我們只要修改ASIHTTPRequest架構中的相關配置就行了,開啟ASIHTTPRequest.m檔案,查 找“https”關健字,找到
Java代碼
- NSMutableDictionary *sslProperties = [NSMutableDictionary dictionaryWithCapacity:1];
將其注掉,然後換成以下代碼
Java代碼
- NSMutableDictionary *sslProperties =[[NSMutableDictionary alloc] initWithObjectsAndKeys:
- [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates,
- [NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot,
- [NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain,
- kCFNull,kCFStreamSSLPeerName,
- nil];
解決我們的錯誤的關鍵代碼是
[NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain 表示不校正憑證鏈結。
儲存一下再運行就可以正常訪問應用了。