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.
I do not have the direct answer to your question, but perhaps few guidelines:
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 :-)
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:
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).
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
Basically you have to:
kCFStreamSSLValidatesCertificateChain
kCFStreamPropertySSLPeerTrust
) once connected to the stream (but before sending any data, i.e, on kCFStreamEventCanAcceptBytes
or kCFStreamEventHasBytesAvailable
events)kSecTrustResultRecoverableTrustFailure
resultSecTrustEvaluate
) and check result is either kSecTrustResultProceed
or kSecTrustResultUnspecified
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With