Swift iOS客户端证书authentication

我想要使​​用的Web服务需要客户端证书。 我怎样才能发送我的证书呢?

为了进一步阐述我不明白如何创buildSecIdentityRef

NSURLConnection didReceiveAuthenticationChallenge我在ServerTrust之后得到了这个条件:

 else if challenge?.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate { var secIdent : SecIdentityRef = ????????? var certCred = NSURLCredential(identity: secIdent, certificates: [getClientCertificate()], persistence: NSURLCredentialPersistence.Permanent) challenge?.sender.useCredential(certCred, forAuthenticationChallenge: challenge!) } 

getClientCertificate方法:

 func getClientCertificate() -> SecCertificateRef { let mainBundle : NSBundle = NSBundle.mainBundle() var mainBund = mainBundle.pathForResource("iosClientCert", ofType: "cer") //exported the cert in der format. var key : NSData = NSData(contentsOfFile: mainBund!)! var turnToCert : SecCertificateRef = SecCertificateCreateWithData(kCFAllocatorDefault, key).takeRetainedValue() return turnToCert; } 

从技术上讲,当我认识的人需要在Swift中实现时,他使用了下面的Objective-C实现来获取NSURLCredential对象到连接; 基于包含在PKCS12密钥库中的私钥和X509证书对 。

对不起,我无法使用Swift解决scheme访问源代码。 我所知道的是,NSURLCredential被返回到Swift,并直接在http url连接中使用。 不过,这个和这个相似。

我不是一个iOS开发人员,所以我不能帮助你“桥接到Swift”部分。

 - (void)getMessageWithURL:(NSString *)url { NSURL *URL = [NSURL URLWithString:url]; NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; [request setURL:URL]; [request setHTTPMethod:@"GET"]; NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; [connection self]; } - (void)postMessageWithURL:(NSString *)url withContent:(NSString *)content { NSData *postData = [content dataUsingEncoding:NSUTF8StringEncoding]; NSString *postLength = [NSString stringWithFormat:@"%d", [postData length]]; NSURL *myURL = [NSURL URLWithString:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:myURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60]; [request setHTTPMethod:@"POST"]; [request setValue:postLength forHTTPHeaderField:@"Content-Length"]; [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; [request setHTTPBody:postData]; NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; [connection self]; } - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace { return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]; } - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { NSLog(@"didReceiveAuthenticationChallenge"); } - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { responseData = [[NSMutableData alloc] init]; } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { [responseData appendData:data]; } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { NSLog(@"Unable to fetch data"); NSLog(@"%@", error); } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSLog(@"Succeeded! Received %lu bytes of data", (unsigned long)[responseData length]); NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]; NSLog(@"%@", responseString); [bridge callHandler:handlerName data:responseString]; } - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { /* Reading the certificate and creating the identity */ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = paths[0]; // Get documents directory NSData *p12data = [CertificateManager getP12Data]; //returns essentially a byte array containing a valid PKCS12 certificate if (!p12data) { return; NSAssert(p12data, @"Couldn't load p12 file..."); } CFStringRef password = CFSTR("password"); const void *keys[] = {kSecImportExportPassphrase}; const void *values[] = {password}; CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL); CFArrayRef p12Items; OSStatus result = SecPKCS12Import((__bridge CFDataRef) p12data, optionsDictionary, &p12Items); if (result == noErr) { CFDictionaryRef identityDict = CFArrayGetValueAtIndex(p12Items, 0); SecIdentityRef identityApp = (SecIdentityRef) CFDictionaryGetValue(identityDict, kSecImportItemIdentity); SecCertificateRef certRef; SecIdentityCopyCertificate(identityApp, &certRef); SecCertificateRef certArray[1] = {certRef}; CFArrayRef myCerts = CFArrayCreate(NULL, (void *) certArray, 1, NULL); CFRelease(certRef); NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identityApp certificates:nil persistence:NSURLCredentialPersistenceNone]; CFRelease(myCerts); [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; } else { // Certificate is invalid or password is invalid given the certificate NSLog(@"Invalid certificate or password"); NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:result userInfo:nil]; return; } } 

编辑:哈哈,非常有趣,两次downvoting当你自己没有打扰,而赏金。 *抱怨*

无论如何,要使用以上的内容,你只需要从Swift中访问它。

 func connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge) { if let p12Data = UserManager.currentP12, let credential = CertificateManager.getCredentialsForP12(p12Data) as? NSURLCredential { challenge.sender.useCredential(credential, forAuthenticationChallenge: challenge) } else { UIApplication.sharedApplication().networkActivityIndicatorVisible = false } } 

