Accept untrusted SSL server certificate with CFStream socket on iOS - iphone

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:
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
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:
disable default trust evaluation using kCFStreamSSLValidatesCertificateChain
get the trust object (kCFStreamPropertySSLPeerTrust) once connected to the stream (but before sending any data, i.e, on kCFStreamEventCanAcceptBytes or kCFStreamEventHasBytesAvailable events)
set your self-signed root certicate as a trusted anchor for that trust object
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
evaluate the trust object (SecTrustEvaluate) and check result is either kSecTrustResultProceed or kSecTrustResultUnspecified

Related

Difference between SecPKCS12Import and storing certificate as NSData in keychain?

I've been writing client certificate code for iOS using many of the resources here: iOS Client Certificates and Mobile Device Management and I've broken out the process to these steps:
Get the Cert via email or AppConfig
Store the Cert (securely)
Extract Identity and Trust from the Cert.
Intercept failed web requests, create NSURLConnection to manually handle auth responses as per Eskimo's advice.
Turn Identity and Trust into the auth response challenge.
My problem is step 2. SecPKCS12Import function appears to automatically add Identity to the keychain as well as return all Identities and Trusts from the certificates, thus eliminating the need for the convenience function often given ExtractIdentityAndTrust().
But on my 2nd run, I will need the Identity and Trust, not just Identity. My current plan is to store the entire cert raw using SecItemAdd, test for duplicates and use it, but I feel like I should be able to just use SecPKCS12Import then later grab it without also using SecItemAdd.
The documentation that is most confusing is SecPKCS12Import, and I would like a clearer understanding of what it does vs secItemAdd, and if secItemCopyMatching() is the same in the end just to grab the certificate. Is Trust not needed or am I just being literal and it's stored with the identity?
The general save, use, store, grab is working, but I'm using NSData and would prefer to store it correctly
I eventually became more familiar with the KeyChain and Identity vs Trust and learned this:
The Trust is a Cert stored in a place that determines who your custom Certificate Authorities are. It only needs to be tested once, which is why it isn't stored.
Storing the Identity is also a certificate, but needed for later. The keychain considers Certificates/Identities to be a Special/Unique thing so it is stored as its own thing, which is why all the keychain code looks different than just securing a password.
Basically, storing the Trust is unnecessary for future reference, but should be checked for good practice. I personally think an expiration might be handy

REST(ful) simple authentication without SSL and HMAC?

I wonder if it is possible to have an easy authentication method that is restful, fast and provides a litte security.
SSL is not an option, because I can't rely on a valid SSL Certificate on the server of clients. Also HMAC is not really possible because the body of the request should be signed, when used properly, but in my case that body could be a large file. Further should the authentication be possible with JavaScript/AJAX.
I thought about something really simple. What's the problem with that one:
HEADER: X-Authentication: timestamp:username:sha256(timestamp:password)
The server knows the users password and could check the hash, the timestamp is used to only allow request that took place e.g. 10 seconds before. The replay window would be extremly small, and there are no sessions on the serverside.
If the hash is cracked the attacker knows the password and has unlimited access.
Alternative would be to use
HEADER: X-Authentication: timestamp:username:HMAC(password, 'timestamp+request-method+verb')
What's the way to go? I'm not a security pro, maybe storing the session on the server would be better (but not RESTful)?
I built a random hash algorithm that does what you need, it's called jAuthenticate.
You can download it from: https://github.com/thomasoeser/jAuthenticate​
You can see how it works here: http://furiousgryphon.com/jauthenticatedemo.html
The reason it's a strong algorithm (in my opinion) is that I'm using a random number to influence the hash but I'm sending an obfuscated number with the hash.
Each hash is single use only.
Have a look, it's free open source (MIT).
HTTP authentication is extensible so you can invent your own mechanism (obviously at your own risk!). See https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-p7-auth-20 for details.
Don't bother inventing your own new X- header. Using the existing Authorization header with your scheme is a better option.
e.g.
Authorization: kruemel-auth timestamp:HMAC(password, 'timestamp+request-method+verb')
Ideally new schemes will be registered with IANA along with a specification. A registry is being setup to track auth schemes that have been developed. See http://tools.ietf.org/id/draft-ietf-httpbis-authscheme-registrations-03.html

