Issue implementing the Connection with TLS/SSL method from TCP method using swift5.

Currently, We are developing a MacOS application and the app can connect successfully to a server "X.X.X.X:YYYY" with default TCP Option and none TLS by NWConnection.

Note: the host name without any "http/https" or "ws/wss". If we add one of them before host name, the connection will be fail to connect.

After the client connected to the server. Server will send a message for client contains "TLS". We have two certificate files from server so how can we setup the connection with those certificates for SSL Handshake after the connection has been established?

We have a MFC App using the logic as description below:

  • Open socket with TCP.
  • Get the sever receive message.
  • Get Handshake context if the message contains "TLS", Client credentials.
  • Perform Client Handshake with the context above.
  • Verify the Server CA with *.der and *.cer format

About Certificate Authority, I used these lines of code to add to the keychain for testing:

let rootCertPath = "***/enterprise_der.cer"
        let rootCertData = NSData(contentsOfFile: rootCertPath)
        let rootCert = SecCertificateCreateWithData(kCFAllocatorDefault, rootCertData!)
        //var result: CFTypeRef1
        let dict = NSDictionary.init(objects: [kSecClassCertificate, rootCert!], forKeys: [kSecClass as! NSCopying, kSecValueRef as! NSCopying])

        err = SecItemAdd(dict, nil)

The CA is automatically add to the login Keychain.

I have tried to use the

 let tlsOptions = NWProtocolTLS.Options()
    sec_protocol_options_set_min_tls_protocol_version(
        tlsOptions.securityProtocolOptions,
        .TLSv12)

but my app dose not connect to the IP totally.

func connect(toHost host: String, port: UInt16 = broadcastPort) -> Void) {
        connection = NWConnection(host: .init(host), port: .init(integerLiteral: port), using: NWParameters(tls: nil, tcp: .init()))
        connection?.stateUpdateHandler = { ... }
        self.connection?.start(queue: .main)
    }

Is there any solution for this case? I have tried several guidelines in StackOverFlow but It doesn't work or being deprecated.

Answered by DTS Engineer in 757312022

Hmmm, implementing STARTTLS is currently being discussed in a different thread. Are you related to the OP over there (lgminh)?

Share and Enjoy

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

Regarding:

Verify the Server CA with *.der and *.cer format

For your TLS options you will want to add a block for sec_protocol_options_set_verify_block.

See an example of this in action in the article Creating an Identity for Local Network TLS, at the bottom of the article.

I also try to use OpenSSL to get the Certificate from client but is is nothing to show:

macos@ESB-Dev-MacBook ~ % openssl s_client -starttls postgres -showcerts -connect  10.1.141.91:1515 </dev/null -debug
CONNECTED(00000003)
write to 0x600003015480 [0x1049b1fb8] (8 bytes => 8 (0x8))
0000 - 00 00 00 08 04 d2 16 2f-                          ......./
read from 0x600003015480 [0x159810800] (8192 bytes => 15 (0xF))
0000 - 4d 44 43 53 54 41 52 54-3c 3c 54 4c 53 3e 3e      MDCSTART<<TLS>>
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 15 bytes and written 8 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
read from 0x600003015480 [0x159810800] (8192 bytes => -1 (0xFFFFFFFFFFFFFFFF))

the Client IP is down immediately.

Have you tried using a local network name as described in the article here?

Regarding:

We have two certificate files from server so how can we setup the connection with those certificates for SSL Handshake after the connection has been established?

Just so I'm clear also, you don't want to upgrade the connection to use TLS midstream, you are looking to perform either server side or client side verification using TLS from the start, correct?

For client side verification of the server's (listener) certificates use sec_protocol_options_set_verify_block.

For client authentication with the server (listener) user sec_protocol_options_set_challenge_block.

Thanks for your quick response

Just so I'm clear also, you don't want to upgrade the connection to use TLS midstream, you are looking to perform either server side or client side verification using TLS from the start, correct?

I finally found that is STARTTLS connection protocol. It didn't make a TLS connection from the beginning, it follow this flow:

  1. Connect via TCP protocol.
  2. Get SSL context.
  3. Verify the Certificate using the context above.
  4. Send encrypt data

I have found some example as:

// Create the SSL/TLS context
var context: SSLContext? = nil
let status = SSLCreateContext(nil, SSLProtocolSide.clientSide, SSLConnectionType.streamType, &context)
guard status == errSecSuccess else {
    // Handle the error
    return
}

But it seems to be deprecated in MacOS 13.0. Did you have any clue about this case? Thanks a lot.

Accepted Answer

Hmmm, implementing STARTTLS is currently being discussed in a different thread. Are you related to the OP over there (lgminh)?

Share and Enjoy

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

Issue implementing the Connection with TLS/SSL method from TCP method using swift5.
 
 
Q