这使用这个。

 + (id)getCredentialsForP12:(NSData *)p12 { NSData* p12data = p12; const void *keys[] = {kSecImportExportPassphrase}; const void *values[] = {CFSTR("thePassword")}; CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL); CFArrayRef p12Items; OSStatus result = SecPKCS12Import((__bridge CFDataRef) p12data, optionsDictionary, &p12Items); if (result == noErr) { CFDictionaryRef identityDict = CFArrayGetValueAtIndex(p12Items, 0); SecIdentityRef identityApp = (SecIdentityRef) CFDictionaryGetValue(identityDict, kSecImportItemIdentity); SecCertificateRef certRef; SecIdentityCopyCertificate(identityApp, &certRef); SecCertificateRef certArray[1] = {certRef}; CFArrayRef myCerts = CFArrayCreate(NULL, (void *) certArray, 1, NULL); CFRelease(certRef); NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identityApp certificates:nil persistence:NSURLCredentialPersistenceNone]; CFRelease(myCerts); return credential; } else { // Certificate is invalid or password is invalid given the certificate NSLog(@"Invalid certificate or password"); UIAlertView* av = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Invalid cert or pass" delegate:nil cancelButtonTitle:@"ok" otherButtonTitles: nil]; [av show]; NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:result userInfo:nil]; return nil; } 

编辑:上面的一个迅速的版本在这里,虽然它已经够乱了,我们宁愿不使用它。

  var p12items : Unmanaged<CFArrayRef>? let index: CFIndex = 1 let password: CFString = "password" let key = kSecImportExportPassphrase.takeRetainedValue() as String var values = [unsafeAddressOf(password)] var keys = [unsafeAddressOf(key)] var keyCallbacks = kCFTypeDictionaryKeyCallBacks var valueCallbacks = kCFTypeDictionaryValueCallBacks let length: CFIndex = p12Data.length let p12CfData: CFData = CFDataCreate(kCFAllocatorDefault, UnsafePointer<UInt8>(p12Data.bytes), length) let options = CFDictionaryCreate(kCFAllocatorDefault, &keys, &values, index, &keyCallbacks, &valueCallbacks) let result = SecPKCS12Import(p12CfData, options, &p12items) if result == noErr { let idIndex: CFIndex = 0 var items = p12items?.takeRetainedValue() var identityDict = CFArrayGetValueAtIndex(items!, idIndex) var key = kSecImportItemIdentity.takeRetainedValue() as String var keyAddress = unsafeAddressOf(key) var identityApp: SecIdentityRef = CFDictionaryGetValue(identityDict, keyAddress) var certRef : Unmanaged<SecCertificateRef>? SecIdentityCopyCertificate(identityApp, &certRef) var cert: SecCertificateRef = certRef!.takeRetainedValue() var certArray = [unsafeAddressOf(cert)] var arrayCallback = kCFTypeArrayCallBacks var myCerts: CFArrayRef = CFArrayCreate(kCFAllocatorDefault, &certArray, index, &arrayCallback); let credential: NSURLCredential = NSURLCredential(identity: identityApp, certificates: [AnyObject](), persistence: NSURLCredentialPersistence.None) 

为了响应身份validation挑战,您需要从客户端证书中提取身份。

 struct IdentityAndTrust { var identityRef:SecIdentityRef var trust:SecTrustRef var certArray:NSArray } func extractIdentity(certData:NSData, certPassword:String) -> IdentityAndTrust { var identityAndTrust:IdentityAndTrust! var securityError:OSStatus = errSecSuccess var items:Unmanaged<CFArray>? let certOptions:CFDictionary = [ kSecImportExportPassphrase.takeRetainedValue() as String: certPassword ]; // import certificate to read its entries securityError = SecPKCS12Import(certData, certOptions, &items); if securityError == errSecSuccess { let certItems:CFArray = items?.takeUnretainedValue() as CFArray!; let certItemsArray:Array = certItems as Array let dict:AnyObject? = certItemsArray.first; if let certEntry:Dictionary = dict as? Dictionary<String, AnyObject> { // grab the identity let identityPointer:AnyObject? = certEntry["identity"]; let secIdentityRef:SecIdentityRef = identityPointer as! SecIdentityRef!; // grab the trust let trustPointer:AnyObject? = certEntry["trust"]; let trustRef:SecTrustRef = trustPointer as! SecTrustRef; // grab the certificate chain var certRef:Unmanaged<SecCertificate>? SecIdentityCopyCertificate(secIdentityRef, &certRef); let certArray:NSMutableArray = NSMutableArray(); certArray.addObject(certRef?.takeRetainedValue() as SecCertificateRef!); identityAndTrust = IdentityAndTrust(identityRef: secIdentityRef, trust: trustRef, certArray: certArray); } } return identityAndTrust; } 

NSURLSessionDelegate响应这样的authentication挑战:

 public func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) { let bundle:NSBundle = NSBundle(forClass: self.dynamicType); let bundleCertPath:NSString = bundle.pathForResource("clientCertificateName", ofType: "p12")!; let certData:NSData = NSData(contentsOfFile: bundleCertPath as String)!; let identityAndTrust:IdentityAndTrust = self.certificateHelper.extractIdentity(certData, certPassword: "C00lp@assword"); if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate { let urlCredential:NSURLCredential = NSURLCredential( identity: identityAndTrust.identityRef, certificates: identityAndTrust.certArray as [AnyObject], persistence: NSURLCredentialPersistence.ForSession); completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, urlCredential); } else { // nothing here but us chickens } } 

我使用的是最后一个xcode和swift版本,而这个代码对我来说,使用客户端证书.pfx,基于Bins Ich答案:

  func extractIdentity(certData:NSData) -> IdentityAndTrust { var identityAndTrust:IdentityAndTrust! var securityError:OSStatus = errSecSuccess var items:Unmanaged<CFArray>? let certOptions:CFDictionary = [ kSecImportExportPassphrase.takeRetainedValue() as String: "password" ]; // import certificate to read its entries securityError = SecPKCS12Import(certData, certOptions, &items); if securityError == errSecSuccess { let certItems:CFArray = items?.takeUnretainedValue() as CFArray!; let certItemsArray:Array = certItems as Array let dict:AnyObject? = certItemsArray.first; if let certEntry:Dictionary = dict as? Dictionary<String, AnyObject> { // grab the identity let identityPointer:AnyObject? = certEntry["identity"]; let secIdentityRef:SecIdentityRef = identityPointer as! SecIdentityRef!; // grab the trust let trustPointer:AnyObject? = certEntry["trust"]; let trustRef:SecTrustRef = trustPointer as! SecTrustRef; // grab the cert let chainPointer:AnyObject? = certEntry["chain"]; let chainRef:SecCertificateRef = chainPointer as! SecCertificateRef; let certArray:CFArrayRef = chainRef as! CFArrayRef identityAndTrust = IdentityAndTrust(identityRef: secIdentityRef, trust: trustRef, certArray: certArray); } } return identityAndTrust; } func connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge) { let strTemp = challenge.protectionSpace.authenticationMethod if(strTemp == NSURLAuthenticationMethodServerTrust) { challenge.sender.continueWithoutCredentialForAuthenticationChallenge(challenge) } if(strTemp == NSURLAuthenticationMethodClientCertificate) { let certFile = NSBundle.mainBundle().pathForResource("mycert", ofType:"pfx") let p12Data = NSData(contentsOfFile:certFile!) let identityAndTrust:IdentityAndTrust = extractIdentity(p12Data!) let urlCredential:NSURLCredential = NSURLCredential( identity: identityAndTrust.identityRef, certificates:identityAndTrust.certArray as [AnyObject], persistence: NSURLCredentialPersistence.Permanent) challenge.sender.useCredential(urlCredential ,forAuthenticationChallenge:challenge) } }