HTTPS requests implemented with Certificate pinning in Swift using AlamoFire fails - ios

We are trying to implement ssl in our iOS application to connect to Rest webservices running in Tomcat Webserver.
Facing following issue when clientAuth is set to true in tomcat configuration :
2018-05-08 09:28:08.442409+0530 [925:337357] XPC connection interrupted
2018-05-08 09:29:26.481465+0530 [925:336959] [Common] _BSMachError: port 9d6f; (os/kern) invalid capability (0x14) "Unable to insert COPY_SEND"
2018-05-08 09:29:26.485693+0530 [925:336959] [Common] _BSMachError: port 9d6f; (os/kern) invalid name (0xf) "Unable to deallocate send right"
2018-05-08 09:29:44.930812+0530 [925:337804] [] nw_coretls_read_one_record tls_handshake_process: [-9825]
2018-05-08 09:29:44.970760+0530 [925:337766] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9825)
2018-05-08 09:29:44.989 [Debug] [main] [HttpUtils.swift:351] handleResponse(response:completionHandler:) > Value: FAILURE;
2018-05-08 09:29:44.999 [Error] [main] [HttpUtils.swift:363] checkRestResponseErrorAndGetUserUnderstandableError(error:completionHandler:) > Error Domain=NSURLErrorDomain Code=-1205 "The server “example.com” did not accept the certificate." UserInfo={NSLocalizedDescription=The server “example.com” did not accept the certificate., _kCFStreamErrorDomainKey=3, NSUnderlyingError=0x16e35930 {Error Domain=kCFErrorDomainCFNetwork Code=-1205 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=1, _kCFNetworkCFStreamSSLErrorOriginalValue=-9825, _kCFStreamErrorCodeKey=-9825, _kCFStreamErrorDomainKey=3, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x16e2ab90>, kCFStreamPropertySSLPeerCertificates=(
"<cert(0x17337200) s: example.com i: Go Daddy Secure Certificate Authority - G2>",
"<cert(0x172e0c00) s: Go Daddy Secure Certificate Authority - G2 i: Go Daddy Root Certificate Authority - G2>",
"<cert(0x172abe00) s: Go Daddy Root Certificate Authority - G2 i: Go Daddy Root Certificate Authority - G2>"
)}}, _kCFStreamErrorCodeKey=-9825, NSErrorFailingURLStringKey=https://example.com:8443/myserver/rest/myresource/servicepath, NSErrorPeerCertificateChainKey=(
"<cert(0x17337200) s: example.com i: Go Daddy Secure Certificate Authority - G2>",
"<cert(0x172e0c00) s: Go Daddy Secure Certificate Authority - G2 i: Go Daddy Root Certificate Authority - G2>",
"<cert(0x172abe00) s: Go Daddy Root Certificate Authority - G2 i: Go Daddy Root Certificate Authority - G2>"
), NSErrorClientCertificateStateKey=1, NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x16e2ab90>, NSErrorFailingURLKey=https://example.com:8443/myserver/rest/myresource/servicepath}
Here is a code snippet that shows how we create AlamoFire Session manager:
import Foundation
import Alamofire
class HttpUtils {
static let utils = HttpUtils()
var alamoFireSSLManager : SessionManager?
public static func getJSONRequestWithBody(url: String, targetViewController: UIViewController?, params : Dictionary<String, String>,
handler: #escaping RiderRideRestClient.responseJSONCompletionHandler){
let isNetworkAvailable = Reachability.isConnectedToNetwork()
if isNetworkAvailable {
utils.createAlamoFireManager()
utils.alamoFireSSLManager!
.request(url, method: .get, parameters: params, encoding: URLEncoding.methodDependent, headers: nil)
.responseJSON(completionHandler: {
(response) in
handleResponse(response: response, completionHandler: handler)
})
}
else {
handler(nil, Errors.NetworkConnectionNotAvailableError)
}
}
func createAlamoFireManager() {
if (HttpUtils.utils.alamoFireSSLManager == nil) {
let mydomainCertificates = getCertificates(filename: "mydomaincertificate", type: "cer")
let mydomainTrustPolicy = ServerTrustPolicy.pinCertificates(
certificates: mydomainCertificates,
validateCertificateChain: true,
validateHost: true)
let sub2DomainCertificates = getCertificates(filename: "sub2domaincertificate", type: "cer")
let sub2DomainTrustPolicy = ServerTrustPolicy.pinCertificates(
certificates: sub2DomainCertificates,
validateCertificateChain: true,
validateHost: true)
var serverTrustPolicies = [String : ServerTrustPolicy] ()
serverTrustPolicies[example.com] = mydomainTrustPolicy
serverTrustPolicies[sub2.example.com] = sub2DomainTrustPolicy
serverTrustPolicies[sub3.example.com] = ServerTrustPolicy.disableEvaluation
HttpUtils.utils.alamoFireSSLManager = SessionManager(configuration: URLSessionConfiguration.default,
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies))
}
}
func getCertificates(filename : String, type : String) -> [SecCertificate] {
let url = Bundle.main.url(forResource: filename, withExtension: type)!
let localCertificate = try! Data(contentsOf: url) as CFData
guard let certificate = SecCertificateCreateWithData(nil, localCertificate)
else { return [] }
return [certificate]
}
}
I tried by adding following to "App Transport Security Settings" of info.plist :
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>sub2.example.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
<key>example.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
</dict>
Then I got following error :
2018-05-08 12:36:26.761020+0530[82215:62448515] CredStore - copyIdentPrefs - Error copying Identity cred. Error=-25300, query={
class = idnt;
labl = "https://example.com:8443/";
"r_Ref" = 1;
}
2018-05-08 12:36:26.834384+0530[82215:62448514] [BoringSSL] Function boringssl_session_handshake_error_print: line 3108 boringssl ctx 0x105767600: 4505905920:error:10000412:SSL routines:OPENSSL_internal:SSLV3_ALERT_BAD_CERTIFICATE:/BuildRoot/Library/Caches/com.apple.xbs/Sources/boringssl/boringssl-109.20.5/ssl/tls_record.c:547:SSL alert number 42
2018-05-08 12:36:26.838433+0530[82215:62448514] TIC TCP Conn Failed [1:0x1c017b900]: 3:-9802 Err(-9802)
2018-05-08 12:36:27.044962+0530[82215:62447873] CredStore - copyIdentPrefs - Error copying Identity cred. Error=-25300, query={
class = idnt;
labl = "https://example.com:8443/";
"r_Ref" = 1;
}
2018-05-08 12:36:27.112478+0530[82215:62448515] TIC TCP Conn Failed [2:0x1c417e240]: 3:-9802 Err(-9802)
2018-05-08 12:36:27.179865+0530[82215:62448515] TIC TCP Conn Failed [3:0x1c417dac0]: 3:-9800 Err(-9800)
2018-05-08 12:36:27.180800+0530[82215:62448515] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9800)
2018-05-08 12:36:27.180898+0530[82215:62448515] Task .<1> HTTP load failed (error code: -1200 [3:-9800])
2018-05-08 12:36:27.182153+0530[82215:62448514] Task .<1> finished with error - code: -1200
2018-05-08 12:36:27.204 [Error] [main] Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={_kCFStreamErrorCodeKey=-9800, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, NSUnderlyingError=0x1c025d2b0 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9800, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9800}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://example.com:8443/myserver/rest/myresource/servicepath, NSErrorFailingURLStringKey=https://example.com:8443/myserver/rest/myresource/servicepath, _kCFStreamErrorDomainKey=3}
I generated the certificate using following command and made sure this certificate is applied to my target in Xcode:
openssl s_client -connect example.com:8443 -servername example.com < /dev/null | openssl x509 -outform DER > mydomaincertificate.cer
I have checked several SO posts to understand what's wrong, but none of the answers given in other posts worked. What am I doing wrong?

