Protecting resources in iPhone and iPad apps

by Robin Summerhill on July 20, 2010

UPDATE: The example project has been updated to work with iOS5.

If you are distributing an iPhone or iPad app you may be giving away more than you realize. Resources that you embed in your application bundle can be extracted very easily by anyone who has downloaded the app with iTunes or has the app on their iOS device. This is not such a problem if those resources are just parts of your UI or the odd NIB file, but once you realise that your high-value resources, such as embedded PDFs, audio files, proprietary datasets and high-resolution images are just as accessible then you might want to consider how to protect those resources from prying eyes or unauthorised use outside your application.

To see just how accessible your resources are, use a tool such as iPhone Explorer to connect to your device. This tool lets you browse all the app bundles on your device and copy any resource to your Mac or PC. Alternatively, just locate the ipa file that is downloaded when you purchase an app with iTunes, change the extension to ‘.zip’ then unzip it. All your resources are on display.

Protection through encryption

In this article I’m going to walk through one approach to protecting your resources from unauthorised use outside your application. During the build phase, your high-value resources will be encrypted before being embedded in the app bundle. These resources can then be decrypted on-the-fly when required in your app. I had a number of goals in mind when developing the solution presented in this article:

  • Adding new resources to be encrypted should be quick and easy.
  • The encryption process should be completely automatic during the build phase.
  • Encrypted PDFs or HTML files must be useable in an embedded UIWebView
  • Decryption must be in-memory and not create temporary unprotected files

I also did not want to complicate the submission process by introducing encryption that might be subject to export restrictions, so we are limiting ourselves to using the CommonCrypto API, which is already distributed as part of iOS. The usual caveats apply when using any sort of encryption approach – given enough time and determination someone will bypass any protection scheme you can come up with. However, make it difficult enough to crack the protection compared to the value of the protected resources and it becomes a question of economics. If you are working with client-supplied resources then it becomes even more important to use a scheme such as the one described here to protect yourself from charges of negligence should those resources turn up on some torrent site.

An encryption command-line tool

So, down to the details. We’re going to create a simple command-line tool that will be run as part of the build process to encrypt resources using the AES256 symmetric cipher. An Xcode project to build the tool can be found at the bottom of this article. The tool takes command-line arguments specifying a 256-bit key, the input file path and an output file path. We will use a custom build step to call the command-line tool for each of our resources that we want to be encrypted.

Setting up our project

XCode project showing EncryptedResources folder and custom build step

Now we have a tool to perform the encryption, we can turn to an example project that makes use of the encryption to protect embedded resources. You can find an example Xcode project at the end of this article. This sample simply displays a UIWebView when run and populates it with protected HTML and image files, which are embedded as encrypted resources in the app.

The first step is to create a sub-folder under the project folder to contain the original resources that we want to protect. In the example we have called this sub-folder ‘EncryptedResources’. One of our goals was to make adding new resources as quick and easy as possible and when we have finished setting up the project we will be able to add a new protected resource simply by dragging it into this folder within Finder. As a cosmetic convenience I also added the folder to the Xcode project as a folder reference (by checking the ‘Create Folder References for any added folders’ checkbox) but please remember to deselect any target membership on the ‘Targets’ tab of the Info dialog for this folder or the unencrypted resources will be added to the app bundle by the regular ‘Copy Bundle Resources’ build step.

Adding a custom build step

To process the files in the EncryptedResources folder, we add a custom build step by selecting the ‘Project > New Build Phase > New Run Script Build Phase’ menu item in Xcode. In the example we have moved this build step to the start of the project target (under the Targets > EncryptedResourceDemo item in the ‘Groups and Files’ list) so that it is run as the first step in the build process. The shell script associated with this step can be viewed by selecting the ‘File/Get Info’ menu item when the step is selected:

DIRNAME=EncryptedResources
ENC_KEY="abcdefghijklmnopqrstuvwxyz123456"
 
INDIR=$PROJECT_DIR/$DIRNAME
OUTDIR=$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH/$DIRNAME
 
if [ ! -d "$OUTDIR" ]; then
  mkdir -p "$OUTDIR"
fi
 
