Retrieve Only the Certificates Presented by the Server

Is it possible using the network framework to retrieve the list of certificates presented by the host alone, and not the reconstructed chain assembled by the system?

For example, in OpenSSL one can call SSL_get_peer_cert_chain which will return exactly this - a list of the certificates presented by the server. This is useful for when you may want to manually reconstruct the chain, or if the server is misconfigured (for example, is missing an intermediate cert).

Is something like this possible with the network framework?

If I connect to a host that I know only returns 1 certificate, the trust ref already has the reconstructed chain by the time my code is called:

sec_protocol_options_set_verify_block(tlsOptions.securityProtocolOptions, { metadata, trustRef, verifyComplete in
let trust = sec_trust_copy_ref(trustRef).takeRetainedValue()
let numberOfCertificates = SecTrustGetCertificateCount(trust) // Returns 3 even though the server only sent 1
Answered by DTS Engineer in 830939022

I believe the droid you’re looking for is sec_protocol_metadata_access_peer_certificate_chain. Pasted in below is a small demo of how to get it working. It prints:

state: preparing
cert: <SecConcrete_sec_certificate: 0x6000036b0460>
trustCount: 3
state: ready

which reflects the fact that the trust object has the resolved chain but sec_protocol_metadata_access_peer_certificate_chain just returns the leaf that was presented by the server.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"


import Foundation
import Network
func main() {
let tls = NWProtocolTLS.Options()
let sec = tls.securityProtocolOptions
sec_protocol_options_set_verify_block(sec, { meta, trust, completionHandler in
sec_protocol_metadata_access_peer_certificate_chain(meta) { cert in
print("cert: \(cert)")
}
let trust = sec_trust_copy_ref(trust).takeRetainedValue()
let trustCount = SecTrustGetCertificateCount(trust)
print("trustCount: \(trustCount)")
completionHandler(true)
}, .main)
let params = NWParameters(tls: tls, tcp: .init())
let conn = NWConnection(host: "incomplete-chain.badssl.com", port: 443, using: params)
conn.stateUpdateHandler = { state in
print("state: \(state)")
}
conn.start(queue: .main)
dispatchMain()
}
main()
Accepted Answer

I believe the droid you’re looking for is sec_protocol_metadata_access_peer_certificate_chain. Pasted in below is a small demo of how to get it working. It prints:

state: preparing
cert: <SecConcrete_sec_certificate: 0x6000036b0460>
trustCount: 3
state: ready

which reflects the fact that the trust object has the resolved chain but sec_protocol_metadata_access_peer_certificate_chain just returns the leaf that was presented by the server.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"


import Foundation
import Network
func main() {
let tls = NWProtocolTLS.Options()
let sec = tls.securityProtocolOptions
sec_protocol_options_set_verify_block(sec, { meta, trust, completionHandler in
sec_protocol_metadata_access_peer_certificate_chain(meta) { cert in
print("cert: \(cert)")
}
let trust = sec_trust_copy_ref(trust).takeRetainedValue()
let trustCount = SecTrustGetCertificateCount(trust)
print("trustCount: \(trustCount)")
completionHandler(true)
}, .main)
let params = NWParameters(tls: tls, tcp: .init())
let conn = NWConnection(host: "incomplete-chain.badssl.com", port: 443, using: params)
conn.stateUpdateHandler = { state in
print("state: \(state)")
}
conn.start(queue: .main)
dispatchMain()
}
main()
Retrieve Only the Certificates Presented by the Server
 
 
Q