Automatic Certificate Installation on XCode 7

Few months ago, I wrote a Ruby gem named iostrust to automate the installation of trusted certificate on the different iOS simulators.

This is really helpful when you have a development process that includes continuous integration.
In our case, we perform some tests with calabash each time someone commits to git and before starting the tests, we reset the simulator. As we are using HTTP pinning, we need to find a way to install automatically the certificates within the simulators.

iostrust does not work with XCode 7 anymore. I spent some time to find a solution.

I first wanted to follow the same strategy (see previous post) but I seemed to be really complex.

I noticed then that using the Accessibility API on OSX was giving access to the UI elements of the simulator and that it was possible to interact with them.

With this remarks plus some commands to launch/close the simulator application, I wrote a small Swift program named simcert that should solve the problem on XCode 7.

example

One .pkg file and some instructions are available on GitHub

Certificate pinning on iOS simulators with iostrust

03.02.2016 Update: iostrust seems to be broken for Xcode 7. I’m currently trying a new approach to fix this issue.

The other day, I published a post on some issues we had regarding certificate pinning and the TLS cache. While the calabash tests were executed, a new issue appears.

We have several backends to speak with. Each uses a SSL certificate, not necessarily a certificate signed by a root CA trusted by iOS.

As explained on the web, it is not a big deal to add a certificate as a trusted one on the simulators: simply drag and drop the concerned certificate on the simulator window.

Our calabash test sequences need to work with fresh new simulators and automatically reset them before starting. By resetting them, we loose all the previous work we did to add the trusted certificates.

I started looking if there was another way to automate the process.

By having used Charles Proxy in the past, I remembered that using the SSL proxying option required that you install a certificate generated by Charles as a trusted one on the simulator. I also remembered that Charles was able to handle it alone.

After looking within the Mac Ox X bundle of the app, I noticed a bash script with an interesting name: install-charles-ca-cert-for-iphone-simulator.sh.

By reading it, I understood that the trusted certificates are stored in a sqlite3 database somewhere in the simulator’s directories.

By Googling a little bit, I discover the ADVTrustStore github repository. It contains a script helping you to install certificates from the command line and a wonderful description on how are trusted certificates handled within the simulators.

I wanted to use the python script, but it does not import a certificate directly on all the simulators, it asks for each simulator it founds if you want to install it :(

Based on this work and the documentation provided, I wrote a gem that automates all the process.

To install it:

$ gem install iostrust

If you want to install a certificate as a trusted one, symply run:

$ iostrust add ./my_cert.cer

Now you have no excuse to avoid certificate pinning :)

Certificate Pinning issues with AFNetworking

The others day at work, we were confronted to a serious bug on the iOS app of a customer. Due to some security issues, we used https and a certificate key pinning method to avoid man in the middle attack.

To not reinvent the wheel, we use AFNetworking as our main network stack. This wonderfull API (thanks @mattt), we can use three different certificate pinning strategies with no effort.

During the development phase, the pinning was working really good out of the box and we were very confident for the production :)

Sadly, after releasing the app into production, we noticed a strange issue with the pinning process.

The delegate method connection:willSendRequestForAuthenticationChallenge: from the AFURLConnectionOperation was not called everytime, even if the credential storage was not used…

My colleague fell on this issue on Github that refers to an Apple Technical Q&A. We discover the existence of a low level cache regarding the TLS connection.

After reading that carefully, the problem was obvious.

We use AFNetworking to connect to our API. We also use an analytics tool that does not use AFNetworking and does not implement any certificate pinning strategy.

During the development phase, we use a stubbed server that fakes the data of the client. In this environment, both URLS have a different hostname. But in production, both URLs have the same hostname, only the paths differ.

When the analytics tool speaks to the endpoint api.endpoint.com/analytics, if the certificate is valid, it accepts the certificate but does not “pin” it. This answer will be cached for 10 minutes for any request reaching the api.endpoint.com hostname.

