How to accept a self-signed SSL certificate with URLSession and Combine

I'm using URLSession and Combine to monitor and control a device on my internal network. One URL to retrieve JSON data is https://ip_address/api/v1/items

The device has a self-signed certificate so I'm getting an error that the certificate is invalid. I need my code to ignore the certificate and retrieve the data.

Is there a good solution to accept this self-signed certificate with Combine and URLSession?

Here is the code that makes the HTTPS get request.

Code Block
    func sendControllerGetRequest(uri: String) -> PassthroughSubject<Data, Never> {
        getRequestPublisher = PassthroughSubject<Data, Never>()
        var urlRequest = URLRequest(url: URL(string: self.baseURI+uri)!)
        urlRequest.httpMethod = "GET"
        urlRequest.setValue("Bearer \(self.directorBearerToken)", forHTTPHeaderField: "Authorization")
        getRequestCancellable = URLSession.shared.dataTaskPublisher(for: urlRequest)
            .map{ $0.data }
            .sink(receiveCompletion: { completion in
                switch completion {
                case .failure(let error):
                    print("sendControllerGetRequest error")
                    print(error)
                case .finished:
                    print("sendControllerGetRequest finished")
                    break
                }
            }, receiveValue: { requestDetails in
               print(String(data: requestDetails, encoding: .utf8)!)
                print("sendControllerGetRequest()")
                self.getRequestPublisher.send(requestDetails)
                self.getRequestPublisher.send(completion: .finished)
            }
            )
        return getRequestPublisher
    }


Answered by tommym in 647823022
Here is a solution.

urlSession is called twice. The second time challenge.protectionSpace.serverTrust is nil. I'm still trying to fully understand this so any help is appreciated. This implementation is not secure and should only be used for testing.

This code has been built and tested with Swift 5, iOS 14 running on the simulator in Xcode 12.2.

Code Block
import Foundation
import Combine
class myClass: NSObject, URLSessionDelegate {
var ip: String
var directorBearerToken: String
var baseURI: String
private var getRequestCancellable: AnyCancellable?
private var getRequestPublisher = PassthroughSubject<Data, Never>()
init(ip: String, token: String) {
self.ip = ip
self.directorBearerToken = token
self.baseURI = "https://\(ip)"
}
// Get a JSON data with all the items
func getAllItemInfo() -> PassthroughSubject<Data, Never> {
let test = sendControllerGetRequest(uri: "/api/v1/items")
return test
}
func sendControllerGetRequest(uri: String) -> PassthroughSubject<Data, Never> {
print("sendControllerGetRequest")
getRequestPublisher = PassthroughSubject<Data, Never>()
var urlRequest = URLRequest(url: URL(string: self.baseURI+uri)!)
urlRequest.httpMethod = "GET"
urlRequest.setValue("Bearer \(self.directorBearerToken)", forHTTPHeaderField: "Authorization")
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)
getRequestCancellable = session.dataTaskPublisher(for: urlRequest)
.map{ $0.data }
.sink(receiveCompletion: { completion in
switch completion {
case .failure(let error):
print("sendControllerGetRequest error")
print(error)
case .finished:
print("sendControllerGetRequest finished")
break
}
}, receiveValue: { requestDetails in
print(String(data: requestDetails, encoding: .utf8)!)
print("sendControllerGetRequest()")
self.getRequestPublisher.send(requestDetails)
self.getRequestPublisher.send(completion: .finished)
}
)
return getRequestPublisher
}
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if challenge.protectionSpace.serverTrust == nil {
completionHandler(.useCredential, nil)
} else {
let trust: SecTrust = challenge.protectionSpace.serverTrust!
let credential = URLCredential(trust: trust)
completionHandler(.useCredential, credential)
}
}
}


Accepted Answer
Here is a solution.

urlSession is called twice. The second time challenge.protectionSpace.serverTrust is nil. I'm still trying to fully understand this so any help is appreciated. This implementation is not secure and should only be used for testing.

This code has been built and tested with Swift 5, iOS 14 running on the simulator in Xcode 12.2.

Code Block
import Foundation
import Combine
class myClass: NSObject, URLSessionDelegate {
var ip: String
var directorBearerToken: String
var baseURI: String
private var getRequestCancellable: AnyCancellable?
private var getRequestPublisher = PassthroughSubject<Data, Never>()
init(ip: String, token: String) {
self.ip = ip
self.directorBearerToken = token
self.baseURI = "https://\(ip)"
}
// Get a JSON data with all the items
func getAllItemInfo() -> PassthroughSubject<Data, Never> {
let test = sendControllerGetRequest(uri: "/api/v1/items")
return test
}
func sendControllerGetRequest(uri: String) -> PassthroughSubject<Data, Never> {
print("sendControllerGetRequest")
getRequestPublisher = PassthroughSubject<Data, Never>()
var urlRequest = URLRequest(url: URL(string: self.baseURI+uri)!)
urlRequest.httpMethod = "GET"
urlRequest.setValue("Bearer \(self.directorBearerToken)", forHTTPHeaderField: "Authorization")
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)
getRequestCancellable = session.dataTaskPublisher(for: urlRequest)
.map{ $0.data }
.sink(receiveCompletion: { completion in
switch completion {
case .failure(let error):
print("sendControllerGetRequest error")
print(error)
case .finished:
print("sendControllerGetRequest finished")
break
}
}, receiveValue: { requestDetails in
print(String(data: requestDetails, encoding: .utf8)!)
print("sendControllerGetRequest()")
self.getRequestPublisher.send(requestDetails)
self.getRequestPublisher.send(completion: .finished)
}
)
return getRequestPublisher
}
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if challenge.protectionSpace.serverTrust == nil {
completionHandler(.useCredential, nil)
} else {
let trust: SecTrust = challenge.protectionSpace.serverTrust!
let credential = URLCredential(trust: trust)
completionHandler(.useCredential, credential)
}
}
}


How to accept a self-signed SSL certificate with URLSession and Combine
 
 
Q