Doh!! I have found it....
I am also receiving a NSURLAuthenticationMethodClientCertificate challenge that was falling into the section for .performDefaultHandling and was causing the error as no certificate was exchanged from my side (although the error messages imply that the problem was the certificate from the accessory).
I needed to create an identity from the certificate and key I used to set up the connection to the accessory, store it in my keychain and then return that as the client credential from the challenge... and it works
Thanks for your help, this security and identity stuff is certainly a long learning curve...
Post
Replies
Boosts
Views
Activity
Sorry that's not working, something strange going on here...
I created a new command line tool in Xcode using that exact same code just replacing https://self-signed.badssl.com with https:192.168.86.200:8444/smarthome/rooms
I also checked that the challenge was NSURLAuthenticationMethodServerTrust
Response is the same as I had before...
will run task
did not run task, error: NSURLErrorDomain / -1202
boringssl_context_handle_fatal_alert(2,072) [C1.1.1:2][0x157e07280] read alert, level: fatal, description: bad certificate
boringssl_session_handshake_incomplete(210) [C1.1.1:2][0x157e07280] SSL library error
boringssl_session_handshake_error_print(44) [C1.1.1:2][0x157e07280] Error: 5770378176:error:10000412:SSL routines:OPENSSL_internal:SSLV3_ALERT_BAD_CERTIFICATE:/AppleInternal/Library/BuildRoots/a8fc4767-fd9e-11ee-8f2e-b26cde007628/Library/Caches/com.apple.xbs/Sources/boringssl/ssl/tls_record.cc:592:SSL alert number 42
nw_protocol_boringssl_handshake_negotiate_proceed(779) [C1.1.1:2][0x157e07280] handshake failed at state 12,288: not completed
Connection 1: received failure notification
Connection 1: failed to connect 3:18,446,744,073,709,541,808, reason 18,446,744,073,709,551,615
Connection 1: encountered error(3:18,446,744,073,709,541,808)
Task <857020EA-0B48-44DF-ABE3-E869745E0696>.<1> HTTP load failed, 0/0 bytes (error code: 18,446,744,073,709,550,414 [3:18,446,744,073,709,541,808])
Task <857020EA-0B48-44DF-ABE3-E869745E0696>.<1> finished with error [18,446,744,073,709,550,414] Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “192.168.86.200” which could put your confidential information at risk." UserInfo={NSErrorFailingURLStringKey=https://192.168.86.200:8444/smarthome/rooms, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <857020EA-0B48-44DF-ABE3-E869745E0696>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <857020EA-0B48-44DF-ABE3-E869745E0696>.<1>"
), NSLocalizedDescription=The certificate for this server is invalid. You might be connecting to a server that is pretending to be “192.168.86.200” which could put your confidential information at risk., NSErrorFailingURLKey=https://192.168.86.200:8444/smarthome/rooms, NSUnderlyingError=0x60000010ccf0 {Error Domain=kCFErrorDomainCFNetwork Code=-1202 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=1, _kCFNetworkCFStreamSSLErrorOriginalValue=-9808, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9808, _NSURLErrorNWPathKey=satisfied (Path is satisfied), viable, interface: en0[802.11], ipv4, dns, uses wifi}}, _kCFStreamErrorCodeKey=-9808}
Program ended with exit code: 0
I'm running macOS 14.5 and Xcode 15 (15A240d). ..
The only difference I can see is my hard-coded IP address, although I tried with the device name (https://shc403ac8.local:8444/smarthome/rooms) and that didn't work either ... do you have any other ideas? I could try the Xcode 16 beta but I'm not sure that's going the issue...
Thanks
Thanks, I had tried something like that, even just forcing shouldAllowHTTPSConnection(trust: response to true to see if that was the problem, but I still always get the same The certificate for this server is invalid. You might be connecting to a server that is pretending to be “192.168.86.200 ...” error.
NSAllowsLocalNetworking property is not set as (a) I'm running macOS 14.5 so it should not be necessary, and (b) I can't work out how to set Info.plist values in a command line app that has no Bundle....
Did you manage to fix this? It's frustrating me as the normal builds work fine, but Xcode cloud insists that /Downloads/GoogleService-Info.plist is part of my build when nothing in the project is pointing there...
Doh! The certificate processing was working properly, my issue was with the Hue device username - I should have realised that the Hue bridge was actually replying to my request (albeit with the 403!). Thanks to Quinn for the great advice and references - I've learned a lot about TLS!
I'm also a novice with TLS and struggling with this interface. Following Quinn's example code I ended up with this for the session delegate:
import Foundation
class NSURLSessionPinningDelegate: NSObject, URLSessionDelegate {
func urlSession(_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void) {
if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
if let serverTrust = challenge.protectionSpace.serverTrust {
Task {
let result = await shouldAllowHTTPSConnection(trust: serverTrust)
if result == true {
completionHandler(URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: serverTrust))
} else {
completionHandler(URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
}
}
}
}
}
func shouldAllowHTTPSConnection(chain: [SecCertificate]) async throws -> Bool {
let anchor = Bundle.module.certificateNamed("phillips-hue-cert")!
let policy = SecPolicyCreateBasicX509()
let trust = try secCall { SecTrustCreateWithCertificates(chain as NSArray, policy, $0) }
let err = SecTrustSetAnchorCertificates(trust, [anchor] as NSArray)
guard err == errSecSuccess else {
throw NSError(domain: NSOSStatusErrorDomain, code: Int(err), userInfo: nil)
}
var secresult: CFError? = nil
let status = SecTrustEvaluateWithError(trust, &secresult)
return status
}
func shouldAllowHTTPSConnection(trust: SecTrust) async -> Bool {
guard let chain = SecTrustCopyCertificateChain(trust) as? [SecCertificate] else { return false }
do {
return try await shouldAllowHTTPSConnection(chain: chain)
} catch {
return false
}
}
func secCall<Result>(_ body: (_ resultPtr: UnsafeMutablePointer<Result?>) -> OSStatus ) throws -> Result {
var result: Result? = nil
let err = body(&result)
guard err == errSecSuccess else {
throw NSError(domain: NSOSStatusErrorDomain, code: Int(err), userInfo: nil)
}
return result!
}
}
extension Bundle {
func certificateNamed(_ name: String) -> SecCertificate? {
guard
let certURL = self.url(forResource: name, withExtension: "cer"),
let certData = try? Data(contentsOf: certURL),
let cert = SecCertificateCreateWithData(nil, certData as NSData)
else {
return nil
}
return cert
}
}
The delegate is called as expected and the result from shouldAllowHTTPSConnection is true!.
My URLSession is set up as:
let session = URLSession(
configuration: URLSessionConfiguration.default,
delegate: NSURLSessionPinningDelegate(),
delegateQueue: OperationQueue.main)
BUT a call to let (data, response) = try await session.data(for: request, delegate: nil) returns a 403 status. I'm at a loss how to debug that as it looks fine up to that point. Clearly I'm doing something wrong with the certificates (I suspect with the delegate callback) but I've no idea what...
As a more general question, I'm trying to use the format: parameter of AxisValueLabel to format axis values of type Int. According to the documentation format: takes a FormatStyle so I would expect that something like this would work:
AxisValueLabel(format: .number.sign(strategy: .always()))
The code completion in Xcode offers this as an option, but any attempt to format a number like this throws a Ambiguous use of number error from the compiler. Am I missing something?