Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accept untrusted SSL server certificate with CFStream socket on iOS

I need to open a CFStream socket connection to a server that has an untrusted CA root. I have the certificate of the server, and I can create a SecCertificateRef structure from it. The problem is how to set up the properties of the stream.

I think I should set the kCFStreamPropertySSLSettings property to a CFDictionary that in turn contains a kCFStreamSSLCertificates key. This key should hold a "a CFArray of SecCertificateRefs except for the first element in the array, which is a SecIdentityRef" according to the docs. Now I can create the SecCertificateRef from the server's certificate that I'll ship with the app, but how to get the SecIdentityRef? I guess it should be the client identity but I absolutely don't want client side authentication for now. And I can't find a way how to feed CFStream only with the server certificate.

Note, I don't want to add the untrusted certificate to the keychain, neither disable kCFStreamSSLValidatesCertificateChain in the settings. I need to accept the server authentication only if it is based on my own server certificate data loaded from the disk, and only on this CFStream.

like image 540
MrTJ Avatar asked Mar 08 '12 14:03

MrTJ


2 Answers

I do not have the direct answer to your question, but perhaps few guidelines:

  1. Why do you need to use the CFStream API and not the more intuitive NSURLConnection ?
    From what I could find in the documentation, it seams like not everything that is available for Mac OS X, regarding CFStream API, is available for iOS. So think about it, and see if you can switch to NSURLConnection :-)

  2. For NSURLConnection, you can use the NSURLConnectionDelegate methods to get the SSL challenge and validate the certificate on your own. You can check the wsdl2objc project, where I have implemented these features:

    • The project: http://code.google.com/p/wsdl2objc/
    • Some guidelines on how to get the certificate references: http://code.google.com/p/wsdl2objc/wiki/AdvancedOptions
  3. Now about your questions :-)
    I don't see how you can set a custom (untrusted) CA in kCFStreamPropertySSLSettings. I'm not sure if it can be done by using kCFStreamSSLCertificates since it is meant to be used for setting client-side certificates (thus the requirement of having the SecIdentityRef on index 0, which basically provides the private key).

  4. When you say you don't want to add the certificate to the keychain, do you mean manually or programmatically ? I guess you don't like the users of your app to have to do it manually, but you can use the Security API to import the certificate programatically. In this case your certificate will be imported in a sandboxed keychain which is only available for your application. (again, not sure if this will work but worths the try)

In my applications I use NSURLConnectionDelegate to manually validate untrusted certificates.

Regards,
Pece

like image 171
pmilosev Avatar answered Oct 19 '22 17:10

pmilosev


Basically you have to:

  1. disable default trust evaluation using kCFStreamSSLValidatesCertificateChain
  2. get the trust object (kCFStreamPropertySSLPeerTrust) once connected to the stream (but before sending any data, i.e, on kCFStreamEventCanAcceptBytes or kCFStreamEventHasBytesAvailable events)
  3. set your self-signed root certicate as a trusted anchor for that trust object
  4. optionally, you may add custom SSL policies to the trust object (e.g, hostname doesn't match certificate CN), but if you do that it's important that you do it before setting the trusted anchor or you may get kSecTrustResultRecoverableTrustFailure result
  5. evaluate the trust object (SecTrustEvaluate) and check result is either kSecTrustResultProceed or kSecTrustResultUnspecified
like image 29
AAA Avatar answered Oct 19 '22 17:10

AAA