How to make my CouchbaseMobile iOS app use SSL?

I’m pretty sure the fix is in 1.1.1, which will be released in the next few days. If you don’t want to wait, you can download the latest nightly build and try it out.

Hi Jens,

I tried latest build from 14.09, but I get the same error message:
CBL_Pusher[https://here is my address]: SSL cert is not trustworthy (result=5)

According to code in github in any case error message should be
Warn(@“CBLCheckSSLServerTrust: SSL cert for %@ is not trustworthy (result=%d)”,
host, result);
and also I should not get this error message for result 5.

I did check twice, removed framework and added it again. plist says me it is 1.1.1.

How can it be?

The short answer is that you’re looking at the source code of the master branch. The 1.1.1 release is built from the branch release/1.1.1.

The warning you’re getting is produced here.

also I should not get this error message for result 5.

Actually you should. Result 5, kSecTrustResultRecoverableTrustFailure, indicates that the cert is self-consistent but not signed by a trusted root, so it can’t be trusted.

It sounds like you didn’t add your cert to the CBLReplication’s list of trusted root certs. Doing that should fix the problem.

Thanks Jens,

Why master branch has code so different to release you are going to issue?

But actually my goal is - it should work, also in 1.1.0 actually.

Again: yes, I have a SELF SIGNED certificate, we have test server, of course it is not trusted by root. Do I have a chance to avoid trust failure with self signed certificate, which is not trusted. I can send you link to our https:// page, if you want.

For Android there is a way with CouchBase Lite to “Trust all”, I did this. for iOS? I have also feeling that CBL does not get my certificate, since error message is the same if I comment addSelfSignedCertificate call.
What I am doing wrong?

My code:

- (void) addSelfSignedCertificate{
NSMutableArray *certs = [NSMutableArray array];
NSString *resourcePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"cert/server.cer"];
if (resourcePath) {
    NSData *certData = [NSData dataWithContentsOfFile:resourcePath];
    SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData);
    if (cert) {
        [certs addObject:CFBridgingRelease(cert)];
        [CBLReplication setAnchorCerts:certs onlyThese:NO];
    }

- (void) replicateDatabase:(NSString*)url withFilter:(NSString*)filter username:(NSString*)username password:(NSString*)password once:(BOOL)once toResult:(void(^)(NSString* result, NSError* error))returnResultBlock{
NSError* dbInitError = [self checkDatabaseInited];
if(!dbInitError){
    [self addSelfSignedCertificate];
    NSURL* remoteSyncURL = [NSURL URLWithString: url];
    CBLReplication *pull, *push;
    pull = singlePull = [database createPullReplication: remoteSyncURL];
    push = singlePush = [database createPushReplication: remoteSyncURL];
    push.continuous = pull.continuous = NO;

    if(username && ![username isKindOfClass:[NSNull class]] && ![username isEqualToString:@""]){
        id<CBLAuthenticator> auth;
        auth = [CBLAuthenticator basicAuthenticatorWithName: username password: password];
        pull.authenticator = push.authenticator = auth;
    }
    pull.filter = filter;
    push.filter = @"quotes";
    [[NSNotificationCenter defaultCenter] addObserver: self
                                     selector: @selector(replicationChanged:)
                                         name: kCBLReplicationChangeNotification
                                       object: push];
    [[NSNotificationCenter defaultCenter] addObserver: self
                                     selector: @selector(replicationChanged:)
                                         name: kCBLReplicationChangeNotification
                                       object: pull];
    [push start];
    [pull start];
    returnResultBlock(@"Done", nil);
} else {
    returnResultBlock(nil, dbInitError);
}

}

Because master is for 1.2. The 1.1.1 branch is a continuation of 1.1 with specific bug fixes cherry-picked to it. This is a pretty typical development workflow for a large project.

For Android there is a way with CouchBase Lite to “Trust all”, I did this.

Really? I wasn’t aware of such a method. That’s a bad idea, as it removes most of the security from SSL. You still have encryption, but no assurance that you’re talking to the real server.

My code:

Set a breakpoint and make sure the addAnchorCerts: call is actually being reached.

Double-check that the cert data matches the cert in use on your server.

Hi Jens,

Of course I tripple checked cert is added to the list and see that array has count 1 with data.
Thanks for link to code, now I see how my cert should be trusted. Maybe problem is in cert file itself?

I tried different site. For example take any https site in the world, which has approved CA certificate - no problem, I get an error “not found”, but connection works. And actually it is not needed to import any certificate in App, it works, how it should be.

Then I tried any site which has self signed certificate. Any, also without couch db. Not sure how to export certificate on mac, but I can export certificate on Windows, in DER format and with .CER extension. Add this certificate as per code above, and simply try to replicate with server, where I got this certificate.

Always I got an error SSL cert is not trustworthy (result=5). And it does not matters do I add cert in Array or not.

Do you have a working example with sample self signed certificate? Maybe cert I export in wrong format?

Yes you are completely right, this is a security risk, and I will never deploy my app in production without correct SSL. But for testing purposes we have only self signed…

The cert should be in raw binary (DER) format, not PEM or whatever.

The Couchbase Lite unit tests include replication tests against a Sync Gateway server using a known self-signed SSL cert; the test bundle contains a copy of the cert and adds it to the replication root-cert list just the way you’re doing. (Example)

Hi Jens,

Ok now I could export my self signed cert on mac, it has exactly the same format as SelfSigned.cer in your tests. It still does not work with the same error code: kSecTrustResultRecoverableTrustFailure.

I noted also that your test certificate issued for localhost. If I look to your sources version 1.1.1, then I could see you force trust if you get kSecTrustResultRecoverableTrustFailure for localhost. But of course not for all. That’s why probably your tests are OK for localhost, but did you try this code with real site with self signed certificate.

code:

if (result == kSecTrustResultRecoverableTrustFailure) {
// Accept a self-signed cert from a local host (".local" domain)
if (SecTrustGetCertificateCount(trust) == 1 &&
([host hasSuffix: @".local"] || [host hasSuffix: @".local."])) {
result = kSecTrustResultUnspecified;
}
}
if (result != kSecTrustResultProceed && result != kSecTrustResultUnspecified) {
Warn(@"%@: SSL cert is not trustworthy (result=%d)", self, result);
return NO;
}

can I send you my certificates? I have actually tried 2 different self signed hosts and certificates…

If the cert has been added to the trusted-roots list, the status should be kSecTrustResultProceed not kSecTrustResultRecoverableTrustFailure, as far as I know.

Sure, you can send me your cert (jens at couchbase) and I’ll try it out. Is the host publicly reachable?

I noticed that my self signed certificate with an IP address as the FQDN does work on iOS8.x
I checked the replication log and it is connecting via HTTPS.
But it fails on iOS9

@edumos, are you aware of App Transport Security? You’ll have to update your app’s Info.plist to allow self-signed certs.

@jens yes I’m aware but I should have pointed out in my previous post that the app is build for iOS8.4 with XCode 6.4 So in theory it should work on iOS9 without any adjustment. I did try adding the keys to the plist in order to disable ATS but as expected this did not have any effect.
So apparently setting the anchor certs in the CBLReplication is not working if the app is build for iOS8 (XCode 6.4) and deployed onto a iOS9 device.
I’m looking deeper into this problem but I hoped to find an answer in this forum.