Swift iOS 客户端证书身份验证
Swift iOS Client Certificate Authentication
我要使用的 Web 服务需要客户端证书。如何将我的证书发送给它?
为了进一步说明,我不明白如何创建 SecIdentityRef
。
在我的 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 对象; based on the private key and X509 Certificate pair contained in a PKCS12 keystore.
抱歉,我无法使用 Swift 解决方案访问源代码。我所知道的是 NSURLCredential 被返回到 Swift,并直接用于那里的 http url 连接。不过,它与此类似。
我不是 iOS 开发人员,所以我无法帮助您解决 "bridging to 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;
}
}
编辑:哈哈,非常有趣,当你自己在赏金上升时没有打扰时,两次对我投反对票。 *抱怨*
无论如何,要使用上面的内容,您只需要从 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;
}
编辑:上面的 swift 版本在这里,尽管它已经够乱了,我们宁愿不使用它。
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)
为了响应身份验证质询,您需要从您的客户端证书中提取身份。
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
中像这样响应身份验证质询:
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)
}
}
在此 Gist 中找到 Swift3 工作实现:
https://gist.github.com/celian-m/8da09ad293507940a0081507f057def5
我要使用的 Web 服务需要客户端证书。如何将我的证书发送给它?
为了进一步说明,我不明白如何创建 SecIdentityRef
。
在我的 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 对象; based on the private key and X509 Certificate pair contained in a PKCS12 keystore.
抱歉,我无法使用 Swift 解决方案访问源代码。我所知道的是 NSURLCredential 被返回到 Swift,并直接用于那里的 http url 连接。不过,它与此类似。
我不是 iOS 开发人员,所以我无法帮助您解决 "bridging to 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;
}
}
编辑:哈哈,非常有趣,当你自己在赏金上升时没有打扰时,两次对我投反对票。 *抱怨*
无论如何,要使用上面的内容,您只需要从 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;
}
编辑:上面的 swift 版本在这里,尽管它已经够乱了,我们宁愿不使用它。
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)
为了响应身份验证质询,您需要从您的客户端证书中提取身份。
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
中像这样响应身份验证质询:
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)
}
}
在此 Gist 中找到 Swift3 工作实现:
https://gist.github.com/celian-m/8da09ad293507940a0081507f057def5