Then when AFNetworking will speak to api.endpoint.com/api, CFNetwork will use the previous cached response and will not call the connection:willSendRequestForAuthenticationChallenge and the certificate will not be pinned :(

To fix it, as preconised in the technical Q&A, we changed the analytics endpoint to prevent the use of the cache.

Morality: AFNetworking rocks. But are you only using the network through this library ? If not, check that some other component will not compromise your certificate pinning strategy.

Scrollable custom view inside a UIWebView

Displaying HTML content is very easy on iOS. Just grab a UIWebView and call loadHTMLString:baseURL or loadData:MIMEType:textEncodingName:baseURL: and enjoy the results. This component would fit your needs 90% of the time. But in some cases, having a simple UIWebView is not enough.

For professional reasons, I had to implement a mail reader like screen. On such a screen, there is a fixed portion on the screen that scrolls along with the portion rendering the HTML. I did not build such a view before and start thinking how I could make it.

After thinking a little bit, I first thought about implementing the fixed portion in HTML/CSS. No offense to those technologies, but they are not really my cup of tea.

I start searching on the Internet to see if someone founds a nice way implementing this without using private APIS.

Tweaking the internal UIWebBrowserView

Each UIWebView contains a UIScrolView. Inside this scroll view, the HTML is rendered by a Apple internal private UIView subclass named UIWebBrowserView. It is possible to get a reference to this subview.

To have a reference to it, simply loop over the subviews contained within the UIScrollView of the UIWebView:

for(UIView *subview in self.webView.scrollView.subviews)
        {
            NSString * className = NSStringFromClass([subview class]);
            if([className isEqualToString:@"UIWebBrowserView"])
            {
            	//YES ! Just grab a reference to it :)
                self.HTMLRender  = subview;

                //Set the frame of the view to fit your needs
                break;
            }
        }

Once you grab it, you can use and place this view by setting its frame.

This works great, but it has the smell of a big workaround :( Would it be possible to do it in a easier way ?

Do it thanks to the documentation

I read the article in the great objc.io concerning scroll views and the start of a solution appears by reading the part Tweaking the Window with Content Insets.

The main idea is to use the contentInset property of the UIScrollView of the UIWebView to create a space at the top of the UIWebView and fill it with some view.

It is better to see with an example. Let’s create a UIWebView subclass named AwesomeWebView. This will be a web view with a 100 points red view at this top.

#define TOP_SPACE_HEIGHT 100.0f

@interface AwesomeWebView : UIWebView
@end

All the work could be done in the initializer. The only thing to consider, is that adding a contentInset create a zone in the view where the coordinates are negatives.

- (id) initWithFrame:(CGRect)frame
{
    if(self = [super initWithFrame:frame])
    {
        [self setupScrollView:TOP_SPACE_HEIGHT]; // Setting the contentInset
    }
    return self;
}

- (void) setupScrollView:(CGFloat)headerHeight
{
	// Set the contentInset to create space for the top View
    self.scrollView.contentInset = UIEdgeInsetsMake(headerHeight, 0, 0, 0);

    // Set the contentSize
    self.scrollView.contentSize = (CGSize){CGRectGetWidth(self.frame), CGRectGetHeight(self.frame) + headerHeight};

    //Make the scroll indicator insets starts at the bottom of the top view
    self.scrollView.scrollIndicatorInsets = UIEdgeInsetsMake(headerHeight, 0, 0, 0);

    //Initialize the redTopView
    //-> Keep in mind that the zone up to the scroll view is negative.
    UIView * redView = [[UIView alloc] initWithFrame:CGRectMake(0, -headerHeight, CGRectGetWidth(self.frame), headerHeight)];
    redView.backgroundColor = [UIColor redColor];

    //Finally add it to the view;
    [self.scrollView addSubview:redView];
}

This methods seems to be better than the first one.

You can find an example of this method on github

Android annotations processor plugin

The other day I explained how to use Android Annotations with Gradle on Android Studio. There was a lot of Groovy to write before starting to program. I discover the android-apt plugin for Gradle which reduces my last post to boilerplate code. The excilys team advices to use it for building android annotations with Gradle.

This plugin does not target android annotations specifically but it helps you to work with android and annotation processors and make life easier on Android Studio.

Starting a new android application with Gradle and android annotations is simple:

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.7.+'
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.2'

    }
}
apply plugin: 'android'
apply plugin: 'android-apt'

repositories {
    mavenCentral()
}

configurations {
    apt
}


dependencies {
    compile 'com.android.support:appcompat-v7:+'
    compile 'org.androidannotations:androidannotations-api:3.0'
	
    apt 'org.androidannotations:androidannotations:3.0'
}

android {
    compileSdkVersion 19
    buildToolsVersion "19.0.1"

    defaultConfig {
        minSdkVersion 14
        targetSdkVersion 19
    }
    buildTypes {
        release {
            runProguard false
            proguardFile getDefaultProguardFile('proguard-android.txt')
        }
    }
    productFlavors {
        defaultFlavor {
            proguardFile 'proguard-rules.txt'
        }
    }
    signingConfigs {
        myConfig {
            storeFile file(project.property("MyProject.signing"))
            storePassword "xxxxxxxxx"
            keyAlias "yageek company"
            keyPassword "xxxxxxxxxx"
        }
    }

    buildTypes {
        release {
            signingConfig signingConfigs.myConfig
        }
    }
}

apt {
    arguments {
        androidManifestFile variant.processResources.manifestFile
    }
}