mutual TLS with secp512r1 private key certificate

Hi guys,


I'm trying to implement mutual TLS connection. Here is the flow:


  1. I generate the csr request (using elliptic curve key, secp512r1)
  2. Send the signing rquest to the server and receive the public key certificate as a response
  3. I make p12 certificate using public key certificate received in step2 with my ECC secp512r1 private key. I save it as "ca.p12" file in Documents
  4. when I get NSURLAuthenticationMethodClientCertificate challenge I resolve it using the credentials from this certificate.
  5. Get Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made."


While checking the traffic between server and client it appears that it's a client side issue.


Questions:

1. Does iOS 9/10 support elliptic curve keys, secp512r1 in TLS handshake?

2. Is there anything that is wrong or I missed some critical item in my flow?


Any thoughts, suggestions are really appreciated. Thanks.




public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void) {
        if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate) {
        
            let identityAndTrust: IdentityAndTrust = self.extractIdentity()
    
             let urlCredential: URLCredential = URLCredential(
                  identity: identityAndTrust.identityRef,
                  certificates: identityAndTrust.certArray as? [AnyObject],
                  persistence: URLCredential.Persistence.forSession)
    
             completionHandler(.useCredential, urlCredential)
        }
        
        // server trust verification works fine
        if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
            if (self.shouldTrustProtectionSpace(space: challenge.protectionSpace)) {               
                completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
            }
        }
    }


struct IdentityAndTrust {
        var identityRef: SecIdentity
        var trust: SecTrust
        var certArray: AnyObject
    }

    func extractIdentity() -> IdentityAndTrust {
        var identityAndTrust: IdentityAndTrust!
        var securityError: OSStatus = errSecSuccess

        var path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
        path = path + "/ca.p12"
    
        let PKCS12Data = NSData(contentsOfFile:path)!
        let key: NSString = kSecImportExportPassphrase as NSString
        let options: NSDictionary = [key : "123"]

        var items: CFArray?

        securityError = SecPKCS12Import(PKCS12Data, options, &items)

        if securityError == errSecSuccess {
            let certItems: CFArray = items as CFArray!
            let certItemsArray: Array = certItems as Array
            let dict: AnyObject? = certItemsArray.first
            if let certEntry: Dictionary = dict as? Dictionary<String, AnyObject> {
         
                /
                let identityPointer: AnyObject? = certEntry["identity"]
                let secIdentityRef: SecIdentity = identityPointer as! SecIdentity!
                print("\(identityPointer)  :::: \(secIdentityRef)")
                /
                let trustPointer: AnyObject? = certEntry["trust"]
                let trustRef: SecTrust = trustPointer as! SecTrust
                print("\(trustPointer)  :::: \(trustRef)")
         
                //get certificates from identity
                var certRef: SecCertificate?
                SecIdentityCopyCertificate(secIdentityRef, &certRef)
                let certArray: NSMutableArray = NSMutableArray()
                certArray.add(certRef as SecCertificate!)
            
                //add also ca root certificate
                let caRootCert = CertRepository.caRootCertificate()
                certArray.add(caRootCert)
         
                identityAndTrust = IdentityAndTrust(identityRef: secIdentityRef, trust: trustRef, certArray:  certArray)
            }
        }
        return identityAndTrust
    }

Replies

What I recommend you do in this situation is split the problem in half. Right now your process consists of two important steps:

  • Generate the PKCS#12

  • Use the PKCS#12 for client certificate authentication

You can split this up easily:

  • You can generate a PKCS#12 on iOS, and then see if OpenSSL is able to use it for client certificate authentication

  • You can generate a PKCS#12 with OpenSSL, and then see if iOS is able to use it for client certificate authentication

IMPORTANT The ancient OpenSSL built in to macOS does not have good ECC support. You’ll either need to update that yourself or run the test on some other platform (I generally do this by running Ubuntu in a VM).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I did the following:

  • generated PKC#12 on iOS, saved it to my documents folder, copied to desktop mac and extracted public certificate and private key into pem.
  • Updated openSSL to 1.0.2k
  • Used s_client to check client authentication
openssl s_client -connect myhost.ae:27447 -cert publicCert.pem -key privateKey.pem -CAfile serverCA.crt -state -debug


Output:

