I have been thinking about a lot lately on security and I thought of writing a little app where I can store important files in Dropbox safely and share them with my family. As an excuse to learn the deeper ends of encryption, I did a little hackathon during a plane ride to a trip to New York a couple of weeks ago. The goal was to write a little app that eventually I can turn into its own standalone product so that I can give it to my family instead of having them cracking through GPG or OpenSSL.

Before jumping into the nitty gritty, and because my blog lacks pretty pictures, here’s a pretty picture that I took during that drip.

My phone froze and shut off 2 mins after snaping this pic #minus15fahrenheit #nyc #empirestate

A photo posted by Juan Carlos Moreno (@jcrogel) on

Ok so back into what I learned. After a first few tries, I realized that Apple had deprecated OpenSSL (well deprecated support) since OSX 10.7 in favor of their own Security Framework

And just to have extra references to some basic concepts of encryption here are two useful resources to fully follow this post:

On my first attempt to use OpenSSL I quickly realized that the API is HORRIBLE and Apple’s is only slightly more human readable but it is quickly made worse by the fact that you can’t use it in any other platforms other than iOS and OS X.

Encrypting small data using Public Key Encryption

First I tried generating the key and certificate using the following commands:

1 openssl req -out certificate.cer -new -newkey rsa:1024 -keyout private_key.pem
2 
3 openssl pkcs12 -export -out private_key.p12 -inkey private_key.pem -nocerts

I failed

…turns out the security framework only likes files generated by itself so the workaround was to generate a certificate using Keychain access.

Then I used keychain to export a p12 and a cer file. Then I could start to encrypt data. More or less this is how the code looks.

 1 .....
 2     SecCertificateRef certificate = SecCertificateCreateWithData(kCFAllocatorDefault, ( __bridge CFDataRef)myCertData);
 3     if (certificate == nil) {
 4         NSLog(@"Can not read certificate from certificate.cer");
 5         return nil;
 6     }
 7 
 8     SecPolicyRef policy = SecPolicyCreateBasicX509();
 9     SecTrustRef trust;
10     OSStatus returnCode = SecTrustCreateWithCertificates(certificate, policy, &trust);
11     if (returnCode != 0) {
12         NSLog(@"SecTrustCreateWithCertificates fail. Error Code: %d", (int)returnCode);
13         return nil;
14     }
15     
16     SecTrustResultType trustResultType;
17     returnCode = SecTrustEvaluate(trust, &trustResultType);
18     if (returnCode != 0) {
19         return nil;
20     }
21     
22     SecKeyRef myKey = nil;
23     OSStatus status = SecCertificateCopyPublicKey(certificate, &myKey);
24     if ( status != 0 )
25     {
26         NSLog( @"SecCertCopyPubKey failed %d", status );
27         return nil;
28     }
29     
30     size_t cipherLen = SecKeyGetBlockSize(myKey);
31     void *cipher = malloc(cipherLen);
32     
33     status = SecKeyEncrypt(myKey, kSecPaddingPKCS1, [data bytes], [data length], cipher, &cipherLen);
34     
35     if ( status != 0 )
36     {
37         NSLog( @"SecKeyEncrypt failed %d", status );
38         free(cipher);
39         return nil;
40     }
41 
42     NSData *encryptedData = [NSData dataWithBytes:cipher length:cipherLen];
43     free(cipher);
44 ....

One thing I quickly learned is that I could not use public key encryption for files longer than the key itself. Meaning I could only encrypt very very little data so its not enough to store an image.

Encrypting small data using S/MIME

 1 .....
 2     NSBundle* myBundle = [NSBundle mainBundle];
 3     NSString* pkpath = [myBundle pathForResource:@"private_key" ofType:@"p12"];
 4     NSString* cert = [myBundle pathForResource:@"certificate" ofType:@"cer"];
 5     NSData *inP12data  = [NSData dataWithContentsOfFile: pkpath];
 6     
 7     SecCertificateRef certificateCA = nil;
 8     NSData *caData = [[NSData alloc] initWithContentsOfFile:cert];
 9     CFDataRef inPEMData = (__bridge CFDataRef)caData;
10     certificateCA = SecCertificateCreateWithData(nil, inPEMData);
11     
12     SecIdentityRef myIdentity;
13     SecTrustRef myTrust;
14 
15     OSStatus status = extractIdentityAndTrust((__bridge CFDataRef)inP12data, &myIdentity, &myTrust);
16     
17     CFDataRef encodedContentOutCF = NULL;
18 
19     status = CMSEncodeContent ( myIdentity, certificateCA, CFSTR("1.2.840.113549.1.7.1"), FALSE, 0, [data bytes], [data length], &encodedContentOutCF );
20 
21     if (status != 0 )
22     {
23         NSLog(@"Error Encoding Data %d", status);
24         return nil;
25     }
26     
27     NSData *encodedOut = (__bridge NSData *)(encodedContentOutCF);
28     
29 ....

This one worked nicely and I had some passed tests. I am sure that I probably can do it simpler using some other approach but it is useful to learn that under the hood Apple is not really using OpenSSL and thus bugs like Heartbleed didn’t affect apple computers.

Also one thing that I found interesting is that for the most part, their framework, being slightly more friendly than OpenSSL it forces you to store Keys/Certificates under Keychain…Anyway it was a little window in how things work at a deeper level inside the OS.