I am trying to consume web services for my iOS app over https. The web server uses a self signed certificate.
When consuming the web service, I get the error “certificate is Invalid”.
FAILURE: Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “portal” which could put your confidential information at risk."
I know the best practise is to fix this at the server side to enable a trusted root CA. But as this is a temporary development environment, we are using a self signed certificate. Since this is ATS issue, I have edited ATS in my info.plist as below.
<?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>devportal</key>
<dict>
<key>NSTemporaryExceptionMinimumTLSVersion</key>
<string>TLSv1.2</string>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
</dict>
</plist>
As the NSException domains doesn’t work with IP and port number, I have created a host entry in my etc/hosts file for the web server IP and consuming it like https://devportal:8443/rest/login instead of consuming it as https://192.22.xx.xxx:8443/rest/login
I have followed alamofire documentation on server trust policies, edited ATS to allow exception domains but nothing worked out for me. I have spent over 3 days on this issue. Am I missing something? Does anybody faced a similar issue? Is there any solution for this? Thanks in advance
I am using almofire 4.0, Xcode 8.0. Below is my code.
class LoginService{
private static var Manager: Alamofire.SessionManager = {
let pathToCert = Bundle.main.path(forResource: "192.22.xx.xxx", ofType: "crt") // Downloaded this certificate and have added to my bundle
let localCertificate:NSData = NSData(contentsOfFile: pathToCert!)!
// Create the server trust policies
let serverTrustPolicies: [String: ServerTrustPolicy] = [
"192.22.xx.xxx": .pinCertificates(
certificates: [SecCertificateCreateWithData(nil, localCertificate)!],
validateCertificateChain: true,
validateHost: true
),
"devportal:8443": .disableEvaluation
]
// Create custom manager
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
let manager = Alamofire.SessionManager(
configuration: URLSessionConfiguration.default,
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
)
return manager
}()
/**
Calls the Login Web Service to authenticate the user
*/
public func login(username:String, password: String){
let parameters = [
"username": "TEST",
"password": "PASSWORD",
]
let header: HTTPHeaders = ["Accept": "application/json"]
LoginService.Manager.request("https://devportal:8443/rest/login", method: .post, parameters: parameters, encoding: JSONEncoding(options: []),headers :header).responseJSON { response in
debugPrint(response)
if let json = response.result.value {
print("JSON: \(json)")
}
}
}
}
I modified my code like below and it worked. I referred Swift: How to Make Https Request Using Server SSL Certificate for fixing this issue.
class LoginService{
private static var Manager: Alamofire.SessionManager = {
// Create the server trust policies
let serverTrustPolicies: [String: ServerTrustPolicy] = [
"devportal:8443": .disableEvaluation
]
// Create custom manager
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
let manager = Alamofire.SessionManager(
configuration: URLSessionConfiguration.default,
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
)
return manager
}()
/**
Calls the Login Web Service to authenticate the user
*/
public func login(username:String, password: String){
// Handle Authentication challenge
let delegate: Alamofire.SessionDelegate = LoginService.Manager.delegate
delegate.sessionDidReceiveChallenge = { session, challenge in
var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
var credential: URLCredential?
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
disposition = URLSession.AuthChallengeDisposition.useCredential
credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
} else {
if challenge.previousFailureCount > 0 {
disposition = .cancelAuthenticationChallenge
} else {
credential = LoginService.Manager.session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace)
if credential != nil {
disposition = .useCredential
}
}
}
return (disposition, credential)
}
//Web service Request
let parameters = [
"username": "TEST",
"password": "PASSWORD",
]
let header: HTTPHeaders = ["Accept": "application/json"]
LoginService.Manager.request("https://devportal:8443/rest/login", method: .post, parameters: parameters, encoding: JSONEncoding(options: []),headers :header).responseJSON { response in
debugPrint(response)
if let json = response.result.value {
print("JSON: \(json)")
}
}
}
}
You should also configure your plist as below
<?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>devportal</key>
<dict>
<key>NSTemporaryExceptionMinimumTLSVersion</key>
<string>TLSv1.2</string>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
</dict>
</plist>
Do not enter IP or port numbers in your NSExceptiondomains. It won't work. If you are trying to connect to a web server with IP address, map the IP address to a domain by adding a host entry in etc/hosts file in your mac and then use the domain name in NSExceptionDomains
IMPORTANT: Do not use this code in production as this puts your users information at risk, by bypassing auth challenge.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With