How can we tell a CFStream to use a set of anchor certificates?

I know we can use SecTrustSetAnchorCertificates() given a SecTrustRef. But with CFStreams, we can get the trust object only after the hand shake. One workaround seems to be to disable certificate chain verification on the CFStream using the kCFStreamSSLValidatesCertificateChain property and then get the peer certificates using kCFStreamPropertySSLPeerCertificates, create a trust from those certificates and evaluate the trust ourselves.
But it would be a lot cleaner if we could just tell CFStream to use an array of certs as anchor. Am I hoping for too much?
eskimo1 from Apple Devforums answered this so:
First, disable automatic trust evaluation using kCFStreamSSLValidatesCertificateChain.
Second, once the stream is up and running (I typically do this in my 'can accept bytes' or 'has bytes available' message handling), get the SecTrust object from the stream using kCFStreamPropertySSLPeerTrust and evaluate that trust for yourself. If the trust evaluation fails, tear down the stream.

iOS - managing trusted roots on an SSL connection

By adding to the kCFStreamPropertySSLSettings dictionary the usual kCFStreamSSLValidatesCertificateChain, kCFStreamSSLAllowsAnyRoot, etc - I can make server & client authentication work.
However I fail to see how I can get this more fine grained; i.e.
detect a cert of server/CA never seen - and escalate acceptance up to the user.
validate a cert of the server I am connected to against a narrow trust list (e.g. the cert learned about during a previous connect).
I.e. what I am looking for is 1) something like the SSLSetTrustedRoots() on MacOSX and 2) something like the error/domain callbacks in kCFStreamErrorDomainSSL (e.g. errSSLPeerCertUnknown) - none which seems defined/accesible on the ipad/iphone 4.2.1.
Or am I missing something ? Or do I need to do something explicit on the callback ? Examples appreciated (the AdvancedURLConnections example is not quite applicable - I have a raw (IRC) socket).
Thanks,
Dw.
Set kCFStreamSSLValidatesCertificateChain to kBooleanFalse and manually verify the cert with the APIs.
Specifically, use
SecTrustCreateWithCertificates
with the certificates you get from
CFReadStreamCopyProperty(readStream, kCFStreamPropertySSLPeerCertificates);
Then, you can use
SecTrustSetAnchorCertificates
and finally call
SecTrustEvaluate

Storing certificate chains in the application keychain

On iOS, I know that we can evaluate a trust with SecTrustEvaluate(). To create a trust, we either import it from a *.p12 file or create it using an array of certificates and a set of policies.
I also know that in order to ensure that a server, that is not trusted by default, is trusted, we can use SecTrustSetAnchorCertificates() to augment the list of root CAs SecTrustEvaluate() uses to verify the server's certificate.
Now, how can I ensure that these anchor certificates are available on a subsequent launch? I can store certificates, keys and identities in the application keychain, but not a certificate chain. Even if I store all the certificates in the chain, how will I know what certificates are to be used as anchor certificates?
One solution that comes to mind is to just get all the certificates from the keychain and set them as anchor certificates. Another method could be to all the certificate chains on the disk as *.p12 files and load them once on every application run.
eskimo1 from Apple Devforums answered this so:
First up, you don't need to store the certificates in the keychain. You can store them elsewhere if you like (and create the SecCertificateRef using SecCertificateCreateWithData).
Secondly, your question talks about .p12 files, but I'm not sure what this has to do with things. In general you only use a .p12 file to distribute confidential information, and certificates are not confidential.
Finally, you can store a certificate chain by just storing the persistent reference to each certificate in that chain (or by storing the actual data of each certificate).