Getting Client Certificate to Work for Mutual Authentication Using Swift 3 and Alamofire 4

Getting client certificate to work for mutual authentication using Swift 3 and Alamofire 4

I was able to get it to work. A few issues got into the way. First, you have to allow IOS to accept self signed certificates. This requires to set up AlamoFire serverTrustPolicy:

let serverTrustPolicies: [String: ServerTrustPolicy] = [
"your-domain.com": .disableEvaluation
]

self.sessionManager = Alamofire.SessionManager(
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
)

From there, you have to override the sessionDidRecieveChallenge to send the client certificate. Because i wanted to use a p12 file I modified some code I found elsewhere (sorry i don't have the source anymore) to make is Swift 3.0 to import the p12 using foundation classes:

import Foundation

public class PKCS12 {
var label:String?
var keyID:Data?
var trust:SecTrust?
var certChain:[SecTrust]?
var identity:SecIdentity?

let securityError:OSStatus

public init(data:Data, password:String) {

//self.securityError = errSecSuccess

var items:CFArray?
let certOptions:NSDictionary = [kSecImportExportPassphrase as NSString:password as NSString]

// import certificate to read its entries
self.securityError = SecPKCS12Import(data as NSData, certOptions, &items);

if securityError == errSecSuccess {
let certItems:Array = (items! as Array)
let dict:Dictionary<String, AnyObject> = certItems.first! as! Dictionary<String, AnyObject>;

self.label = dict[kSecImportItemLabel as String] as? String;
self.keyID = dict[kSecImportItemKeyID as String] as? Data;
self.trust = dict[kSecImportItemTrust as String] as! SecTrust?;
self.certChain = dict[kSecImportItemCertChain as String] as? Array<SecTrust>;
self.identity = dict[kSecImportItemIdentity as String] as! SecIdentity?;
}


}

public convenience init(mainBundleResource:String, resourceType:String, password:String) {
self.init(data: NSData(contentsOfFile: Bundle.main.path(forResource: mainBundleResource, ofType:resourceType)!)! as Data, password: password);
}

public func urlCredential() -> URLCredential {
return URLCredential(
identity: self.identity!,
certificates: self.certChain!,
persistence: URLCredential.Persistence.forSession);

}



}

This will allow me to import the file, and send it back to the client.

let cert = PKCS12.init(mainBundleResource: "cert", resourceType: "p12", password: "password");

self.sessionManager.delegate.sessionDidReceiveChallenge = { session, challenge in
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate {
return (URLSession.AuthChallengeDisposition.useCredential, self.cert.urlCredential());
}
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
return (URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!));
}
return (URLSession.AuthChallengeDisposition.performDefaultHandling, Optional.none);
}

Now you can use the sessionManager to create as many calls as you need to.

As a note, i've also added the following to the info.plist as recomended to get around the new security features in newer iOS features:

<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>your-domain.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>

I hope this helps!

Swift 3 UrlSession with client authentication by certificate

If your getClientCredential() function is being called, then your client is trying to authenticate. If not, then the server logs (such as /var/log/nginx/access.log) might indicate why.

The PKCS12 class in this answer worked for me.

Regarding the keychain, this Apple documentation says

To use digital identities in your own apps, you will need to write code to import them. This typically means reading in a PKCS#12-formatted blob and then importing the contents of the blob into the app's keychain using the function SecPKCS12Import documented in Certificate, Key, and Trust Services Reference.

This way, your new keychain items are created with your app's keychain access group.

Alamofire 5 with self-signed certificate

It's very similar in Alamofire 5 but ServerTrustPolicy has been refactored into a protocol with conforming types for better extensibility. Similar to the answer you linked, you'll need to create a ServerTrustManager for your domain:

let manager = ServerTrustManager(evaluators: ["your.domain.here": DisabledTrustEvaluator()])
let session = Session(serverTrustManager: manager)

Of course, you'll still need to add ATS exceptions for your domains as well.

Additionally, you should never ship code that uses the DisabledTrustEvaluator, as it would allow all invalid TLS connections.



Related Topics



Leave a reply



Submit