Apparently, client certificates are not supported by AlamoFire! Hence this error when clientAuth is set to true in tomcat configuration.

Related

Not able to upload Images on to Google Firebase Storage using iOS Swift API

Earlier i was able to upload Images onto Google Firebase Strage. but suddenly it's not working.
Below is the code:
private func uploadEmergencyImages(images: [Int: URL], emergencyId: String){
let imagesRef = Storage.storage().reference().child("incidents").child(emergencyId).child("images")
if(!images.isEmpty){
for (key, value) in images {
print("Image to be Uploaded = \(value.absoluteString)")
let imageData = try! Data(contentsOf: value)
// let image = UIImage(data: imageData)
let metaData = StorageMetadata()
metaData.contentType = "image/png"
imagesRef.child("Image_\(key+1).png").putData(imageData, metadata: metaData) { (storageMetaData, storageError) in
if(storageError != nil){
print("Error - While uploading Image:: \(storageError.debugDescription)")
}else{
print("Upload completed...")
}
}
}
}
}
Below is the Error message (NOTE: Here i have masked my firebasePrjName with 'XXX' for this post.)
2018-08-23 20:25:54.758092+0300 XXX[44855:1058133] [AXRun-PID] Client
requesting unsuspension of PID:44870 Name: 2018-08-23
20:25:55.833590+0300 XXX[44855:1057851] [AXRun-PID] Client requesting
unsuspension of PID:44870 Name: 2018-08-23
20:25:57.512286+0300 XXX[44855:1057987] [discovery] errors encountered
while discovering extensions: Error Domain=PlugInKit Code=13 "query
cancelled" UserInfo={NSLocalizedDescription=query cancelled} New
Incident Key = -LKboaL9nTohkCHuYp1U Image to be Uploaded =
file:///Users/macbookpro/Library/Developer/CoreSimulator/Devices/863EEA6A-F1F5-4A6C-B19B-D686D81486F4/data/Containers/Data/Application/F0D6F426-EE5E-4B45-8D1F-3B880C049DF3/tmp/39F05E90-3F8F-4A40-84CB-1931D1E9C7CA.jpeg
2018-08-23 20:26:07.359567+0300 XXX[44855:1058132] TIC TCP Conn Failed
[4:0x60400036ffc0]: 3:-9802 Err(-9802) 2018-08-23 20:26:07.360138+0300
XXX[44855:1058132] NSURLSession/NSURLConnection HTTP load failed
(kCFStreamErrorDomainSSL, -9802) 2018-08-23 20:26:07.360235+0300
XXX[44855:1058132] Task .<1>
HTTP load failed (error code: -1200 [3:-9802]) 2018-08-23
20:26:07.360437+0300 XXX[44855:1057735] Task
.<1> finished with error - code:
-1200 Error - While uploading Image:: Optional(Error Domain=FIRStorageErrorDomain Code=-13000 "An unknown error occurred,
please check the server response."
UserInfo={NSLocalizedRecoverySuggestion=Would you like to connect to
the server anyway?, NSLocalizedDescription=An unknown error occurred,
please check the server response., _kCFStreamErrorDomainKey=3,
NSErrorPeerCertificateChainKey=(
"",
"" ), NSErrorClientCertificateStateKey=0,
NSErrorFailingURLKey=https://firebasestorage.googleapis.com/v0/b/XXX-ots.appspot.com/o/incidents%2F-LKboaL9nTohkCHuYp1U%2Fimages%2FImage_1.png?uploadType=resumable&name=incidents%2F-LKboaL9nTohkCHuYp1U%2Fimages%2FImage_1.png,
NSErrorFailingURLStringKey=https://firebasestorage.googleapis.com/v0/b/XXX-ots.appspot.com/o/incidents%2F-LKboaL9nTohkCHuYp1U%2Fimages%2FImage_1.png?uploadType=resumable&name=incidents%2F-LKboaL9nTohkCHuYp1U%2Fimages%2FImage_1.png,
NSUnderlyingError=0x600000647290 {Error Domain=kCFErrorDomainCFNetwork
Code=-1200 "(null)"
UserInfo={_kCFStreamPropertySSLClientCertificateState=0,
kCFStreamPropertySSLPeerTrust=,
_kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
"",
"" )}}, _kCFStreamErrorCodeKey=-9802,
object=incidents/-LKboaL9nTohkCHuYp1U/images/Image_1.png,
ResponseErrorDomain=NSURLErrorDomain, bucket=XXX-ots.appspot.com,
NSURLErrorFailingURLPeerTrustErrorKey=,
ResponseErrorCode=-1200})
I had tried to modify my info.plist file as below also, but it did not work:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Any lead is highly appreciated.
This is working for me (swift 4 Code)
let storageRef = DBProvider.Instance.imageStorageRef.child(user!.uid) // Put Here your Storage Referance
let imageData = UIImageJPEGRepresentation(youimagehere, 1.0) // put your image Here
storageRef.putData(imageData!, metadata: nil, completion: { (metadata, error) in
if error == nil
{
let path = metadata?.downloadURL()?.absoluteString
print(path)
}
})
Make sure you have enabled firebase storage read, write Permission from your Firebase project.

