nginx關鍵配置如下:
listen 443; server_name localhost; ssl on; ssl_certificate /usr/local/opt/nginx/certificates/server.cer; ssl_certificate_key /usr/local/opt/nginx/certificates/server.key.pem; ssl_client_certificate /usr/local/opt/nginx/certificates/ca.cer; ssl_verify_client on;
ssl開啟https
ssl_certificate是服務端證書的路徑,ssl_certificate_key是服務端私鑰的路徑ssl_verify_client是配置雙向認證(client certificate)
ssl_client_certificate是簽發客戶端證書的根證書
為什麼是根證書,因為可以簽發很多客戶端證書,只要是由該根證書簽發的,服務端都視為認證通過
配置完成以後,一般需要把80端口的http請求跳轉到443端口,否則用戶可以通過80端口以http方式訪問,就失去了安全保護的意義
在網上找了很多ios client certificate的帖子,要麼是代碼不全,要麼是很老的帖子,還是用的NSURLConnection,delegate method都deprecated了,最後找了一個代碼片段,改了一下
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; NSURL *url = [NSURL URLWithString:@"https://localhost/svc/portal/setting"]; NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; [request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData]; [request setHTTPShouldHandleCookies:NO]; [request setTimeoutInterval:30]; [request setHTTPMethod:@"GET"]; NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSString *message = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"%@", message); }]; [task resume];
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { NSString *method = challenge.protectionSpace.authenticationMethod; NSLog(@"%@", method); if([method isEqualToString:NSURLAuthenticationMethodServerTrust]){ NSString *host = challenge.protectionSpace.host; NSLog(@"%@", host); NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; completionHandler(NSURLSessionAuthChallengeUseCredential, credential); return; } NSString *thePath = [[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"]; NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath]; CFDataRef inPKCS12Data = (CFDataRef)CFBridgingRetain(PKCS12Data); SecIdentityRef identity; // 讀取p12證書中的內容 OSStatus result = [self extractP12Data:inPKCS12Data toIdentity:&identity]; if(result != errSecSuccess){ completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); return; } SecCertificateRef certificate = NULL; SecIdentityCopyCertificate (identity, &certificate); const void *certs[] = {certificate}; CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL); NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:(NSArray*)CFBridgingRelease(certArray) persistence:NSURLCredentialPersistencePermanent]; completionHandler(NSURLSessionAuthChallengeUseCredential, credential); } -(OSStatus) extractP12Data:(CFDataRef)inP12Data toIdentity:(SecIdentityRef*)identity { OSStatus securityError = errSecSuccess; CFStringRef password = CFSTR("the_password"); 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; }
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];