for file in "$INDIR"/*
do
  echo "Encrypting $file"
  "$PROJECT_DIR/crypt" -e -k $ENC_KEY -i "$file" -o "$OUTDIR/`basename "$file"`"
done

The shell script iterates over every file in the ‘EncryptedResources’ folder and calls our ‘crypt’ tool for each file, placing the encrypted output in the application bundle. Note that the script expects the ‘crypt’ tool to be present in the base of the project directory. As an alternative, you could place the ‘crypt’ tool somewhere on your path and modify the script accordingly. Note also, that it is in the build script where we specify the key to use for encryption. The 256-bit key is specified as 32 x 8-bit characters – you should change it from the default given here.

Using protected resources in applications

So now we have a built app bundle containing our protected resources. The resources can still be viewed with tools such as iPhone Explorer or extracted from an iTunes ipa file but are useless without the correct decryption key. We now turn to how these resources can be used legitimately by your app. In creating a decryption framework, I wanted a scheme that would be as flexible as possible and not place an unnecessary burden on the developer every time she wanted to use a protected resource. I also needed a scheme that decrypted in memory and did not create temporary files (which could be viewed with iTunes Explorer while an app was running). I chose to create a custom URL protocol and extend the URL loading system. This allows us to use encrypted resources anywhere that a URL can be specified and, thanks to the widespread use of URLs in the iOS frameworks, we get a lot of bang for our buck with relatively little new code including:

  • Loading encrypted resources into memory with [NSData dataWithContentsOfURL:]
  • Viewing encrypted HTML files and images in UIWebView

Custom URL protocols

If you haven’t come across implementing custom URL protocols before and you are interested in finding out more then check out the iPhone Reference Library. By subclassing NSURLProtocol and implementing a few required methods we can grant the URL loading system the ability to load other types of resources beyond the standard built-in schemes (http:, https:, ftp: and file:).

Our NSURLProtocol subclass is called EncryptedFileURLProtocol and the implementation can be found under the EncryptedFileURLProtocol group in the example project. It implements a new URL scheme (encrypted-file:) that works like the standard file: scheme. A URL using this scheme specifies a file on the local system just like file:, however, the files will be decrypted on-the-fly when the resource is loaded.

A custom NSURLProtocol subclass must override the canInitWithRequest:. The URL loading system uses this method to determine which protocol can handle a particular request. The loading system asks each registered protocol in turn whether it can handle the specified request. The first protocol to return YES gets to handle the request. The implementation of our canInitWithRequest: method is shown below and will return YES if the requested URL scheme is ‘encrypted-file:’.

33
34
35
36
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
    return ([[[request URL] scheme] isEqualToString:ENCRYPTED_FILE_SCHEME_NAME]);
}

A custom NSURLProtocol must also override two additional methods, startLoading and stopLoading. These are called by the URL loading system at appropriate points when handling a request. Our startLoading method initializes the cryptographic engine and opens an NSInputStream to load the resource. The decryption key is also specified at this point. It is shared between all instances of our custom NSURLProtocol and needs to match the key used for encryption during the build process. The default key value is specified on line 20 of EncryptedFileURLProtocol.m and can be modfied using the class-level key property.

58
59
60
61
62
63
64
65
66
67
68
- (void)startLoading
{
    inBuffer = malloc(BUFFER_LENGTH);
    outBuffer = malloc(BUFFER_LENGTH);
    CCCryptorCreate(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, [sharedKey cStringUsingEncoding:NSISOLatin1StringEncoding], kCCKeySizeAES256, NULL, &cryptoRef);
 
    inStream = [[NSInputStream alloc] initWithFileAtPath:[[self.request URL] path]];
    [inStream setDelegate:self];
    [inStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [inStream open];
}

The stopLoading method is called when the request ends, either due to normal completion or an error condition. In our implementation we clean up the input stream, cryptographic engine and buffers:

71
72
73
74
75
76
77
78
79
80
- (void)stopLoading
{
    [inStream close];
    [inStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [inStream release];
    inStream = nil;
    CCCryptorRelease(cryptoRef);
    free(inBuffer);
    free(outBuffer);
}

Because we have scheduled our input stream on the current run loop and specified our custom instance of NSURLProtocol as the stream delegate, we will be called periodically with chunks of data to process. Below is an extract from the stream event handler where a chunk of data read from the input stream is decrypted and passed on to the URL loading system:

88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
  switch(streamEvent) {
        case NSStreamEventHasBytesAvailable:
        {
            size_t len = 0;
            len = [(NSInputStream *)inStream read:inBuffer maxLength:BUFFER_LENGTH];
            if (len)
            {
                // Decrypt read bytes
                if (kCCSuccess != CCCryptorUpdate(cryptoRef, inBuffer, len, outBuffer, BUFFER_LENGTH, &len))
                {
                    [self.client URLProtocol:self didFailWithError:[NSError errorWithDomain:ERROR_DOMAIN code:DECRYPTION_ERROR_CODE userInfo:nil]];
                    return;
                }
 
                // Pass decrypted bytes on to URL loading system
                NSData *data = [NSData dataWithBytesNoCopy:outBuffer length:len freeWhenDone:NO];
                [self.client URLProtocol:self didLoadData:data];
            }
            break;
        }
        ...

Implementing these four methods is nearly all we need to do to leverage the power of the URL loading system. The final piece of the puzzle is to register our custom NSURLProtocol subclass with the system. This is done in the application:didFinishLaunchingWithOptions: method of our application delegate (in EncryptedResourceDemoAppDelegate.m):

30
31
    // Register the custom URL protocol with the URL loading system
    [NSURLProtocol registerClass:[EncryptedFileURLProtocol class]];

Using the encrypted-file: URL scheme

Now that we have implemented our custom NSURLProtocol subclass and registered it with the URL loading system we can start using URLs that use the encrypted-file: scheme. The example application demonstrates one way to do this – that is to use the scheme to load protected resources into a UIWebView instance. This is actually not very different from using a regular file: URL to load and view an embedded HTML resource. The code from the viewDidLoad method of our main view controller is shown below (from EncryptedResourceDemoViewController.m):

21
22
23
24
25
26
27
28
29
30
31
- (void)viewDidLoad {
    [super viewDidLoad];
 
    webView.scalesPageToFit = YES;
 
    NSString *indexPath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html" inDirectory:@"EncryptedResources"];
 
    NSURL *url = [NSURL encryptedFileURLWithPath:indexPath];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [webView loadRequest:request];
}

The app bundle is queried for the path to ‘index.html’ in the EncryptedResources sub-folder. This is then used to construct an NSURL. An NSURLRequest is created from the URL and a loadRequest: message is sent to the web view. If you are familiar with NSURL and its class methods then you may have spotted the unfamiliar encryptedFileURLWithPath: method. I have extended NSURL using a category to add this method as a convenience. It works just like fileURLWithPath: but creates a URL using the encrypted-file: scheme rather than the regular file: scheme. One cool benefit of extending the URL loading system is that any relative URLs referenced in the HTML, such as the src parameter of IMG elements, will also use the encrypted-file: protocol and will be decrypted on-the-fly.

As mentioned above, URLs are used in many places in the iOS frameworks. To load an encrypted resource into memory you can do the following:

    NSString *indexPath = [[NSBundle mainBundle] pathForResource:@"..." ofType:@"..." inDirectory:@"EncryptedResources"];
 
    NSURL *url = [NSURL encryptedFileURLWithPath:indexPath];
    NSData *data = [NSData dataWithContentsOfURL:url];

This could used to create a UIImage from an encrypted image file using [UIImage initWithData:] or you could go one step further by extending UIImage using categories to implement a initWithContentsOfEncryptedFile: method.

Next steps

In this article I have presented a scheme for protecting the resources that you embed in your iPhone and iPad applications. Along the way we have also learned about how to use the CommonCrypto API and how to implement a custom URL protocol. The example project demonstrates how to use the scheme and you are free to use the ‘crypt’ command-line tool and EncryptedFileURLProtocol source in your own projects. Something you might want to think about (depending on the value of the resources you are trying to protect and your level of paranoia) is a mechanism for obfuscating the decryption key. With the current scheme the key is compiled into the application binary as a plain string and could be extracted by anyone with a hex editor, a little patience and a little knowledge. Of course, this assumes that they have worked out that the resources are encrypted using AES256.

We’d love to hear if you have found this article useful or even used it in your own projects.

Share and Enjoy:
  • Print
  • email
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Reddit
  • Twitter

{ 157 comments… read them below or add one }

Stephan Burlot July 29, 2010 at 7:33 am

Your article is very interesting, but are you sure you don’t need all the paperwork asked by Apple when using encryption?

Reply

Robin Summerhill July 29, 2010 at 8:23 am

This question comes up weekly on devforums.apple.com. None of the Apple representatives are prepared to give legal advice on the forum but the general consensus is that you only need to answer ‘yes’ to the first question ‘Does your product contain encryption?’ if you are implementing new encryption functionality.
If you link to the encryption routines contained in the CommonCrypto library then you are making use of encryption provided by Apple, which should be already covered by a CCATS.

Reply

Stephan Burlot July 29, 2010 at 8:35 am

Ah, thanks, I will try to see what happens.

Reply

Ricardo Palma August 9, 2010 at 12:46 pm

Hello Stephan,

I would be very much interested on knowing what the response from Apple was when you attempted registering your applicaction.

Thanks!

Reply

Dan August 28, 2010 at 12:08 pm

Great writeup, thanks a lot! I’m not familiar with shell scripting and I’m getting errors when I try to build my app. I think they are related to spaces in my project’s path. Other than renaming my folders is there a simple fix for this?

/Volumes/Macintosh HD/Documents/iPhone Apps/App Name/AppName/AppName Files/AppName 2_0/build/AppName.build/Distribution-iphoneos/AppName.build/Script-370BB4E4122962B80047442C.sh: line 9: [: too many arguments

Encrypting /Volumes/Macintosh

/Volumes/Macintosh HD/Documents/iPhone Apps/App Name/AppName/AppName Files/AppName 2_0/build/AppName.build/Distribution-iphoneos/AppName.build/Script-370BB4E4122962B80047442C.sh: line 16: /Volumes/Macintosh: No such file or directory

Reply

Robin Summerhill August 28, 2010 at 8:57 pm

Hi Dan,

Glad you liked the article. You were right with your guess about spaces in your project’s path. I’ve updated the shell script in the article and the zip file to add quotes around all the filenames to solve this issue. Thanks for pointing it out to me.

Reply

Dan September 3, 2010 at 10:26 am

Thanks for the reply. One more thing – I’m getting a bunch of “Synchronous client exited with no response and no error!” messages in the debugger console when I load files that have been encrypted. Any idea what that’s about?

Reply

Robin Summerhill September 7, 2010 at 8:30 am

Not sure what that message means. I’m not seeing it when running on the simulator or the device. Google has a few hits for that message but no-one seems to know what it means. Are you seeing this in your own code or the example project? Simulator or device? Are the files loaded correctly despite the message?

Reply

Sarah Clough March 30, 2011 at 9:27 am

I just had this and it was because I had changed the key in the script but not in the URL class.

Reply

Blake September 26, 2012 at 3:35 am

I am now getting this on ios 6…no clue why

Alan August 30, 2010 at 12:41 pm

Hi Robin,

I try to work with your code. But decryption method with NSData don’t working.

NSData *data = [NSData dataWithContentsOfURL:url];

Result of the above statement is NULL. Encypting data isn’t decrypted. All function in your NSURLProtocol class isn’t called.

So, what is problem in this case?

Reply

Robin Summerhill August 30, 2010 at 12:53 pm

Have you registered the custom protocol by calling:
[NSURLProtocol registerClass:[EncryptedFileURLProtocol class]];

If you have done this correctly then you should be able to put a breakpoint on the canInitWithRequest: method and see it return ‘YES’ when you request the system to load an ‘encrypted-file:’ URL.

Reply

Athrun August 27, 2012 at 11:45 pm

Hi robin, many thanks for your great post.

I have the same issue with Alan, I’m sure that i have [NSURLProtocol registerClass:[EncryptedFileURLProtocol class]]; in my code.

Project works with load index.html in web view, but not works with [NSData dataWithContentsURL]. in NSData case ,all functions in NSURLProtocol class isn’t be called.

Do you have any idea on that? Thanks

Reply

John Kelley September 2, 2010 at 2:16 pm

Great post. This is one of the better solutions I have seen on the subject. Curious, though. Is this a iOS 4.0 only solution? I think the apple docs list NSStreamDelegate Protocol (used in EncryptedFileURLProtocol.h) as making its debut in 4.0.

Thanks for sharing.

Reply

Robin Summerhill September 2, 2010 at 3:16 pm

Glad you found the article interesting. The delegate method stream:handleEvent: has been around as part of an informal protocol since OS2.0, which was formalized as the NSStreamDelegate protocol in OS4.0. To compile prior to OS4.0 just remove the reference to NSStreamDelegate in EncryptedFileURLProtocol.h. I may get around to adding conditional compilation for this at some point.

Reply

Francesc November 23, 2010 at 6:58 am

Your solution looks really good. I’ve tryed your example in iOS 4 devices and simulator and works great, but I can’t make it work with a 3.1.3 iOS device. I’ve removed the NSStreamDelegate reference you mentioned, but it looks like when EncryptedFileURLProtocol is finished something goes wrong loading the webview.

These are the last thread-trace printed:
#0 0x33ad1d0c in WTF::HashTable<WebCore::String, std::pair<WebCore::String, WTF::PassRefPtr (*)(WebCore::SharedBuffer*)>, WTF::PairFirstExtractor<std::pair<WebCore::String, WTF::PassRefPtr (*)(WebCore::SharedBuffer*)> >, WebCore::CaseFoldingHash, WTF::PairHashTraits<WTF::HashTraits, WTF::HashTraits<WTF::PassRefPtr (*)(WebCore::SharedBuffer*)> >, WTF::HashTraits >::contains<WebCore::String, WTF::IdentityHashTranslator<WebCore::String, std::pair<WebCore::String, WTF::PassRefPtr (*)(WebCore::SharedBuffer*)>, WebCore::CaseFoldingHash> >
#1 0x33ad1cd0 in WebCore::ArchiveFactory::isArchiveMimeType
#2 0x33ba20ec in WebCore::FrameLoader::committedLoad
#3 0x33ba2020 in WebCore::DocumentLoader::commitLoad

Any suggestions?

Thanks!

Reply

Zaygraveyard July 6, 2011 at 4:25 am

it hapened to me too and this is what i did:

#if __IPHONE_3_2 <= __IPHONE_OS_VERSION_MAX_ALLOWED
if (kCFCoreFoundationVersionNumber_iPhoneOS_3_2 kKeySize ? kKeySize : keyLength);

size_t bufferSize = [self length] + kCCBlockSizeAES128;
void* buffer = malloc(bufferSize);

size_t dataUsed;

CCCryptorStatus status = CCCrypt(decrypt ? kCCDecrypt : kCCEncrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
key, kKeySize,
NULL,
[self bytes], [self length],
buffer, bufferSize,
&dataUsed);

switch(status) {
case kCCSuccess:
return [NSData dataWithBytesNoCopy:buffer length:dataUsed];
case kCCParamError:
NSLog(@”Error: NSData+AES256: Could not %s data: Param error”, decrypt ? “decrypt” : “encrypt”);
break;
case kCCBufferTooSmall:
NSLog(@”Error: NSData+AES256: Could not %s data: Buffer too small”, decrypt ? “decrypt” : “encrypt”);
break;
case kCCMemoryFailure:
NSLog(@”Error: NSData+AES256: Could not %s data: Memory failure”, decrypt ? “decrypt” : “encrypt”);
break;
case kCCAlignmentError:
NSLog(@”Error: NSData+AES256: Could not %s data: Alignment error”, decrypt ? “decrypt” : “encrypt”);
break;
case kCCDecodeError:
NSLog(@”Error: NSData+AES256: Could not %s data: Decode error”, decrypt ? “decrypt” : “encrypt”);
break;
case kCCUnimplemented:
NSLog(@”Error: NSData+AES256: Could not %s data: Unimplemented”, decrypt ? “decrypt” : “encrypt”);
break;
default:
NSLog(@”Error: NSData+AES256: Could not %s data: Unknown error”, decrypt ? “decrypt” : “encrypt”);
}

free(buffer);
return nil;
}

- (NSData*)decryptedWithKey:(NSData*)key {
return [self makeCryptedVersionWithKeyData:[key bytes] ofLength:[key length] decrypt:YES];
}

- (id)initWithContentsOfEncryptedFile:(NSString *)path withKey:(NSData *)key {
[self release];
NSData *encryptedData = [[NSData alloc] initWithContentsOfFile:path];
if (!encryptedData) return nil;
self = [[encryptedData decryptedWithKey:key] retain];
[encryptedData release];
return self;
}

i hope it helps and if you need any info plz ask :)

Reply

Zaygraveyard July 6, 2011 at 4:40 am

I realized that I didn’t thank you Robin for this great post so thanks and I found that I forgot to put important code in the last comment so here’s the link to my project which is an updated version of yours “http://dl.dropbox.com/u/20989950/EncryptedResourceDemo.zip”
and if you find any thing I did wrong plz let me know.
Thx again Robin for this.

Reply

mike p September 13, 2010 at 3:11 pm

Any ideas on weather this would work for the MPMoviePlayer(View)Controller? I’m using the automatic view controller, the view controller seems to load but doesn’t play the video. I’ve gotten the code to work without encryption to make sure everything is set up right, second snippet of code, and it does.

code that doesn’t work with sample code

NSString *indexPath = [[NSBundle mainBundle] pathForResource:@”VideoFull” ofType:@”mov” inDirectory:@”EncryptedResources”];
NSURL *url = [NSURL encryptedFileURLWithPath:indexPath];
moviePlayer = [[MPMoviePlayerViewController alloc] initWithContentURL:url];
[self presentModalViewController:self.moviePlayer animated:YES];

code that works using sample code

NSString *url = [[NSBundle mainBundle] pathForResource:@”VideoFull” ofType:@”mov”];
NSURL *videoURL = [NSURL fileURLWithPath:url];
moviePlayer = [[MPMoviePlayerViewController alloc] initWithContentURL:videoURL];
[self presentModalViewController:self.moviePlayer animated:NO];

thanks ahead of time!!!

Reply

Robin Summerhill September 15, 2010 at 9:24 am

Hi Mike,

It looks like MPMoviePlayerViewController is bypassing the URL loading system by using lower-level Core Foundation APIs. The canInitWithRequest: method of the custom URL protocol is not being called at all.

An alternative approach here would be to include a minimal web server in your app that decrypts resources on-the-fly and serves them up to local requests. I may add an article on how to do this in the near future.

Reply

mike p September 15, 2010 at 10:58 pm

Ok thanks for looking into it. I’m avoiding hosting anything on a server, so will probably look into not using the ready built movie view controller. Will let you know if I come across anything.

Reply

Arnold G March 3, 2011 at 10:36 pm

Good article. I would be interested in reading an article on using an intermediate web server for on-the-fly decryption if you ever write it. I suspect Netflix is doing something similar in their app to map between streaming protocols.

Reply

Brian Chung March 15, 2011 at 5:05 am

Hi Robin,

Are there anyway to decrypt mp4 files and load it into MPMoviePlayerViewController without using web server? In my case, all mp4 files will be encrypted and stored in the bundle.

Regards,
Brian Chung

Reply

stefan e April 14, 2011 at 10:43 am

hi, a tutorial about this would be greatly appreciated. we are facing the same problems are currently trying to get cocoahttpserver to run in our app to be able to decode the video and stream it locally, not funny :)

cheers

Stefan

thomas May 17, 2011 at 12:46 pm

Have you had time yet to look into the issue in more detail?

I tried it myself with a MPMoviePlayerController and have the same problem. In my case, canInitWithRequest is called though. Also, startLoading, stopLoading and stream methods are called, so there seems to be happening something. Unfortunately, the media player doesn’t play anything.

Reply

Lars February 10, 2012 at 10:30 am

Robin, great post!
Has anybody got this working with video files?
I am very interested!

Reply

Nathan B. January 25, 2013 at 12:03 am

I realize this post is old but it is still very much relevant and I wanted to share a solution. While efficiency can be questioned it satisfied the immediate need; and performing steps 1-3 via Grand Central Dispatch I did not find a massive performance hit.

Using the project posted by Akhil P.K December 13, 2012 at 1:29 am, here are the steps I used to leverage the hard work of the author (Robin) and bypass some of the limitations of MPMoviePlayerViewController:

1. Obtain the file URL for the encrypted movie:

NSURL *url = [NSURL encryptedFileURLWithPath:[[NSBundle mainBundle] pathForResource:@”video” ofType:@”mov” inDirectory:@”EncryptedResources”]];

2. Using the URL load the video into an NSData Object:

NSData *movideData = [NSData dataWithContentsOfURL:url];

3. At this point due to the custom protocol the content is decrypted therefore we now store the NSData blob to a file within the our apps /tmp directory:

NSString *destinationPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"decrypted.mov"];
BOOL isFileSaved = [movideData writeToFile:destinationPath atomically:YES];

4. If the file saved properly we can use the destination path and make a NSURL for the MPMoviePlayerViewController:

NSURL *url_ = [NSURL fileURLWithPath:destinationPath];

5. Once the URL is created we can load the decrypted file with MPMoviePlayerViewController, setup prefs, and play the video:

MPMoviePlayerViewController *player_ = [[MPMoviePlayerViewController alloc]initWithContentURL:url_];
player_.moviePlayer.allowsAirPlay = YES;
player_.moviePlayer.controlStyle = MPMovieControlStyleFullscreen;
player_.moviePlayer.fullscreen = YES;
player_.moviePlayer.shouldAutoplay = YES;

Silvia August 6, 2012 at 6:27 pm

Hi Robin,
Did you post any article about this later? I couldn’t find that…
Thanks

Reply

Stephen Prance February 28, 2011 at 8:21 pm

Hello Robin,

I am writing an app which essentially has a navigation controller (based on the Elements by Apple) pushing selected pages of a pdf (based on the Zooming PDF Viewer by Apple). Your solution looked just the trick to secure the pdf.

I am pretty sure my file NSW_CAG_EDITION_14_JUL_09 is encrypting OK on build. However crashing when I try to decrypt and view.

I put your code in pdfscrollview.m (where CGPDFDocumentCreateWithURL is called)

NSString *indexPath = [[NSBundle mainBundle] pathForResource:@”NSW_CAG_EDITION_14_JUL_09″ ofType:@”pdf” inDirectory:@”EncryptedResources”];

NSURL *url = [NSURL encryptedFileURLWithPath:indexPath];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
pdf = CGPDFDocumentCreateWithURL((CFURLRef) request); //crashes here

Will your code work with CGPDFDocumentCreateWithURL? Any help would be greatly appreciated.

Cheers

Steve

Reply

Robin Summerhill March 4, 2011 at 7:37 am

Hi Steve,

Since originally writing the article it has come to my attention that certain APIs bypass the URL loading system and access the file system at a lower level. This includes the C-based PDF API. The method presented in the article works for displaying PDFs in an embedded UIWebView but the C API calls will fail, as you have found. Luckily for you, PDFs support encryption natively. You can encrypt your PDF in the application you used to author it, or encrypt it later in Acrobat Pro. Then open the PDF with CGPDFDocumentCreateWithURL and supply the password with CGPDFDocumentUnlockWithPassword.

I plan to update the article soon with some extra enhancements and to cover issues such as this.

Reply

Wilson March 24, 2011 at 10:32 am

Hi Steve,

great post.
When do you plan to update the article?
Right now i need to use encrypted PDFs. The problem is, that i need to encrypt them on device (no authoring software) and because of customer restrictions i cannot decrypt them to filesystem for reading. So i have to use the URL loading system. Is there any way to force CGPDF* to use the URL loading system?

Thanks
Wilson

Reply

JD March 1, 2011 at 5:28 pm

Hi,
Great work! I try it on png files with UIImage initWithData, works well.
But what I want to do exactly the same for sqlite file, I try it and don’t work.
I do something like that:

NSString *indexPath = [[NSBundle mainBundle] pathForResource:@”MyDBFile” ofType:@”sqlite” inDirectory:@”encrypted”];
NSURL *storeURL = [NSURL encryptedFileURLWithPath:indexPath];
[persistentStoreCoordinator_ addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error];

Any ideas ?

Reply

Robin Summerhill March 4, 2011 at 7:46 am

Hi JD,

Since originally writing the article it has come to my attention that certain APIs bypass the URL loading system and access the file system at a lower level. This includes the C-based SQLite API, which underlies the Core Data storage that you are using. Working with SQLite is also a slightly more complicated proposition than just loading a resource file because random access is used rather than bringing the whole database into memory. I’m sure it would be possible to intercept the data flow somewhere in the chain from database, through Core Data to the application level to perform automatic decryption. It’s something we might look into in future.

Regards,
Robin

Reply

Arpit April 5, 2011 at 1:29 pm

Hi Robin,
Thanks for the awesome solution. But we are having problems with loading of ajax files. We are using jquery to load the files lazily in the app using ajax. However $.ajax is always resulting in Error. Have you tried this with Ajax ?

Reply

Arpit April 6, 2011 at 9:12 am

Figure out a solution for Ajax requests. Safari has issue with cross domain requests if you use encrypted-file:// So Just changed encrypted-file to “file” and all ajax requests works seamlessly.

Reply

Robin Summerhill April 6, 2011 at 10:12 am

OK. Sounds like XMLHttpRequest is looking at the protocol and switching behaviour based on ‘http’ or ‘file’. It doesn’t recognise ‘encrypted-file’ as a valid protocol so is barfing. Good to know.

Reply

Luke April 23, 2013 at 10:51 am

I am using this in a Mac OS X project instead of iOS, everything works but the AJAX doesn’t work. I tried changing the schema constant from encrypted-file: to file: but when that is changed the files are not decrypted. The startLoading, stopLoading and stream methods don’t seem to fire even when canInitWithRequest is returning true for these requests. Any ideas?

Reply

Luke April 24, 2013 at 2:07 am

If anyone else is having trouble with this on Mac OS X using NSWebView with AJAX you can use [WebView registerURLSchemeAsLocal:ENCRYPTED_FILE_SCHEME_NAME]; to set the WebView so that your custom scheme is treated as local in regards to security. Not sure if UIWebView supports this on iOS though.

Reply

Marc A. April 29, 2011 at 4:43 am

Hi,
Great work! I tried the code below but it doesn’t work.
Any idea of what’s wrong with my code?

-(IBAction)OpenIN:(id)sender {

NSString *fileToOpen = [[NSBundle mainBundle] pathForResource:@”…” ofType:@”…” inDirectory:@”EncryptedResources”];
NSLog(@”%@”,fileToOpen);
self.controller = [UIDocumentInteractionController interactionControllerWithURL:[NSURL encryptedFileURLWithPath:fileToOpen]];
self.controller.delegate = self;

CGRect navRect = self.navigationController.navigationBar.frame;
navRect = CGRectMake(348.0f, 870.0f, 0.0f, 0.0f);
[self.controller presentOptionsMenuFromRect:navRect inView:self.view animated:YES];
}

Thanks for your help!
Marc

Reply

Rodrigo May 6, 2011 at 4:15 pm

Hi!
It’s possible to change the name of the folder “EncryptedResources”? If so, how can I change that?
Tks a lot.

Reply

Robin Summerhill May 6, 2011 at 6:11 pm

Yes, it’s easy.

1. Change the name of the directory where you place your resources to be encrypted from ‘EncryptedResources’ to whatever you want.
2. Change the first line of the encryption script attached to the ‘Encrypt Resources’ custom build step:
DIRNAME=[the directory you chose in step 1]
3. Change the directory name wherever you load an encrypted resource in your app:
e.g. NSString *indexPath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html" inDirectory:@"EncryptedResources"];

Reply

Rodrigo May 6, 2011 at 7:35 pm

great!

But I’m still having problems with the last step (Using the encrypted-file: URL scheme). I have an xml file and I couldn’t show the data because the file is empty and don’t even starts the process of parse. Do you have any suggest?

Tks a lot again

Reply

Rodrigo May 6, 2011 at 7:46 pm

Sorry, I forgot one detail. Appears this text in console: Synchronous client exited with no response and no error!

Reply

Robin Summerhill May 7, 2011 at 8:51 am

This may be a separate issue. See the comments up above. Have you changed the encryption key in the script but not at the top of EncryptedFileURLProtocol.m? They need to match.

Otherwise you will need to work out which step is failing – is it not locating the file correctly or is it not decrypting it? Try changing ‘encrypted-file:….’ to just a plain ‘file:….’ and see if the encrypted file is loaded – view the encrypted contents in the debugger.

Daoxin July 2, 2012 at 9:14 am

Hi, Excellent article!
I want to use this technology to my project.
I have one html and one mp3 file, but I can not play mp3 file.
Here is index.html source.

If you are reading this, it is because your browser does not support the audio element.

Looking forward to your answer.

Reply

John May 11, 2011 at 2:33 pm

Hi!
Are there any news about sending an app with this encprytion? Do I have to specify? Apple is accepting with no problems?
Tks and congratulations for this article

Reply

Mario May 23, 2011 at 9:14 am

Hello, I am getting the same error on the console…any other things we need to look into which might be incorrect?

Reply

Mario May 23, 2011 at 9:21 am

The file is encrypted in the EncryptedResources folder of the Game.app file. Although the original file still exists in the root directory of the Game.app file. I have removed it from the “copy bundle resources” in the build phases.

Thanks for help

Reply

shri September 16, 2011 at 6:30 am

i m able to play 1 song using mediaplayer framework but next song not play
plaza give me some step to play.

thanks in advance

Reply

shri September 22, 2011 at 1:03 am

hi,
very great demo apps for encription. lastly my my encription is done from this demo but i m not able play more then one song at a time. if i copy more song into the encryptedResource folder that can not be played. why?
plz. give me some steps for playing these audios.
thanks

Reply

Sapana September 30, 2011 at 8:30 am

Hello ,
This is the best way to hide the images and song of application . But i have one major problem , I have added some song in the EncryptedResources folder and play this song on simulator it was play . Same application i have tested on iPhone device at that song was not play. Please help its very urgent for me.
Thank you so much

Reply

Arpit Jain October 5, 2011 at 2:02 pm

Any update for iOS5. Our application is working on 4.3.2 but has errors for iOS 5 GM release. I am trying to debug the issue, but if you have already worked on it, it will save me time :-)

Will update here if I find the solution or at least the issue.

Reply

Andrew Jackman October 5, 2011 at 5:23 pm

Yep same here Arpit. Doesn’t work for iOS 5. Major issue for me, as I have an app in the field with encrypted resources that I need to fix.

Here are the kinds of errors I get:

ImageIO: PNG Extra compressed data
ImageIO: PNG invalid stored block lengths
ImageIO: PNG incorrect data check
ImageIO: PNG PNG unsigned integer out of range

Any suggestions would be greatly appreciated.

Andrew

Reply

Robin Summerhill October 5, 2011 at 7:30 pm

We’re pretty busy at the moment fixing some iOS5 issues with our apps but I’ll take a look at the problem here.

Did you see the problem with the early betas or has it just appeared in the GM release?

Reply

Robin Summerhill October 5, 2011 at 11:23 pm

I’ve fixed the problem on iOS5. The example project has been updated. The single change is line 99 of EncryptedFileURLProtocol.m. It should read:

NSData *data = [NSData dataWithBytes:outBuffer length:len];

Reply

Andrew Jackman October 6, 2011 at 2:04 am

Thanks for the prompt response. Really appreciated. Unfortunately when I make the change I get the following:

malloc: *** error for object 0xb5fe00: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug

Reply

Andrew Jackman October 6, 2011 at 3:32 am

Sorry – my mistake. All looking good now. Thanks very much!

Arpit Jain October 6, 2011 at 4:53 am

Thanks for the prompt response Robin. This is working perfectly.
Although, we again are facing problems with Ajax requests and files requested through ajax are not getting loaded. We are working on it.

If you have any suggestion for this, it will be greatly appreciated.

Reply

Arpit Jain October 6, 2011 at 5:02 am

Just FYI, its working in iOS 4.3.

It is just not working in iOS5. After [inStream open] is called, control is not reaching the “stream” method. Any suggestions?

shridhar October 10, 2011 at 1:16 am

its great ………
i have some song in EncryptedResource folder , encryption done successfully but not played into AVAudioPlayer. its show me right song path of EncryptedResources but not play.
please give me the right way

thanks in advance.
shridhar mali.

Reply

Daniel Shaw October 14, 2011 at 8:26 pm

Hi Robin,
I realize the original post is over a year old, but this is fantastic!. A friend and I are working on a Universal app and need this mechanism to protect a licensed font we will be placing it it. I had to do a little bit of extra work from the default, in order to make the custom font available to UIFont, so I thought I would post this message if anyone else needs to do something similar
In addition to the NSURL, you will need to manually read the font data into an NSData object, then use CoreGraphics and CoreText to load the font (or fonts) into the lower level font manager. Once this is complete, you can initialize a UIFont with the font name e.g.:
NSURL *fontsURL = [NSURL encryptedFileURLWithPath:indexPath];
CFErrorRef error; //used my the CoreText font manager to record any errors.
NSData * fData = [NSData dataWithContentsOfURL:fontsURL];

CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)fData);
CGFontRef fr = CGFontCreateWithDataProvider(provider);
CTFontManagerRegisterGraphicsFont(fr,&error);
UIFont * calendar = [[UIFont fontWithName:@"calendar" size:25] retain];

These are iOS documented API calls and should not cause any app rejection.

Reply

Robin Summerhill October 19, 2011 at 8:08 am

Thanks for the great tip about fonts. Glad you liked the article too.

Reply

Jaanus May 13, 2012 at 8:01 pm

Thanks so much for both the original article, and for this extra info about fonts! Very useful and works great. I thought I could simplify this by using CTFontManagerRegisterFontsForURL, but sadly, this again bypasses the custom URL business, so it has to be done exactly as shown in this comment.

I’m also getting the “Synchronous client exited with no response and no error!” indicated by others when I load each font. Seems to be just a warning in the output and doesn’t affect anything.

Reply

Sam Becker June 22, 2012 at 9:00 pm

Hi Daniel/Robin,

I just tried to do what you did and I keep getting OT Font Face Error: 2 messages. I was able to load the fonts fine using CGFontRef before encryption but not after. ALSO I had to change the file naming scheme from encrypted_file l_something_or_other to ‘file’ just to get as far as the OT error. Any ideas? If I didn’t change the name scheme CGFontRef would say it was an unrecognized file type/scheme.

Thanks,

Sam

Reply

Sam Becker June 25, 2012 at 11:21 am

I figured out what I was doing wrong. I had forgotten to uncheck the ‘Targets’ tab for encrypted resources folder. For some reason this caused the font to fail to load ONCE after each build > clean. After that, every compile worked. Same for simulator AND device. Anyway, this made it impossible to debug. Unchecking the ‘Target’ tab was a lifesaver. Sam

Reply

John Grøtting August 9, 2012 at 10:24 am

We are trying to do something similar, but so far without success. Rather than rendering text using CoreText, we are using UIWebView and rending all text using @fontface in HTML5. Any thoughts?

Reply

Adi November 11, 2011 at 7:10 am

I know that this is an old post, but I’ve used your method recently and had some problems using it on iOS 3.1.3
The easy fix (if anyone is interested) is to call the
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
also when all response is received in your NSURLProtocol subclass. The response can be some bogus NSURLResponse instance, and it will work .

Thanks for the great idea, btw !

Reply

Mudireddy Suresh December 27, 2011 at 6:14 am

Hi Robin Summerhill,
Great solution. It helps me a lot.

I observed one issue with the sample app as well as my application where if requested for a encrypted image
URL in NSURLRequest with UIWebView then all the decrypted 512 byte chunks were provided to UIWebView in NSStreamDelegate but finally the raw image buffer shown in the UIWebView.

What is the issue ? Please let me know if anything need to be handled.
This is the case for image, PPT ..etc (Direct URL without any HTML file reference to them).

Reply

Mudireddy Suresh December 28, 2011 at 6:00 am

Hi Robin,
Thanks for the nice stuff.

My previous issue which I was posted here was solved with
[self.client URLProtocol:self didReceiveResponse:response ] with proper MIME type & encoding type.

Now the new issue I am facing is with the same code, playing encrypted MP4 video file (locally stored) using UIWebView is failing with the following error.

Error Domain=WebKitErrorDomain Code=204 “Plug-in handled load” UserInfo=0x3c0b60 {NSErrorFailingURLStringKey=myapp:///private/var/mobile/Applications/00109035-7155-4FF6-B247-DF5F26A87024/tmp//Docs/34199c68fcfbc45492550c5298272117502b0149.mp4, WebKitErrorMIMETypeKey=video/mp4, NSErrorFailingURLKey=myapp:///private/var/mobile/Applications/00109035-7155-4FF6-B247-DF5F26A87024/tmp//Docs/34199c68fcfbc45492550c5298272117502b0149.mp4, NSLocalizedDescription=Plug-in handled load}

Please help me to overcome this issue with your comments.

Thanks & Regards,
M.Suresh

Reply

Jens Beyer January 6, 2012 at 7:20 am

I try your code on lion, but in – (void)startLoading the [[self.request URL] path] is always nil.
So the inStream init raises an error.
The URLstring is:
encrypted-file:///Users/xnz/Library/Developer/Xcode/DerivedData/alpha-ffyoohbneqvddmduvtijneumhxpn/Build/Products/Debug/My%20Test%20App%20.app/Contents/Resources/media/aptogo.png

Any idea what is going wrong here?

Reply

Tapan Desai January 12, 2012 at 4:59 pm

Hi,

Thanks for the awesome solution. I have one questions though, i am building app that allow users to download files into local device and view it in UIWebView. I want to encrypt file after its downloaded on device and decrypt while trying to view in UIWebView so if anyone jail brake iPad or use any other tool, they can’t view local files.

I would appreciate your input on this.

Thanks in advance,
Tapan Desai

Reply

Berik Visschers January 13, 2012 at 10:53 am

Keep in mind though, that with a jailbroken device, any (https) communications can be hijacked by a proxy.

Reply

Berik Visschers January 13, 2012 at 10:42 am

Hi Robin,

Although the resources are indeed encrypted. The security added is not very high.
To show that this security is easily broken, I’ve build & run the EncryptedResourceDemo.zip project. Then went to the app:
$ cd /Users/myName/Library/Application Support/iPhone Simulator/5.0/Applications/0928889D-296D-4D1C-BC46-247BEDD7DD24/EncryptedResourceDemo.app

then tried to read the encrypted resources:
$ cat EncryptedResources/index.html
??)4?t?????v?

Reply

Berik Visschers January 13, 2012 at 10:43 am

Oke, it’s encrypted. But how about a password? I ran:
$ strings EncryptedResourceDemo
alloc
init
release
….
encrypted-file
abcdefghijklmnopqrstuvwxyz123456
EncryptedFileURLProtocol
%@://%@

There it is, the password is plain-text (hidden inside the binary) ‘abcdefghijklmnopqrstuvwxyz123456′.

I suggest hiding the password for the encryption a little better. Transforming it into an MD5 hash in binary format during compile time will add some security (will be harder to find). More secure strategies can be thought of.

Is this code open sourced? Is this code already on github?

Thanks for sharing this code!

Greets,
Berik

Reply

Berik Visschers January 13, 2012 at 11:04 am

This looks like a good example of how to hide a password in a binary: http://stackoverflow.com/a/1360175/439096

Reply

Robin Summerhill February 10, 2012 at 10:34 am

Hi Berik,

As mentioned in the article: ‘Something you might want to think about (depending on the value of the resources you are trying to protect and your level of paranoia) is a mechanism for obfuscating the decryption key. With the current scheme the key is compiled into the application binary as a plain string and could be extracted by anyone with a hex editor, a little patience and a little knowledge.’

Reply

chandrika bhat February 21, 2012 at 8:31 am

Really helped a lot.. and saved our time.

Reply

Chiemeka March 16, 2012 at 7:06 am

Great Roberts,
I will test this out and try to use the output data with a uidocument object.
Also, obfuscating the decryption key…? Sure necessity. I’ll try to report what I can come up with.

Reply

Sam Becker July 26, 2012 at 11:24 am

Any luck?

Reply

Sarah Clough March 21, 2012 at 1:11 pm

Thanks for the ios 5.0 update, I’ve been puzzling over why it was only reading in half the file to no avail!

Reply

Netshark March 27, 2012 at 4:26 am

It would be great if your code could be used to encrypt files to. I load pdf-files, which must be protected, from a webservice and want to put them unreadable for others on the device. The apple security is not enough, because some customers don’t use a device code.

Reply

Saranya krishnan March 28, 2012 at 8:35 am

Hi,
Thanks for the excellent tutorial . I have one question , When i am trying to retrieve the data from the URL , i am getting output as “0 bytes” . I have pasted the following code

NSString *indexPath1 = [[NSBundle mainBundle] pathForResource:@”aptogo” ofType:@”png” inDirectory:@”EncryptedResources”];
NSURL *url1 = [NSURL encryptedFileURLWithPath:indexPath1];
NSData *data = [NSData dataWithContentsOfURL:url1];
NSLog(@”data length:%d”,[data length]);
UIImage *imagename=[[UIImage alloc] initWithData:data];
[image setImage:imagename];

Please help me out in this.

Reply

Athrun August 27, 2012 at 11:17 pm

I have the same issue, have you found the solution? Thank you

Reply

viv April 15, 2012 at 5:55 pm

Thanks for the awesome article and code Robin.
I have observed that the NSURL pathway misses calling the decryption functions when using the following function:-
[NSFileManager copyItemAtURL:
toURL:
error:];

Any ideas how to make this pathway use the decryption? The error I see is the following:-
Error Domain=NSCocoaErrorDomain Code=262 “The operation couldn’t be completed. (Cocoa error 262.)

I’ve experimented by trying to use the aptogo.png file as source.

Reply

peryll May 22, 2012 at 3:57 am

Thanks Robin for the article.

I’m back to the problem with the “Synchronous client exited with no response and no error!” output.
I have found out that the encryption works in simulator and almost all devices. But in some cases it does not work. It is in the [NSData dataWithContentsOfURL] where the problem is. I havn’t found out yet why this occurs. The encryption key is the same in the script and in the encryption class.

Reply

peryll May 22, 2012 at 4:02 am

Sorry I put my comment in the wrong thread!

Thanks Robin for the article.

I’m back to the problem with the “Synchronous client exited with no response and no error!” output.
I have found out that the encryption works in simulator and almost all devices. But in some cases it does not work. It is in the [NSData dataWithContentsOfURL] where the problem is. I havn’t found out yet why this occurs. The encryption key is the same in the script and in the encryption class.

Reply

Mritunjay June 13, 2012 at 9:29 am

Really, its Good Articles……!

Reply

Steve July 4, 2012 at 4:06 am

Thanks for a great article! I also attempted to get this technique to work with video playback using AVFoundation. Unlike the experiences that some other folks have had, everything is being called correctly. My issue is running out of memory, with Instruments telling me that CFData (bridged from NSData I suppose) is the responsible party. These videos are fairly large, up to 10gb for some. It looks like AVFoundation is retaining all of the NSData instances that are being sent to it via [self.client URLProtocol:self didLoadData:data]. What I think may be happening is that iOS is trying to look up video metadata information in the file, such as duration and as such is ending up trying to load the entire file into memory. It makes me wonder if the normal file based playback just maps the entire thing and just goes about its business that way. Has anyone ever had any success in pulling this off with video files?

Reply

Blake August 8, 2012 at 5:57 am

Hello,

My App crashes with this when I try to use encrypted resources.

*** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘*** -[__NSPlaceholderArray initWithContentsOfFile:]: string argument is not an NSString’

I get the resources with this line:

NSArray *array = [[[NSMutableArray alloc] initWithContentsOfURL:[NSURL encryptedFileURLWithPath:tablePath]] objectAtIndex:[unit intValue]-1];

tablePath is the path to the file. Does anyone see what is wrong? thanks!
Blake

Reply

MarionMck August 27, 2012 at 6:31 am

Sorry, I seem to be missing something here. I had problems getting this working in my app so I added an image to the example project. canInitWithRequest is called as expected for the existing example files of index.html and aptogo.png but doesn’t get called for my new image file. The URL looks ok, but the NSData comes up as nil. This is how I’m loading the image:

NSString *imgPath2 = [[NSBundle mainBundle]
pathForResource:@”filename.jpg”
ofType:nil
inDirectory:@”EncryptedResources”];
NSURL *imgurl2 = [NSURL encryptedFileURLWithPath:imgPath2];
NSLog(@”url scheme %@”, [imgurl2 scheme]);
NSLog(@”url relpath %@”, [imgurl2 relativePath]);
NSData *data2 = [NSData dataWithContentsOfURL:imgurl2];
UIImage *img2 = [[UIImage alloc] initWithData:data2];

I can see the file in the EncryptedResources folder so it should be finding the file from the relative path ok. I get the same result in the simulator or on a device. I am running Xcode 4.3.3 for iOS 5.1.
This seems a really stupid question after all the responses above show that it works fine, but I just can not see what I’m doing wrong!

Reply

MarionMck August 29, 2012 at 4:33 pm

I’ve tried forcing the use of NSURLConnection by using the following

NSURLRequest *urlRequest = [NSURLRequest requestWithURL:imgurl2];
NSData *urlData = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&myerr];

but still canInitWithRequest is not called and I get an ‘unsupported URL’ error.

Reply

Shahid September 11, 2012 at 5:11 pm

Can anybody please help me with the shell script to recursively iterate through all sub folders and encrypt them as well? Currently it hangs if there is subfolder.

Reply

Luke April 23, 2013 at 10:54 am

I updated the script to handle sub-folders, works in Xcode 4.6.1 without issues

#!/bin/bash

DIRNAME=EncryptedResources
ENC_KEY=”abcdefghijklmnopqrstuvwxyz123456″

INDIR=$PROJECT_DIR/$PROJECT_NAME/$DIRNAME
OUTDIR=$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH/Resources/$DIRNAME

if [ ! -d "$OUTDIR" ]; then
mkdir -p “$OUTDIR”
fi

while IFS= read -r -d $” dir;
do
DIRECTORY=`echo $dir | sed -e “s,$INDIR,$OUTDIR,g”`
mkdir -p “$DIRECTORY”
done < <(find "$INDIR" -type d -print0)

while IFS= read -r -d $'' file;
do
OUTFILE=`echo $file | sed -e "s,$INDIR,$OUTDIR,g"`
if [ ! -d "$file" ]; then
echo "Encrypting $file"
"$PROJECT_DIR/crypt" -e -k $ENC_KEY -i "$file" -o "$OUTFILE"
fi
done < <(find "$INDIR" -type f -print0)

Reply

navin June 19, 2013 at 9:13 am

If my directory or file contain space means it shows “Shell Script Invocation error” how can i fix this.

Reply

Jason September 25, 2012 at 9:55 pm

Hi Robin,

Thanks for this great post, I have been using this in my app for quite some time and it’s working great for me.

However, with iOS6 this code actually leaks memory spectacularly when you use it. The official reply I got from Apple as to why was this:

Below is a log that’s new in iOS 6:

coreAssert(ISBOOLEANTRUE(fScheduledCallbackFlags, didReceiveResponse), “NSURLConnection/CFURLConnection ordering violation: didReceiveData to be scheduled before didReceiveResponse”);

It logs when a custom NSURLProtocol, wants to trigger NSURLConnection to issue didReceiveData before didReceiveResponse.

So if you are using this code in iOS6, this is what I added to the – (void)startLoading method to fix this leak

[self.client URLProtocol:self didReceiveResponse:[[[NSURLResponse alloc] init] autorelease] cacheStoragePolicy:NSURLCacheStorageNotAllowed];

Hope this helps somebody! :)

Reply

Fatemeh September 26, 2012 at 11:10 am

Hi It’s great tutorial, Thanks Robin Summerhill…

I have a problem and hope you help me…
I have number of xml files that I don’t want be accessible… I encrypted them and also decrypted and webview shows the content of these files. but I need the string content of these files, or data type of them.
I have tried to use data by NSData* data = [[NSData alloc]initWithContentsOfFile:url]; but these line don’t call the EncryptedFileURLProtocol methods and I get null. The methods in that class only call when I use this line: [webView loadRequest:request];
I tried with other files format but this don’t work. Could you help me?
Thanks for your attention…

Reply

Jason September 26, 2012 at 8:29 pm

Fatemeh you should probably show all of your code leading up to before and after your call to NSData so we can see what your doing better

Reply

Fatemeh September 27, 2012 at 2:36 pm

On the sample code of “EncryptedResourceDemo.zip”, I only commented this line: [webView loadRequest:request];
and then put NSData* data = [[NSData alloc]initWithContentsOfFile:url] to load data on NSData object.
The data is null …

Reply

Jason September 27, 2012 at 9:06 pm

I’m assuming it’s null because you are not actually starting an actual stream for the file to be decrypted. Essentially what you are doing is just loading an encrypted file which isn’t actually decrypted.

If you create a NSURLConnection which loads the URL and then put in delegate methods for didReceiveData to compile the bits of the stream together into one big NSData object then conversely turn it into an NSString in didFinishLoading or whatever representation you want out of your data that it will work for you

Reply

Fatemeh September 28, 2012 at 5:00 pm

Thanks Jason, I followed the way you mentioned before, and I did not get the correct data. After your comment I tried this again and this return data. I think I made a mistake before.
Thanks very much Jason for your help and Thank you, Robbin for your useful topic…

Jason October 1, 2012 at 10:29 pm

No worries, glad you got it all working :)

Jeroen October 4, 2012 at 10:25 am

I really loved this way of encryption. I got it semi-working pretty fast. Sadly I was not able to get it working flawlessly. For some reason it sometimes fails to decrypt the last few characters of an encrypted file. This happens with both my application and the provided demo application. I use IOS SDK 5.1.

For instance:
index.html contains “abcdefghijklmnopqrstuvwxyz1234567890″
it occasionally shows “abcdefghijklmnopqrstuvwxyz123456″, sometimes it does it correctly.

In my main application (Cordova) this causes odd behavior when a closing tag is corrupt. I would love to know if other people faced the same problem. Any insight would be great!

On a side note: Does anyone know if you can disable the xcode warning “A signed resource has been added, modified, or deleted.”. It keeps showing the warning at “Verifying application”. I can get it running by revalidating the shell bash script though. For your info, I test on a device.

Reply

Jeroen October 4, 2012 at 10:28 am

I am pretty sure that it’s related to decrypting it. I have inspected the output files and at both cases (failure & success) it had the same content.

Reply

Jeroen November 6, 2012 at 11:26 am

I solved this by changing this line:
// NSData *data = [NSData dataWithBytesNoCopy:outBuffer length:len freeWhenDone:NO];
to
NSData *data = [NSData dataWithBytes:outBuffer length:len];

I suspect it has to do with Automatic Reference Counting.

Reply

Sam Becker November 7, 2012 at 2:02 pm

Does anyone else get this error:

CFNetwork internal error (0xc01a:/SourceCache/CFNetwork_Sim/CFNetwork-609/Connection/URLConnectionClient.cpp:2216)

When running on iOS 6?

Reply

Wilmar November 8, 2012 at 4:21 am

Tons of it, as does numerous other people in the comments. It ends with the error: Synchronous client exited with no response and no error!

No idea how to fix it yet.

Reply

Wilmar November 8, 2012 at 4:28 am

Haven’t tested completely yet, but I changed the following and the [code]Synchronous client exited with no response and no error![/code] error disappeared.

1) Changed [code]NSData *data = [NSData dataWithBytesNoCopy:outBuffer length:len freeWhenDone:NO];[/code] to [code]NSData *data = [NSData dataWithBytes:outBuffer length:len];[/code]

2) Added [code][self.client URLProtocol:self didReceiveResponse:[[[NSURLResponse alloc] init] autorelease] cacheStoragePolicy:NSURLCacheStorageNotAllowed];[/code] at the beginning of the -startLoading method in EncryptedFileURLProtocol.m

Thanks to the previous two commenters who posted these fixes.

Reply

Wilmar November 8, 2012 at 4:30 am

I see my [code] tags don't work. Here is a clean version:

Haven't tested completely yet, but I changed the following and the Synchronous client exited with no response and no error! error disappeared.

1) Changed NSData *data = [NSData dataWithBytesNoCopy:outBuffer length:len freeWhenDone:NO]; to NSData *data = [NSData dataWithBytes:outBuffer length:len];

2) Added [self.client URLProtocol:self didReceiveResponse:[[[NSURLResponse alloc] init] autorelease] cacheStoragePolicy:NSURLCacheStorageNotAllowed]; at the beginning of the -startLoading method in EncryptedFileURLProtocol.m

Thanks to the previous two commenters who posted these fixes.

Reply

Sam Becker November 8, 2012 at 8:50 pm

Thanks Wilmar (+ Jeroen)! You’re my hero(s)! I had added the didReceiveResponse: method but to the wrong part and I was getting some nasty crashes. Now I have no more errant console warnings and I can actually see what the hell is going on! Thanks SO much.

Reply

DJean May 29, 2013 at 4:34 am

thanks Wilmar!!

Reply

Akhil P.K December 13, 2012 at 1:29 am

Zaygraveyard has done great job and the final project can be downloaded from the below link. The issues relating to NSData, UIImage etc are solved and the application is working fine. Thanks to Robin Summerhill also. It is working fine in IOS 6.0 also. Please download the sample project from

“http://dl.dropbox.com/u/20989950/EncryptedResourceDemo.zip”

Reply

WC April 16, 2013 at 11:54 am

Anyone had any luck trying to incorporate this somehow when using Titanium? Could this be made into a module somehow? I am needing to do encrypting/decrypting of media resources and am finding some examples using xCode, but having difficult time getting a solution to be used with Titanium, or even just a javascript way of doing this.
I have had some success using IOS Security feature of encryption when locked by creating an xCode module to set the ‘NSFileProtectionComplete’ value, but it will not work under any of the app bundle resources because they are read-only, so can’t be modified. I was thinking if I could get resources encrypted prior to build of app bundle, then in the app, at least the first run, decrypt to a new file in the Documents or Private Documents location and set the protection attribute there …

Reply

Criminal Lawyer Houston Texas April 24, 2013 at 11:17 pm

We appreciate you for putting up posts such as these to help
keep awareness. Be sure to look over my site and follow it,
too!

Reply

KobeWong April 25, 2013 at 7:33 am

Hi, Robin

I change the “ENC_KEY” in the run script, and then the decrypt doesn’t work for me. Can you help this?

Reply

newssergw May 6, 2013 at 4:54 pm

Источник: http://news98.ru/
Спецслужбы США, расследующие теракт на Бостонском марафоне, обнаружили в компьютере вдовы Тамерлана Царнаева Кэтрин Рассел электронный журнал Inspire, распространением которого занимается «Аль-Каида», и другие радикальные исламистские материалы, сообщает The Washington Post.

Reply

Meera May 30, 2013 at 2:57 am

Hi there, I downloaded the sample project of yours which works fine but when I tried to do the same with my project to protect my project resources I get this “Can’t open input file. Command /bin/sh failed with exit code 1″. When I tried on terminal to execute the shell script I get it like “this cannot execute binary file”. I dont know whats going wrong. Can you please help?

Reply

Maxthon Chan June 17, 2013 at 10:49 am

Do you think this a good idea: instead of hard coding the key in the app, hard code it into a server and use that server to distribute it in a safe manner and after retrieval, store it in keychain? That can be safer as the server can perform some authentication so that hackers are more difficult to break into it, and it can be adapted into a system allowing distributing encrypted IAP content too.

Reply

bart June 24, 2013 at 8:44 am

It doesn’t work when I use a pdf instead of html:

NSString *indexPath = [[NSBundle mainBundle] pathForResource:@”test” ofType:@”pdf” inDirectory:@”EncryptedResources”];
NSURL *url = [NSURL encryptedFileURLWithPath:indexPath];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];

Any clue?

Reply

chudanqin November 26, 2013 at 10:03 pm

And It doesn’t work when I use a mp4 file. MoviePlayer can’t play it.
If you have solutions about it. Please e-mail me.

Reply

Almas December 7, 2013 at 12:03 pm

Hi, have you got any solution? I’m facing with same problem.Please help)

Reply

http://linkbuildingblog.co.uk July 17, 2013 at 3:23 am

In fact no matter if someone doesn’t know afterward its up to other people that they will assist, so here it occurs.

Reply

real estate license texas July 17, 2013 at 6:22 am

Nice post. I learn something new and challenging on blogs I stumbleupon every day.

It’s always useful to read content from other authors and practice something from their web sites.

Reply

any September 19, 2013 at 2:23 am

can u please how to create the encrypted folder

Reply

trend micro titanium proxy settings September 24, 2013 at 12:04 am

Please lett me kknow if you’re looking for a author for your
blog. You have some really great posts and I believe I would be a good asset.
If you ever want to take some of the load off, I’d
absolutely love to writfe some content for your blog in
exchange for a link back to mine. Please send me an e-mail if
interested. Thanks!

Reply

shalini September 27, 2013 at 3:49 pm

Hi thanks for the gud tutorial..it worked well with htmls..but not working with uiimage..the .PNG files are not getting decrypted well..am working on it for four days..somebody please help
I am using xcode4.6 and iOS 6.

Reply

shalini September 30, 2013 at 2:52 pm

Thanks Zaygraveyard for the updated project.it is working fine with uiimage.but am having problems with htmls with CSS.It works fine with plain htmls

Reply

att cell phones with contract October 28, 2013 at 8:12 pm

Nice weblog right here! Alsoo your web site lots up fast!
What web host are you the use of? Can I am getting your associate hyperlink on your host?

I esire my site loaded up as quickly as yours lol

Reply

Ameen November 19, 2013 at 5:30 pm

Hi Robin, and all
But It’s very simple to see sharedKey in app.ipa with zip it extract and open the app with any hex editor and get it the ENC-Key.

Reply

Botas Ugg Espana November 27, 2013 at 11:38 pm

Hoke seems like an everyman and has turned on fans at Michigan again by bringing back the Michigan Way. He talks like a regular guy, and plays football the way Michigan fans were used to in the old days, when going to a Wolverines game gave you a certain feel.
Botas Ugg Espana http://www.canariasport.com/images/es.html

Reply

Woolrich Parka Donna November 27, 2013 at 11:38 pm

qu hay de los motores? Bien, Hyundai ofrecer hasta cuatro mec repartidas equitativamente entre di y gasolina. En Europa los aut superventas ser los 2.0 y 2.2 a gasoil, con 150 y 200 CV de potencia, mientras que quien busque el refinamiento m absoluto podr inclinarse por un 2.4 de 193 CV o (en EEUU) un 2.0 turbo que homologar 264 CV. La transmisi podr ser autom o manual, dependiendo de las versiones y del sistema de tracci pero siempre con seis marchas.
Woolrich Parka Donna http://www.coopiberici.it/spaccio/woolrich.asp

Reply

Moncler Bambini November 27, 2013 at 11:38 pm

Good Samaritans Rewarded: It sometimes pays to do the right thing (Video) Cheap Castles (Video) Unlike prison workers, federal inmates still getting paid during government shutdown Vatican misspells Jesus’s name on commemorative medals Bush, Cheney ‘never quite friends,’ new book reveals USPS may destroy ‘unsafe’ stamps Members of Congress are people too, drink beer They’re talking? Really? Republicans, President Obama have “productive” meeting on debt, shutdown Problemplagued New Jersey Senate campaign mars Booker’s political ascent Monsanto protesters toss money off balcony in Senate officeChristie weight loss surgery successful
Moncler Bambini http://www.crsport.it/moncler.html

Reply

Nike Free Dame November 27, 2013 at 11:39 pm

3rd Party Cookies We use Advertising agencies to provide us with some of the advertising on our websites. These include (but are not limited to) Specific Media, The Rubicon Project, AdJug, AdConion, Context Web. Please click on the provider name to visit their optout page.Cyclists at Games to win
Nike Free Dame http://www.ottobruunsfond.dk/nike-free-tilbud.html

Reply

Botas Ugg Baratas November 27, 2013 at 11:39 pm

New Range Handmade Wedding Cards 2012My Favorite Wedding Card For 2012New Range Of wedding Cards For 2012Wedding Invitations! When did They First Start with Invitations?Bookmark WidgetKate Middleton Wedding DressWedding PollWedding Day Photo Albums With Swarovski CrystalsAmazonWedding accessories Wedding Day CardsAmazonUK Greeting Cards Are Proud to Anounce Our Range of Civil Partnership CardsGreat Stuff on AmazonWedding AccessoriesHere Is Fabulous Wedding Day Card With Swarovski CrystalsFive Dollar Shake PollAmazon MP3Beautiful Wedding Place Card HolderDo You Run Online Business? Checkout My Other Featured LensesAmazon Voting (Plexo)Handmade Wedding Cards Wedding Invitations Online Worldwide ShippingHow About This Beutiful Handmade Wedding Day Guest BookAmazon SearchLinks Voting (Plexo)Lets Take a Look at Engagement CardsLens Love Widget Please RatePlease Add Your Comments Thank YouFollow Us on Twitter
Botas Ugg Baratas http://www.fac-seguridad.es/ugg.asp

Reply

dmitriy December 4, 2013 at 8:14 am

i have a problem with custom protocol while i’m working with media types ( or ). Media data doesn’t play. I have errors like “Unsupported URL”. i’ll wonder if you can tell me how i can fix this issue.

Reply

support December 5, 2013 at 5:22 am

Hi my loved one! I wish to say that this article is amazing, great written
and come with approximately all vital infos.
I’d like to look more posts like this .

Reply

情趣用品 December 10, 2013 at 1:12 am

I like to share understanding that I’ve accrued through the yr to help improve group functionality.

Reply

aes 256 bit December 23, 2013 at 6:50 am

Simply wish to say your article is as surprising. The clarity in your post
is simply great and i could assume you are an expert on this subject.
Well with your permission allow me to grab your feed to
keep up to date with forthcoming post. Thanks a million
and please keep up the rewarding work.

Reply

aes 256 January 5, 2014 at 9:05 am

i have problem when build script.
This error found :
“Encrypting /Projectdir/folderdecrypt/*
Can’t open input file: /Users/Projectdir/crypt ”

Can you tell me how to fix that?

Reply

May Phase January 5, 2014 at 9:19 am

How to protect with sqlite file. i have encrypt success but i don’t know how to decrypt this file

Reply

Cesar January 27, 2014 at 11:05 am

I need the same info too.

Reply

facebook infiltrator review January 23, 2014 at 6:01 pm

Their keywords received 165000 searches monthly on Google and the first 10 pages on Google were taken up by major travel agencies.
The plugin buttons can be used to promote your business Facebook account by adding them to business cards, emails and monthly
newsletters. Enter the ip address of the website you want to open instead of the website address
in the address bar.

Reply

chafouin.org January 24, 2014 at 6:53 am

I visited many web sites еxcept thе audio quality for audio
songs current ɑt thiѕ site is genuinely superb.

Reply

Sylvia February 3, 2014 at 6:07 pm

No cell phone, no regular phone, no computers, no emails, no kids,
no expectations. This is one of the games where you can
play with multiple players and hence you can make friends across
the globe. Once you have a good idea of what they want, do some homework.

Reply

Financial services February 7, 2014 at 10:41 am

Thsnk you foг the good writeup. It iո fact աaѕ a amusement account іt.
ʟooк advanced tο far addded agreeable from you! By the way, how сan we communicate?

Reply

Melaine February 12, 2014 at 12:54 am

Therefore, assure your content is not the mediocre type and is great to be read
by a reader. This kind of traffic can be great – and even desired – especially
if you have a blog that is providing essential information to
others.

Reply

Kevin February 13, 2014 at 11:02 am

Hi Robin
I have a problem with my project. when i load encrypted file html that embebded file javascript many time my page not run javascript function . Can you help me to fix this problem?
Thank you alot!

Reply

http://instagram.com/p/fY_PbAqqQs/ February 21, 2014 at 1:25 pm

A fascinating discussion is worth comment. I think that you need to
publish more on this issue, it might not be
a taboo matter but usually people don’t discuss these issues.
To the next! Best wishes!!

Reply

film indonesia terbaru February 26, 2014 at 1:26 pm

Admiring the time and effort you put into your blog and in depth information you offer.
It’s good to come across a blog every once in a while that isn’t the same out of date rehashed
material. Wonderful read! I’ve bookmarked your site and I’m adding your
RSS feeds to my Google account.

Reply

football quotes February 27, 2014 at 3:06 pm

We are a group of volunteers and starting a new scheme in
our community. Your website provided us with valuable information to work
on. You have done a formidable job and our whole community will
be grateful to you.

Reply

pocong juga pocong March 2, 2014 at 3:07 am

I blog quite often and I really thank you for your information.
This great article has truly peaked my interest.
I am going to take a note of your blog and keep checking for new information about once a week.
I subscribed to your RSS feed as well.

Reply

Precio Zapatos Louboutin March 15, 2014 at 4:47 am

I’m really impressed with your writing skills and also with the layout on your weblog.
Is this a paid theme or did you modify it yourself?
Either way keep up the excellent quality writing, it’s rare to see a nice blog like this one nowadays.\

Reply

سباق سيارات March 17, 2014 at 4:51 pm

Aƿpreciate tɦe recommendation. ʟеt me try it out.

Reply

dressup March 19, 2014 at 3:29 pm

If you desire to get a good deal from this paragraph then you have to apply such
methods to your won web site.

Reply

Sushil March 22, 2014 at 12:20 pm
Roberto O. Buratti March 25, 2014 at 1:32 pm

Hi Robin, Great Post :)
So, what about if I need to dinamically encrypt resources (for example file downloaded from the Internet) ?
I tried to adjust things in your example to allow writing, but when I try to use the custom protocolo in things like

NSString *urlString = [NSString stringWithFormat:@"my-funny-file-protocol://%@", path];
NSURL *url = [NSURL URLWithString:urlString];
NSOutputStream *writer = [NSOutputStream outputStreamWithURL:url append:NO];

No exception raises, no logs on the console but the result is always nil. The same file works, if I use a standard file: protocol.

Any Idea? Perhaps I’m missing someting in my URLProtocol implementation… but what?

Reply

sujanpatel.com April 9, 2014 at 8:11 pm

Hello, the whole thing is going fine here and ofcourse every one is sharing
information, that’s in fact fine, keep up writing.

Reply

Bali Villa Deals April 22, 2014 at 11:46 am

I like the helpful info you provide in your articles. I’ll
bookmark your weblog and check again here frequently. I’m quite sure I’ll learn
a lot of new stuff right here! Good luck for the next!

Reply

Leave a Comment

{ 34 trackbacks }

Previous post:

Next post: