Certificate pinning on iOS simulators with iostrust

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
    }
}

Android Studio, Gradle and Android Annotations

I switched for some days from iOS to Android to start a new project and refresh a little bit my knowledge about the Android toolchain.

I’m not a first hour user of Java and I have to admit that I do not follow all news about new Java tools or features. I discovered IntelliJ and Gradle only 8 months ago. This was a very good discover and makes me forget old and disapointing experiences with Ant, Eclipse/Netbeans and XML configuration. I really thought XML was a part of the language. The announcement of Android Studio at the last Google I/O sounded like a resurrection of some of old projects on my hard drive disk.

Another bad experience with Android was the lot of boilerplate Java you have to write anytime you code GUI stuffs. In French GNU/Linux magazine I discovered the amazing android annotated library android annotations which do a million more than just avoid you to call the findViewById method. After a lot of time reading the Gradle documentation and browsing Stack Overflow topics about this building tool, I succeed in having a working environment with Android Studio, Gradle and AndroidAnnotations on my laptop (OSX 10.9). I’ll try to share and explain my build.gradle in this post.

Pay attention to the version of Android Studio, Android SDK tools and Gradle you’re using !

Here I am using Android Studio v0.4.2, Android SDK tools 19.0.1 and Gradle 1.9. I notice some problems by using the Gradle version provided with Android Studio. I rather download Gradle somewhere on my hard drive disk and configure Android Studio to use it (Android Studio -> Preferences).

Let’s try some explanations.

buildscript {
    repositories {
        mavenCentral()
    }
 
    dependencies {
        classpath 'com.android.tools.build:gradle:0.7.+'
    }
}
apply plugin: 'android'

Until here no magic, we tell Gradle we need some specifics dependencies to build our project.

repositories {
    mavenCentral()
}
configurations {
    apt
}

The configuration statement defines a dependency group 1. A named dependency group allows you to pick some informations later about the dependencies you define in this group. It is reprensented by an object implementing the Configuration interface 2.

This interface inherits a method named getAsPath which returns the classpath of the dependencies defined in the group as a String. We’ll use this method later to get the classpath of androidannotation annotation compiler.

We create a apt dependency group, to refer to the annotation processor dependencies.

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

Here we define the dependencies we need in each dependency groups. Within the compile group we add the libraries we need in our builded program. In the apt dependency groups, we add the library needed to process the androidannotations’ annotations.

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
        }
    }
}

Simply defines the android SDK we want to use and the minimal API version the application supports and the signing parameters.3

def getSourceSetName(variant) {
    return new File(variant.dirName).getName();
}

We define a simple helper function to retrieve the android source sets variant name.

android.applicationVariants.all { variant ->
    def aptOutputDir = project.file("build/source/apt")
    def aptOutput = new File(aptOutputDir, variant.dirName)
    println "****************************"
    println "variant: ${variant.name}"
    println "manifest:  ${variant.processResources.manifestFile}"
    println "aptOutput:  ${aptOutput}"
    println "****************************"
 
    android.sourceSets[getSourceSetName(variant)].java.srcDirs+= aptOutput.getPath()
 
    variant.javaCompile.options.compilerArgs += [
            '-processorpath', configurations.apt.getAsPath(),
            '-AandroidManifestFile=' + variant.processResources.manifestFile,
            '-s', aptOutput
    ]
 
    variant.javaCompile.source = variant.javaCompile.source.filter { p ->
        return !p.getPath().startsWith(aptOutputDir.getPath())
    }
 
    variant.javaCompile.doFirst {
        aptOutput.mkdirs()
    }
}

Let’s divide this in smaller parts:

android.applicationVariants.all { variant ->
	...
}

The changes are applied for all variants (Variant = Build Type + Product Flavor)4 of the application.

android.applicationVariants.all { variant ->
def aptOutputDir = project.file("build/source/apt")
def aptOutput = new File(aptOutputDir, variant.dirName)

println "****************************"
println "variant: ${variant.name}"
println "manifest:  ${variant.processResources.manifestFile}"
println "aptOutput:  ${aptOutput}"
println "****************************"

We first define a outputDirectory for the generated sources. He’re it is set to “build/source/apt” in the aptOutput variable and print some informations to STDOUT.

android.sourceSets[getSourceSetName(variant)].java.srcDirs+= aptOutput.getPath()

We add the generated sources to the current variants sources’ scope.

variant.javaCompile.options.compilerArgs += [
	'-processorpath', configurations.apt.getAsPath(),
    '-AandroidManifestFile=' + variant.processResources.manifestFile,
    '-s', aptOutput
]

For each variant, we access to the gradle java compilation task 5 of each variant and add some arguments to the compiler. Notice the call to getAsPath method as explained before to retrieve the classpath of the androidannotation’s compiler processor. We also set up, the output of processint to aptOutput

variant.javaCompile.source = variant.javaCompile.source.filter { p ->
        return !p.getPath().startsWith(aptOutputDir.getPath())
}

We are filtering the unnecessary files from the task of compiling java’s task.

variant.javaCompile.doFirst {
       aptOutput.mkdirs()
   }

Don’t forget to create aptOutput directory before start the compilation and it’s done.