...................
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-ECDSA-AES256-GCM-SHA384
    Session-ID: 589DB1117865D9E3B17FB648FA0826CB5EEECE6FF18C02957C7963F0876E84AC
    Session-ID-ctx:
    Master-Key: EC2D8B658A6A9B5F5BD7AE4812E31E0067D094E061B01F90503951DDA4CCE1F51A8F7067D6A69BCAA76A28015ED52C2F
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1486729489
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---


Still can't seem to make it work on iOS.

Now that you’ve got that identity on your Mac, try using it with TLSTool to see if it works there.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hi,


(first, sorry for my english)


im in the same situation, and practically the same code. I have a client certificate send by a client in pk12 format. The client certificate have 3 certificates inside server, intermediate, and root, and a private RSA key. The certificate also has a passphrase


I imported this certificate on my application defining a custom type on my app, changing the extension of the certificate file and getting it as an nsdata.


I cheked with openssl (first I updated openssl to 1.0.2k like RossDM, for tlsv1_2 support) and the output seems ok


---
SSL handshake has read 7222 bytes and written 2591 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: 3FA9104FDB80678A89F92CF8D2A50C8D14D30B6E186C17F83E279D1A0B2974B2
    Session-ID-ctx:
    Master-Key: 7657EB9AD1ADA969EEA8888627ED50F919431D909DBBA0E782BE390D5527F597359B726C6BB1C8E62422CCB6EE765482
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None



I tested with TLSTOOL and the result seems ok (i think)


./TLSTool s_client  -connect host:443 -min tls1_2 -cert certname
* client identity: identityname (Qualified Certificate: ***-xx-***-***)
*  input stream did open
* output stream did open
* output stream has space
* protocol: TLS 1.2
* cipher: ECDHE_RSA_WITH_AES_256_GCM_SHA384
* trust result: unspecified
* certificate info:
*   0 + rsaEncryption 2048 sha256-with-rsa-signature 'server cert'
*   1 + rsaEncryption 4096 sha256-with-rsa-signature 'intermediate'
*   2 + rsaEncryption 4096 sha1-with-rsa-signature 'root'
*  input stream has bytes
*  input stream end
* close
* bytes sent 0, bytes received 0



In other answers to similar problems in this forum, as in this https://forums.developer.apple.com/message/201856. You say that there is a bug and that it can not be done and you refer to a bug that I do not know how to consult. I have also read that the public key must be deleted since it can have conflicts with the private key.


It's really a problem detected or I'm actually doing something wrong??. Since the certificate seems to be fine, the server seems to be fine, even the development on other platforms has not had a problem, I do not know what direction I should take now.



Thanks for the support

In other answers to similar problems in this forum, as in this …. You say that there is a bug and that it can not be done

Right, but that bug relates to WKWebView specifically. RossDM is talking about NSURLSession, which is capable of handling client certificate authentication challenges just fine.

So, which API are you using to test this?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Your are right, im using WKWebView.


Should i use UIWebView instead WKWebView? Does uiwebview behave correctly with client certificates?


Thanks for the support, at this point I needed help not to waste more time on something that did not work

Does uiwebview behave correctly with client certificates?

No. UIWebView does not explicitly support any authentication challenges.

Should i use UIWebView instead WKWebView?

Well, that’s a complex question (-: You can hack around this problem by sliding an NSURLProtocol underneath UIWebView, as shown by the CustomHTTPProtocol sample code. This works with UIWebView because UIWebView does its networking in your process. OTOH, it doesn’t work with WKWebView because WKWebView does most of its work, including its networking, out of process.

There’s a bunch of drawbacks to doing this:

  • You’re using UIWebView not WKWebView, and the latter is the future of web views on our platforms.

  • It requires a bunch of gnarly low-level code.

If you have any control over the server then I recommend you switch it to use some other authentication scheme. For example, this problem often comes up in enterprise environments, where you can use Single Sign-On to avoid the whole issue. However, if you can’t do that then your only current option is to switch to UIWebView and use the custom NSURLProtocol hackaround.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hi,


I've changed the component by the old uiwebview, handling the challenge with the URLConection, and I've been able to connect to the client certificate.


I have "lost" a week of work with a component that has a bug, and nowhere in the documentation does it indicate. I think it would be nice to create a technical note, like other similar ones that already exist, or modify the documentation regarding authentication with a client certificate.


Thank you very much for the help and for orienting me, very good job

Hi Ross,
I am trying to generate CSR with Elliptic Curve. Seems like you have already done that. Will appreciate if you can share the code.