Certificate Invalid Issue with Alamofire 4.0
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.
CrazyDeveloper
Updated on June 14, 2022Comments
-
CrazyDeveloper almost 2 years
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)") } } } }
-
PeqNP almost 5 yearsBeautiful! I didn't need to update my plist. Instead I generated a root CA, created leaf certs, generated pems, installed root cert on device, etc. with the instructions provided by Apple at: developer.apple.com/library/archive/qa/qa1948/_index.html. Thank you!