Alamofire/Moya SSL request fails with ATS failed system trust. System Trust failed for X

I'm trying to make a https request with SSL and a self signed certificate through Alamofire with RxMoya, but it keeps giving me ATS failed system trust.
I've searched in many places for solutions, but I can't figure this out yet. I had a .crt certificate and converted to .der and put it on my main bundle Here's the Stacktrace:
2017-12-07 13:01:05.918360+0100 SmartBackpackerApp[86030:5429201] ATS failed system trust
2017-12-07 13:01:05.919271+0100 SmartBackpackerApp[86030:5429201] System Trust failed for [4:0x60000016b4c0]
2017-12-07 13:01:05.920438+0100 SmartBackpackerApp[86030:5429201] TIC SSL Trust Error [4:0x60000016b4c0]: 3:0
2017-12-07 13:01:05.921390+0100 SmartBackpackerApp[86030:5429201] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
2017-12-07 13:01:05.922103+0100 SmartBackpackerApp[86030:5429201] Task <F73E31A0-8AC4-40B4-B80C-05182D3E5647>.<1> HTTP load failed (error code: -1200 [3:-9802])
2017-12-07 13:01:05.923528+0100 SmartBackpackerApp[86030:5429201] Task <F73E31A0-8AC4-40B4-B80C-05182D3E5647>.<1> finished with error - code: -1200
Moya_Logger: [07/12/2017 13:01:05] Response: Received empty network response for airlines("Easy Jet").
2017-12-07 13:01:05.945: SBAirlinesService.swift:39 (getAirlineInfo(name:)) -> Event error(underlying(Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x604000307980>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, NSErrorPeerCertificateChainKey=(
"<cert(0x7fca6a853800) s: api.smartbackpacker.com i: api.smartbackpacker.com>"
), NSUnderlyingError=0x600000656080 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x604000307980>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
"<cert(0x7fca6a853800) s: api.smartbackpacker.com i: api.smartbackpacker.com>"
)}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://api.smartbackpackerapp.com/v1/airlines?name=Easy%20Jet, NSErrorFailingURLStringKey=https://api.smartbackpackerapp.com/v1/airlines?name=Easy%20Jet, NSErrorClientCertificateStateKey=0}, nil))
Here's the code I'm using to make the request :
let serverTrustPolicies = ["api.smartbackpackerapp.com": ServerTrustPolicy.pinCertificates(certificates: ServerTrustPolicy.certificates(), validateCertificateChain: false, validateHost: true)]
self.manager = Manager(configuration: URLSessionConfiguration.default, serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies))
self.manager.delegate.sessionDidReceiveChallenge = { [weak self] session, challenge in
var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
var credential: URLCredential?
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust, let trust = challenge.protectionSpace.serverTrust {
disposition = URLSession.AuthChallengeDisposition.useCredential
credential = URLCredential(trust: trust)
} else {
if challenge.previousFailureCount > 0 {
disposition = .cancelAuthenticationChallenge
} else {
credential = self?.manager.session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace)
if credential != nil {
disposition = .useCredential
}
}
}
return (disposition, credential)
}
}
provider = RxMoyaProvider<SBApi>(manager: self.manager, plugins: plugins)
return provider.request(.airlines(name: name))
.debug()
.filterSuccessfulStatusCodes()
.mapObject(SBAirline.self)
.asObservable()
In my Info.plist file:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>https://api.smartbackpackerapp.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSTemporaryExceptionMinimumTLSVersion</key>
<string>TLSv1.1</string>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</dict>
</dict>
OMG, I had the wrong domain on the Info.plist, it is without the https://
Lost a few hours with this. :(

Swift 3 How to validate server certificate using SSL Pinning and AlamoFire?

I'm writing an app in swift 3 that needs to talk to my server. I have the full certificate chain in der and crt format which I am the CA for(Not to be confused with self signed). How do I use this in my app to validate my server? Below is my rest call and response
Rest Call:
var request = URLRequest(url: URL(string: "https://myserver/login")!)
request.addValue("Content-Type", forHTTPHeaderField: "application/json")
request.httpMethod = "GET"
let session = URLSession.shared
session.dataTask(with: request) {data, response, err in
print("=========================DATA===============================")
if data != nil {
print(data!)
}
print("=========================RESPONSE===============================")
if response != nil {
print(response!)
}
print("=========================ERR===============================")
if err != nil {
print(err!)
}
}.resume()
Output:
=========================DATA===============================
=========================RESPONSE===============================
=========================ERR===============================
Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x60800011f020>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, NSErrorPeerCertificateChainKey=(
"<cert(0x7fae4803d200) s: myserver i: MySubCA>",
"<cert(0x7fae48047000) s: MySubCA i: MyRootCA>",
"<cert(0x7fae48044600) s: MyRootCA i: MyRootCA>"
), NSUnderlyingError=0x60800005a040 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x60800011f020>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
"<cert(0x7fae4803d200) s: myserver i: MySubCA>",
"<cert(0x7fae48047000) s: MySubCA i: MyRootCA>",
"<cert(0x7fae48044600) s: MyRootCA i: MyRootCA>"
)}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://myserver/login, NSErrorFailingURLStringKey=https://myserver/login, NSErrorClientCertificateStateKey=0}
I solved it pretty simply leveraging an online blog, AlamoFire and openssl.
I used AlamoFire for the networking on iOS.
I used an article about SSL pinning on iOS to get on the right direction.
I used openssl to convert my cert to der format.
Der conversion through openssl.
openssl x509 -in cert.crt -out cert.der -outform DER
You will need to add the der formatted cert to your app bundle.
Swift 3 implementation
// Your hostname and endpoint
let hostname = "YOUR_HOST_NAME"
let endpoint = "YOUR_ENDPOINT"
let cert = "YOUR_CERT" // e.g. for cert.der, this should just be "cert"
// Set up certificates
let pathToCert = Bundle.main.path(forResource: cert, ofType: "der")
let localCertificate = NSData(contentsOfFile: pathToCert!)
let certificates = [SecCertificateCreateWithData(nil, localCertificate!)!]
// Configure the trust policy manager
let serverTrustPolicy = ServerTrustPolicy.pinCertificates(
certificates: certificates,
validateCertificateChain: true,
validateHost: true
)
let serverTrustPolicies = [hostname: serverTrustPolicy]
let serverTrustPolicyManager = ServerTrustPolicyManager(policies: serverTrustPolicies)
// Configure session manager with trust policy
afManager = SessionManager(
configuration: URLSessionConfiguration.default,
serverTrustPolicyManager: serverTrustPolicyManager
)
afManager.request(endpoint, method: .get).responseJSON { response in
debugPrint("All Response Info: \(response)")
}

