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
Usage of Protocols as Array Types and Function Parameters in Swift
iOS 11 - Keyboard Height Is Returning 0 in Keyboard Notification
How to Play Mp3 Audio from Url in iOS Swift
How to Create a Simple Checkbox in iOS
Objective-C 101 (Retain VS Assign) Nsstring
Using Custom Font in a Uiwebview
Wrapping Text in a Uitextview Around a Uiimage Without Coretext
Programmatically Add Centerx/Centery Constraints
How to Switch to Different Storyboard for iPhone 5
Find the Tangent of a Point on a Cubic Bezier Curve
Distancefromlocation - Calculate Distance Between Two Points
What Is "Error in _Connection_Block_Invoke_2: Connection Interrupted" in iOS
How to Set Primary Key in Swift for Realm Model