-
Notifications
You must be signed in to change notification settings - Fork 301
SSL
If your application uses the Couchbase Lite Listener to accept incoming connections, so that peers can access or replicate with your app's database, it's easy to support SSL. This will improve security and privacy by encrypting the network traffic.
Generally the biggest headache in setting up an SSL server is getting a certificate. A certificate used by a website will be signed by a Certificate Authority (CA) that can vouch for its identity. This involves creating a certificate request, submitting it to the authority, doing some sort of challenge/response that lets the authority verify that you own the domain in question, receiving the signed certificate, and then installing it on the computer that will serve requests. (The Sync Gateway wiki contains more information on doing this, if you're curious.)
If you've gone through all that, you can use that certificate with CBLListener pretty easily. First use the Keychain API to get a SecIdentityRef, which is a reference to your certificate plus its matching private key. Then set that as the SSLIdentity property of the listener.
But fortunately, for most purposes we don't need to go through that rigamarole. Identity is a complicated topic, and it's hard to say how a mobile device should identify itself. So we'll put that out of the way and just focus on encryption. For that, any certificate will do, even a so-called self-signed one. Creating a self-signed certificate is just a simple matter of programming, specifically generating an RSA key-pair and wrapping the public key in an X.509 certificate. Couchbase Lite 1.1 can do that for you.
We assume you've already instantiated a CBLListener object. Before calling its -start method, configure its SSL identity like so:
NSError* error;
if (![listener setAnonymousSSLIdentityWithLabel: @"MyApp SSL" error: &error])
[self handleError: error];That's it. Your listener is now serving SSL using an automatically generated identity. (The label string is unimportant; it's just used as an identifier to store and look up the certificate in the app's Keychain.)
Yes and no. It encrypts the connection, which is unquestionably much better than not using SSL. But unlike the usual SSL-in-a-browser approach you're used to, it doesn't identify you to the peer that's connecting to you. The client can regain some of the benefits of identification, though, by using cert pinning:
- The first time the client connects to this listener, it gets the
serverCertificatefrom theReplicationinstance and saves it, associated with whatever name or identifier it uses for that peer. - On subsequent connections, it first looks up that cert and calls the
Replication'ssetAnchorCertsmethod to tell it to only accept that cert.
This way, assuming the first connection is valid, all the subsequent connections will be too.
There are ways to validate even the initial connection by using a "pairing" process where the peers first exchange information through a trusted channel. For example, the device running the listener can generate and display a QR code that contains its IP address and a SHA-1 digest of its SSL certificate; the other peer can scan that code using its camera. Then when the second peer connects it can verify that the serverCertificate has the expected digest.
Usually it's only the SSL server that uses a certificate to identify itself. But SSL also supports client certificates, which work in reverse, allowing the client to present a certificate that identifies itself to the server.
As of 9 July 2015 (commit e849878) Couchbase Lite for iOS supports client certificates. Here's how it works. (Caveat: the API used here is provisional and may change.)
- Call
[CBLAuthenticator SSLClientCertAuthenticatorWithAnonymousIdentity:]. This does the following:- The first time it's called, it generates a self-signed certificate (and private key) in the app's Keychain, tagged with the label you provided.
- On subsequent calls, it reads the certificate and key out of the Keychain.
- It wraps them up in a CBLAuthenticator object.
- Set the returned CBLAuthenticator as the replication's
authenticatorproperty.
Example:
id<CBLAuthenticator> auth = [CBLAuthenticator SSLClientCertAuthenticatorWithAnonymousIdentity: @"me"];
pullRepl.authenticator = pushRepl.authenticator = auth;The string "me" doesn't really mean anything, and it won't be visible to the server. It's just a way of labeling different identities in the Keychain, should you need to use more than one.
To check client certs on the server side (i.e. the app that's running the listener), add a delegate to the CBLListener and implement -[authenticateConnectionFromAddress:withTrust:]. This is passed a SecTrustRef object from which you can get the client cert.
Here's an example that assumes there's a single client cert, allowedClientCert, that you'll accept.
- (NSString*) authenticateConnectionFromAddress: (NSData*)address
withTrust: (SecTrustRef)trust
{
SecCertificateRef clientCert = SecTrustGetCertificateAtIndex(trust, 0);
if (CFEqual(clientCert, self.allowedClientCert) {
return @"user"; // allow connection; exact string doesn't matter
} else {
return nil; // deny connection
}
}The method returns a string; for now it's only significant whether the value is nil (meaning deny) or non-nil (meaning allow.) In the future we may extend the API in ways that will let you use this string to identify the user in later callbacks.