8 Replies
      Latest reply on Feb 14, 2020 1:11 PM by meaton
      LordOfTheFries Level 1 Level 1 (0 points)

        I'm trying to set up a client connection to communicate with an MQTT broker using client side certificates. I've been able to proof-of-concept this all on the Linux side using command line. The MQTT broker is set to require client cert, and a command like:

         

            mosquitto_pub -h servername -p 8899 -t 1234/2/Q/8 -m myMessage --cafile myChain.crt --cert client.crt --key client.pem

        does the job nicely. Now I'm trying to set up an NWConnection:

         

            self.connection = NWConnection(
                host: NWEndpoint.Host("servername"),
                port: NWEndpoint.Port(integerLiteral: 8899),
                using: .tls)

         

        But I think that simple .tls class var needs to be a much more involved NWParameters object, but I'm at a complete loss (documentation is pretty sparse) as to what I create there to attach the client certs to the parameters. Nor do I know how I even move from .crt/.pem file to something the app manages programatically.

         

        What is an example of how one would configure the NWParameters to support the client certs?

        • Re: How to setup iOS 12 NWConnection that uses client cert
          eskimo Apple Staff Apple Staff (13,045 points)

          Sorry I didn’t respond sooner; somehow I missed your post when it first showed up )-:

          What is an example of how one would configure the NWParameters to support the client certs?

          Yeah, that’s a bit tricky.  The basic structure for this as as follows:

          let options = NWProtocolTLS.Options()
          let securityOptions = options.securityProtocolOptions
          … configure `securityOptions` here …
          let params = NWParameters(tls: options)
          let conn = NWConnection(host: "example.com", port: 443, using: params)

          The above gives you access to the sec_protocol_options_t, allowing you to customise various TLS operations.  For example:

          • To customise server trust evaluation, set a verify block:

            sec_protocol_options_set_verify_block(securityOptions, { (_, trust, completionHandler) in
                let isTrusted = … your code here …
                completionHandler(isTrusted)
            }, .main)

            .

          • To always supply a client identity, set it as the local identity:

            let clientIdentity: SecIdentity = … your code here …
            sec_protocol_options_set_local_identity(
               securityOptions,
               sec_identity_create(clientIdentity)!
            )

            .

          • To respond to client certificate challenges from the server — this allows you to pick an identity based on info returned to you by the server — set a challenge block:

            sec_protocol_options_set_challenge_block(securityOptions, { (_, completionHandler) in
                let clientIdentity: SecIdentity = … your code here …
                completionHandler(sec_identity_create(clientIdentity)!)
            }, .main)

            .

          Share and Enjoy

          Quinn “The Eskimo!”
          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
          let myEmail = "eskimo" + "1" + "@apple.com"

            • Re: How to setup iOS 12 NWConnection that uses client cert
              LordOfTheFries Level 1 Level 1 (0 points)

              Thanks Quinn, I've been working through this. But I'm getting stuck at the

               

              let clientIdentity: SecIdentity = … your code here …

               

              part. I'm getting my cert/key pair from a prior BLE negotiaton (and they are self signed). So I can create my key using:

               

              // private key
              var error: Unmanaged?
              if let rsaKey = SecKeyCreateWithData(KeyData as CFData, keyOptions as CFDictionary, &error) {
                  "key \(rsaKey)".print()
              }
              else {
                  "NO KEY \(error!.takeRetainedValue() as Error)".print()
              }
              
              

              AndI can create the cert using a similiar construct:

               

              // client cert
              let certificate = SecCertificateCreateWithData(nil, CertData as CFData)
              if let certificate = certificate {
                  "certificate \(certificate)".print()
              }
              else {
                  "NO CERTIFICATE".print()
              }

               

              But the function that I though I would use to create an Identity

              SecIdentityCreateWithCertificate(nil, certificate!, &identity)

              is only availabe in macOS. I'm running on iOS. I noticed the function SecAddItem, but it doesn't talk about keys at all.

               

              Given I have the DER bytes for both the certificate and the key, what is the right way to turn these into entites I can use the Sec framework on iOS?

               

              (does this need to be a new question, rather than a continuation of your excellent example above)

                • Re: How to setup iOS 12 NWConnection that uses client cert
                  LordOfTheFries Level 1 Level 1 (0 points)

                  I found a single comment below a StackOverflow answer that ended up being the solution:  SecPKCS12Import().

                   

                  XCA lets me bundle up my cert/chain/privkey files as .p12 just as well. Using those bytes, I can import it and extract the SecIdentity.

                   

                  let importOptions = [ kSecImportExportPassphrase as String: "" ]
                  var rawItems: CFArray?
                  let status = SecPKCS12Import(P12Data as CFData, importOptions as CFDictionary, &rawItems)
                  let items = rawItems! as! Array<Dictionary<String, Any>>
                  let firstItem = items[0]
                  let clientIdentity = firstItem[kSecImportItemIdentity as String]! as! SecIdentity
                  print("clientIdentity \(clientIdentity)")
                    • Re: How to setup iOS 12 NWConnection that uses client cert
                      eskimo Apple Staff Apple Staff (13,045 points)

                      XCA lets me bundle up my cert/chain/privkey files as .p12 just as well.

                      Excellent news.  If you start with a .p12, this process is simple.  However, if you start with a private key and certificate pair, things get complex.  On iOS, the absence of SecIdentityCreateWithCertificate means that you have to bounce through the keychain.

                      Share and Enjoy

                      Quinn “The Eskimo!”
                      Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                      let myEmail = "eskimo" + "1" + "@apple.com"

                        • Re: How to setup iOS 12 NWConnection that uses client cert
                          arupcsedu Level 1 Level 1 (0 points)

                          We need to create TLS connection with root CA certificate. We have .pem and .der files of root certificate.

                          Here is the steps :

                          1. Create parameters for TLS connection using NWProtocolTLS
                          func createTLSParameters(allowInsecure: Bool, queue: DispatchQueue) -> NWParameters {
                          
                            let options = NWProtocolTLS.Options()
                            sec_protocol_options_set_local_identity(options.securityProtocolOptions, sec_identity_create(clientIdentity)!)
                          
                            sec_protocol_options_set_challenge_block(options.securityProtocolOptions, { (_, completionHandler) in
                            completionHandler(sec_identity_create(clientIdentity)!)
                            }, .main)
                          
                            let parameters = NWParameters(tls: options)
                          
                          
                            return parameters
                            }
                          1. Create Socket connection with TLS parameter by NWConnection
                          func createSocket() {
                            let parameters = createTLSParameters(allowInsecure: true, queue: self.queue) 
                            parameters.prohibitedInterfaceTypes = [.wifi]
                            guard let endPoint = NWEndpoint.Port(rawValue: 4420) else {
                            return
                            }
                          
                            con = NWConnection(to: .hostPort(host: NWEndpoint.Host("somehostname.com"), port: endPoint), using: parameters)
                          
                            con?.stateUpdateHandler = { [weak self] status in
                            if case .ready = status {
                            self?.receiveData()
                            }
                            }
                          
                            con?.start(queue: queue)
                            }
                          1. Send HTTPS request. Here Required Info is the uri for GET.
                          func sendData(for groupUrl: String, atk: String) {
                            con?.send(content: "GET **Required Info**".data(using: .utf8), contentContext: .defaultMessage, isComplete: false, completion: NWConnection.SendCompletion.contentProcessed({ (error) in
                            print("send Data callback---")
                            if let err = error {
                            print("send Data failed \(err)")
                            }
                            }))
                            }
                          1. In response we have got.
                          HTTP/1.0 200 OK
                          Cache-Control: no-cache
                          Pragma: no-cache
                          Connection: close
                          Content-Length: 188
                          
                          The requested URL was rejected. Please consult with your administrator.
                          
                          
                          Your support ID is: 7*********769
                          I think something happened for loading root CA info, during creating TLS parameter. Can you help us the way of loading root CA during TLS connection?

                          Best regards
                          Arup Sarker
                          Email: arupcsedu@gmail.com
                            • Re: How to setup iOS 12 NWConnection that uses client cert
                              meaton Apple Staff Apple Staff (80 points)

                              It looks like you are wanting to know more about setting up a challenge to evaluate a server's certificate, possibly with something like sec_protocol_options_set_verify_block.  However, it looks like you are seeing a 200 response in your example, so did you evaluation succeed? Can you tell me a bit more about what you are looking to achieve?

                               

                              Matt Eaton

                              DTS Engineering, CoreOS

                              meaton3 at apple.com

                                • Re: How to setup iOS 12 NWConnection that uses client cert
                                  arupcsedu Level 1 Level 1 (0 points)

                                  Hi Meaton,

                                  Thank you for quick response. We have server certificate with ".der" format. We want to create TLS connection connection and perform HTTP GET operation.

                                  Previously, We have used URLSession API and it was working fine.

                                  But We want make a connection specific with LTE, although phone is connected with WiFi. That's why We have used

                                  parameters.prohibitedInterfaceTypes = [.wifi] 

                                   

                                  with tcp/udp, we are able to make connection with NWConnection api. But we need TLS connection with server certificate.

                                  Below is the function to create TLS parameters.

                                   

                                   

                                  func createTLSParameters(allowInsecure: Bool, queue: DispatchQueue) -> NWParameters {  
                                  
                                    let options = NWProtocolTLS.Options()  
                                    sec_protocol_options_set_local_identity(options.securityProtocolOptions, sec_identity_create(clientIdentity)!)  
                                  
                                    sec_protocol_options_set_challenge_block(options.securityProtocolOptions, { (_, completionHandler) in  
                                    completionHandler(sec_identity_create(clientIdentity)!)  
                                    }, .main)  
                                  
                                    let parameters = NWParameters(tls: options)  
                                  
                                  
                                    return parameters  
                                    }
                                  
                                  

                                  How can we create "clientIdentity" (line no 7) from server certificate?

                                  Please correct me, if my concept is wrong.

                                   

                                  Best regards

                                  Arup Sarker

                                  Email : arupcsedu@gmail.com