I want to authenticate client certificate in my app. I am getting the following errors in didReceiveChallenge
function. I found this solution in this link.
My didReceiveChallenge
func code:
func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
if challenge.protectionSpace.authenticationMethod == (NSURLAuthenticationMethodServerTrust) {
let serverTrust:SecTrustRef = challenge.protectionSpace.serverTrust!
let certificate: SecCertificateRef = SecTrustGetCertificateAtIndex(serverTrust, 0)!
let remoteCertificateData = CFBridgingRetain(SecCertificateCopyData(certificate))!
let cerPath: String = NSBundle.mainBundle().pathForResource("example.com", ofType: "cer")!
let localCertificateData = NSData(contentsOfFile:cerPath)!
if (remoteCertificateData.isEqualToData(localCertificateData) == true) {
let credential:NSURLCredential = NSURLCredential(forTrust: serverTrust)
challenge.sender?.useCredential(credential, forAuthenticationChallenge: challenge)
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!))
} else {
completionHandler(NSURLSessionAuthChallengeDisposition.CancelAuthenticationChallenge, nil)
}
}
else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate
{
let path: String = NSBundle.mainBundle().pathForResource("client", ofType: "p12")!
let PKCS12Data = NSData(contentsOfFile:path)!
var p12items : Unmanaged<CFArray>?
let index: CFIndex = 1
let password: CFString = "password"
var values = [unsafeAddressOf(password)]
var keys = [unsafeAddressOf(kSecImportExportPassphrase)]
var keyCallbacks = kCFTypeDictionaryKeyCallBacks
var valueCallbacks = kCFTypeDictionaryValueCallBacks
let length: CFIndex = PKCS12Data.length
let p12CfData: CFData = CFDataCreate(kCFAllocatorDefault, UnsafePointer<UInt8>(PKCS12Data.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 keyAddress = unsafeAddressOf(kSecImportItemIdentity)
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 urlCredential:NSURLCredential = NSURLCredential(
identity: identityApp,
certificates: myCerts as [AnyObject],
persistence: NSURLCredentialPersistence.Permanent)
challenge.sender!.useCredential(urlCredential ,forAuthenticationChallenge:challenge)
}
}
Any example for client authentication in NSURLSession will be helpful.
Thanks in advance.
Following are the code changes which works fine with Self Signed SSL certificate
func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
if challenge.protectionSpace.authenticationMethod == (NSURLAuthenticationMethodServerTrust) {
let serverTrust:SecTrustRef = challenge.protectionSpace.serverTrust!
let certificate: SecCertificateRef = SecTrustGetCertificateAtIndex(serverTrust, 0)!
let remoteCertificateData = CFBridgingRetain(SecCertificateCopyData(certificate))!
let cerPath: String = NSBundle.mainBundle().pathForResource("xyz.com", ofType: "cer")!
let localCertificateData = NSData(contentsOfFile:cerPath)!
if (remoteCertificateData.isEqualToData(localCertificateData) == true) {
let credential:NSURLCredential = NSURLCredential(forTrust: serverTrust)
challenge.sender?.useCredential(credential, forAuthenticationChallenge: challenge)
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!))
} else {
completionHandler(NSURLSessionAuthChallengeDisposition.CancelAuthenticationChallenge, nil)
}
}
else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate
{
let path: String = NSBundle.mainBundle().pathForResource("client", ofType: "p12")!
let PKCS12Data = NSData(contentsOfFile:path)!
let identityAndTrust:IdentityAndTrust = self.extractIdentity(PKCS12Data);
let urlCredential:NSURLCredential = NSURLCredential(
identity: identityAndTrust.identityRef,
certificates: identityAndTrust.certArray as? [AnyObject],
persistence: NSURLCredentialPersistence.ForSession);
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, urlCredential);
}
else
{
completionHandler(NSURLSessionAuthChallengeDisposition.CancelAuthenticationChallenge, nil);
}
}
struct IdentityAndTrust {
var identityRef:SecIdentityRef
var trust:SecTrustRef
var certArray:AnyObject
}
func extractIdentity(certData:NSData) -> IdentityAndTrust {
var identityAndTrust:IdentityAndTrust!
var securityError:OSStatus = errSecSuccess
let path: String = NSBundle.mainBundle().pathForResource("client", ofType: "p12")!
let PKCS12Data = NSData(contentsOfFile:path)!
let key : NSString = kSecImportExportPassphrase as NSString
let options : NSDictionary = [key : "xyz"]
//create variable for holding security information
//var privateKeyRef: SecKeyRef? = nil
var items : CFArray?
securityError = SecPKCS12Import(PKCS12Data, options, &items)
if securityError == errSecSuccess {
let certItems:CFArray = items 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!;
print("\(identityPointer) :::: \(secIdentityRef)")
// grab the trust
let trustPointer:AnyObject? = certEntry["trust"];
let trustRef:SecTrustRef = trustPointer as! SecTrustRef;
print("\(trustPointer) :::: \(trustRef)")
// grab the cert
let chainPointer:AnyObject? = certEntry["chain"];
identityAndTrust = IdentityAndTrust(identityRef: secIdentityRef, trust: trustRef, certArray: chainPointer!);
}
}
return identityAndTrust;
}
Changes done in the info.plist file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>amazonaws.com.cn</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSThirdPartyExceptionMinimumTLSVersion</key>
<string>TLSv1.0</string>
</dict>
<key>amazonaws.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSThirdPartyExceptionMinimumTLSVersion</key>
<string>TLSv1.0</string>
</dict>
<key>xyz.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSTemporaryExceptionMinimumTLSVersion</key>
<string>TLSv1.2</string>
<key>NSRequiresCertificateTransparency</key>
<false/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
</dict>
</plist>
For someone who interested, this is the small modification from the code of @Karlos, which is compatible with Swift 3.0:
fileprivate func extractCredential(_ certData: Data) -> Credential? {
// REF.: itx | EB418667-6ECD-4D22-B31E-0BB4000849A2
var credential: Credential!
let key = kSecImportExportPassphrase as NSString
let importPasswordOption:NSDictionary = [key as NSString:cerPassword]
var items: CFArray?
let securityError = SecPKCS12Import(certData as CFData, importPasswordOption, &items)
if securityError == errSecSuccess {
let certItems = items as CFArray!
let certItemsArray = (certItems as! NSArray)
let dict = certItemsArray.first
guard let certEntry = certItemsArray.firstObject as? [String:AnyObject] else { fatalError() }
// Identity
let identityPointer = certEntry["identity"];
let secIdentityRef = identityPointer as! SecIdentity!;
// Trust
let trustPointer = certEntry["trust"];
let trustRef = trustPointer as! SecTrust;
// Certificates
let chainPointer = certEntry["chain"];
credential = Credential(identityRef: secIdentityRef!, trust: trustRef, certArray: chainPointer!);
}
return credential;
}