how to access quickblox content swift2?

I am trying to upload a file image to the content but i get this error.
error: Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has
occurred and a secure connection to the server cannot be made."
UserInfo={NSLocalizedDescription=An SSL error has occurred and a
secure connection to the server cannot be made.,
NSLocalizedRecoverySuggestion=Would you like to connect to the server
anyway?, _kCFStreamErrorDomainKey=3, NSUnderlyingError=0x162007a0
{Error Domain=kCFErrorDomainCFNetwork Code=-1200 "An SSL error has
occurred and a secure connection to the server cannot be made."
UserInfo={NSLocalizedDescription=An SSL error has occurred and a
secure connection to the server cannot be made.,
_kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamPropertySSLClientCertificateState=0, NSLocalizedRecoverySuggestion=Would you like to connect to the server
anyway?, _kCFStreamErrorCodeKey=-9802,
kCFStreamPropertySSLPeerTrust=,
_kCFStreamErrorDomainKey=3, kCFStreamPropertySSLPeerCertificates={type = immutable, count = 3, values = ( 0 :
1 : 2 : )},
NSErrorFailingURLStringKey=https://qbprod.s3.amazonaws.com/,
NSErrorFailingURLKey=https://qbprod.s3.amazonaws.com/}},
_kCFStreamErrorCodeKey=-9802, NSErrorFailingURLStringKey=https://qbprod.s3.amazonaws.com/,
NSErrorPeerCertificateChainKey={type
= immutable, count = 3, values = ( 0 : 1 : 2 :
)},
NSErrorClientCertificateStateKey=0,
NSURLErrorFailingURLPeerTrustErrorKey=,
NSErrorFailingURLKey=https://qbprod.s3.amazonaws.com/}
I used the below code to upload file and it worked perfectly.
let imageData: NSData = UIImagePNGRepresentation(image)!
QBRequest.TUploadFile(imageData,
fileName: "testimage.png",
contentType: "image/png",
isPublic: false,
successBlock: {(response: QBResponse!, uploadedBlob: QBCBlob!) in
/** You get success with updated blob **/
}, statusBlock: {(request: QBRequest?, status: QBRequestStatus?) in
},
errorBlock: {(response: QBResponse!) in
})

Can't connect to https://test.salesforce.com with ios9

With iOS 9's improved security we are not able to connect to https://test.salesforce.com
We get
NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9824)
which based on SecureTransport.h is related to a handshake failure
errSSLPeerHandshakeFail = -9824, /* handshake failure */
We can disable security and still connect (using NSAllowsArbitraryLoads) but we would prefer to use the new more secure ios features.
We tried making an exception just for Salesforce but still get same error
<key>NSExceptionDomains</key>
<dict>
<key>salesforce.com</key>
<dict>
<key>NSExceptionMinimumTLSVersion</key>
<string>TLSv1.0</string>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
edit(to add a question): What is the best way to make requests from ios apps to https://test.salesforce.com as securely as possible? (Do I need to wait for Salesforce to update their certificates? Or is there something more under my control?)
I ran the nscurl command line utility on the OAuth 2.0 endpoint (ROPC flow, sandbox environment):
nscurl --ats-diagnostics --verbose https://test.salesforce.com/services/oauth2/token
TL; DR:
I found out that the most secure setup that will PASS is the following:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>salesforce.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
</dict>
</dict>
</dict>
(No need to lower the minimum required TLS version. Also, Salesforce is dropping support for TLS 1.0)
The full results of the nscurl diagnostic:
Starting ATS Diagnostics
Configuring ATS Info.plist keys and displaying the result of HTTPS loads to https://test.salesforce.com/services/oauth2/token.
A test will "PASS" if URLSession:task:didCompleteWithError: returns a nil error.
================================================================================
Default ATS Secure Connection
---
ATS Default Connection
ATS Dictionary:
{
}
2016-06-17 10:49:21.533 nscurl[975:53055] CFNetwork SSLHandshake failed (-9824)
2016-06-17 10:49:21.533 nscurl[975:53055] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9824)
Result : FAIL
Error : Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={_kCFStreamErrorCodeKey=-9824, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, NSUnderlyingError=0x7fd67d100230 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9824, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9824}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://test.salesforce.com/services/oauth2/token, NSErrorFailingURLStringKey=https://test.salesforce.com/services/oauth2/token, _kCFStreamErrorDomainKey=3}
---
================================================================================
Allowing Arbitrary Loads
---
Allow All Loads
ATS Dictionary:
{
NSAllowsArbitraryLoads = true;
}
Result : PASS
---
================================================================================
Configuring TLS exceptions for test.salesforce.com
---
TLSv1.2
ATS Dictionary:
{
NSExceptionDomains = {
"test.salesforce.com" = {
NSExceptionMinimumTLSVersion = "TLSv1.2";
};
};
}
2016-06-17 10:49:21.760 nscurl[975:53055] CFNetwork SSLHandshake failed (-9824)
2016-06-17 10:49:21.760 nscurl[975:53055] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9824)
Result : FAIL
Error : Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={_kCFStreamErrorCodeKey=-9824, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, NSUnderlyingError=0x7fd67d000aa0 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9824, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9824}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://test.salesforce.com/services/oauth2/token, NSErrorFailingURLStringKey=https://test.salesforce.com/services/oauth2/token, _kCFStreamErrorDomainKey=3}
---
---
TLSv1.1
ATS Dictionary:
{
NSExceptionDomains = {
"test.salesforce.com" = {
NSExceptionMinimumTLSVersion = "TLSv1.1";
};
};
}
2016-06-17 10:49:21.817 nscurl[975:53055] CFNetwork SSLHandshake failed (-9824)
2016-06-17 10:49:21.817 nscurl[975:53055] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9824)
Result : FAIL
Error : Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={_kCFStreamErrorCodeKey=-9824, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, NSUnderlyingError=0x7fd67b49bf10 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9824, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9824}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://test.salesforce.com/services/oauth2/token, NSErrorFailingURLStringKey=https://test.salesforce.com/services/oauth2/token, _kCFStreamErrorDomainKey=3}
---
---
TLSv1.0
ATS Dictionary:
{
NSExceptionDomains = {
"test.salesforce.com" = {
NSExceptionMinimumTLSVersion = "TLSv1.0";
};
};
}
2016-06-17 10:49:21.878 nscurl[975:53055] CFNetwork SSLHandshake failed (-9824)
2016-06-17 10:49:21.879 nscurl[975:53055] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9824)
Result : FAIL
Error : Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={_kCFStreamErrorCodeKey=-9824, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, NSUnderlyingError=0x7fd67d1002c0 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9824, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9824}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://test.salesforce.com/services/oauth2/token, NSErrorFailingURLStringKey=https://test.salesforce.com/services/oauth2/token, _kCFStreamErrorDomainKey=3}
---
================================================================================
Configuring PFS exceptions for test.salesforce.com
---
Disabling Perfect Forward Secrecy
ATS Dictionary:
{
NSExceptionDomains = {
"test.salesforce.com" = {
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
================================================================================
Configuring PFS exceptions and allowing insecure HTTP for test.salesforce.com
---
Disabling Perfect Forward Secrecy and Allowing Insecure HTTP
ATS Dictionary:
{
NSExceptionDomains = {
"test.salesforce.com" = {
NSExceptionAllowsInsecureHTTPLoads = true;
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
================================================================================
Configuring TLS exceptions with PFS disabled for test.salesforce.com
---
TLSv1.2 with PFS disabled
ATS Dictionary:
{
NSExceptionDomains = {
"test.salesforce.com" = {
NSExceptionMinimumTLSVersion = "TLSv1.2";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
---
TLSv1.1 with PFS disabled
ATS Dictionary:
{
NSExceptionDomains = {
"test.salesforce.com" = {
NSExceptionMinimumTLSVersion = "TLSv1.1";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
---
TLSv1.0 with PFS disabled
ATS Dictionary:
{
NSExceptionDomains = {
"test.salesforce.com" = {
NSExceptionMinimumTLSVersion = "TLSv1.0";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
================================================================================
Configuring TLS exceptions with PFS disabled and insecure HTTP allowed for test.salesforce.com
---
TLSv1.2 with PFS disabled and insecure HTTP allowed
ATS Dictionary:
{
NSExceptionDomains = {
"test.salesforce.com" = {
NSExceptionAllowsInsecureHTTPLoads = true;
NSExceptionMinimumTLSVersion = "TLSv1.2";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
---
TLSv1.1 with PFS disabled and insecure HTTP allowed
ATS Dictionary:
{
NSExceptionDomains = {
"test.salesforce.com" = {
NSExceptionAllowsInsecureHTTPLoads = true;
NSExceptionMinimumTLSVersion = "TLSv1.1";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
---
TLSv1.0 with PFS disabled and insecure HTTP allowed
ATS Dictionary:
{
NSExceptionDomains = {
"test.salesforce.com" = {
NSExceptionAllowsInsecureHTTPLoads = true;
NSExceptionMinimumTLSVersion = "TLSv1.0";
NSExceptionRequiresForwardSecrecy = false;
};
};
}
Result : PASS
---
================================================================================
To continue down the path of making an exception, try adding force.com to the exception list (instead of only salesforce.com)
So... add this as another exception key:
<key>NSExceptionDomains</key>
<dict>
<key>force.com</key>
<dict>
<key>NSExceptionMinimumTLSVersion</key>
<string>TLSv1.0</string>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
I also await a more permanent solution.

Resources