Display encrypted file using QuickLook framework or UiDocumentInteractionController
Asked Answered
P

4

6

I have an encrypted word/excel/pdf file locally stored which I need to preview in my iPad app. I understand that QLPreviewController or UiDocumentInteractionController could be used to preview these files. I can very well use this

- (id <QLPreviewItem>) previewController: (QLPreviewController *) controller previewItemAtIndex: (NSInteger) index {

    return [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:[documents objectAtIndex:index] ofType:nil]];
}

But the file is encrypted and when I decrypt it I would get hold of NSData object. How do I go about loading NSData in either of these.

Also I understand that I can very well store the NSData back as a local file and load it in Preview. But there is a constraint of not storing the unencrypted file locally.

If someone has already accomplished this and can help me out here it will be greatly appreciated.

Thanks AJ

Parquetry answered 18/1, 2012 at 21:2 Comment(0)
L
12

Since you are using Quick Look, your options are limited. You must give Quick Look an NSURL, which means it must be on the file system (or the Internet). Fortunately, this shouldn't be much of a problem. iOS devices use hardware-level encryption. When your file is encrypted, only your app has the key to decrypt it. So, your file will still be encrypted, but it will also be readable by your app and only your app.

Here's what you do:

  1. Decrypt your file into an NSData object, which you've already done.

  2. Write the file to a location that will not get uploaded to iCloud nor backed up by iTunes. The tmp directory is probably the best choice. The code looks something like this:

    NSData * data = // Your decrypted file data.
    NSString * fileName = // Whatever you want to name your file.
    NSString * path = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
    NSURL * url = [NSURL URLWithString:path];
    NSError * error = nil;
    
    BOOL success = [data writeToURL:url
                            options:NSDataWritingFileProtectionComplete
                              error:&error];
    if (success) {
        // Give the URL to Quick Look.
    }
    else {
        // An error happened. See the 'error' object for the details.
    }
    

    At this point you have an NSURL which you can use with Quick Look. Don't forget to delete the decrypted file when you are done with it.

There are a few things to note about on-disk encryption:

  1. It is only supported on iOS 4.0+.

  2. It may not work on "older" devices.

  3. The user must have an active passcode.

  4. If you use NSDataWritingFileProtectionComplete, the file is not accessible while the device is locked. If you need to access the file while the app is locked, then you should use NSDataWritingFileProtectionCompleteUnlessOpen or NSFileProtectionCompleteUntilFirstUserAuthentication instead. This will still give you great protection, even if the device is stolen and jailbroken. Be aware, though, that these encryption options are only available on iOS 5.0+

For more details for on-disk encryption, check out the iOS App Programming Guide

Lookin answered 1/2, 2012 at 23:12 Comment(7)
Thanks Rob for the response. This information really helped. I do have a followup question on this. I am doing all this to provide offline capability in my application for the files. Do you think I am adding redundancy by encrypting and decrypting files by myself. Instead do you think its secured enough for me to just use the on-disk encryption and store the files in my documents folder of the application. Also what would be the best place to store these files. I mean documents or tmp folder. Thanks again.Parquetry
Sorry you have already answered what would be the best place to store them.Parquetry
Also I tried using NSDataWritingFileProtectionComplete attribute to write files to the tmp folder of the application on a simulator. I was able to go to the folder in my file system and open the documents. I am assuming there will be a similar behavior on a jailbroken device where in I have access to the file system of the device and I can just traverse to the temp folder and access the documents. Is that a correct assumption. ThanksParquetry
As for what directory you are going to use, you have a few choices. If you want iCloud to sync the files use <AppHome>/Documents. If you don't want iCloud to sync the files and you don't care if the file gets purged occasionally, then use <AppHome>/Library/Caches. If you want to guarantee that your file stays around and isn't synced by iCloud, use <AppHome>/Library/Private Documents.Lookin
Now for encryption. To have on-disk encryption, you must have the four conditions I mentioned in my answer. I don't know how sensitive your files are. If security is a huge concern or you must deploy on older devices, then go ahead and be redundant. Otherwise, on-disk encryption should be good enough. By using on-disk encryption, files can't be read directly from the file system. The only way to decrypt it is to steal the app's secret key, which I don't know if anyone has done yet. The key to security is that the effort to steal your data must be greater than the data is worth.Lookin
Thanks for the reply. I think I have everything I need. I also found an interesting read about apple's hardware encryption being cracked. Here is the url. geek.com/articles/chips/…Parquetry
Great answer but I did have 1 issue. writeToURL wouldn't work if the url object was created using [NSURL URLWithString:] I had to use [NSURL fileURLWithPath:].Subatomic
K
1

After doing some digging, I found out that QLPreviewController is using UIWebView underneath, and calls the loadRequest: to load the requested file.

Another way to accomplish what you desire is to make a private Category on UIWebView, and use method swizzling to override the loadRequest: method, and call instead the loadData:MIMEType:textEncodingName:baseURL: method.

Beware that:

1) In low-memory scenarios (i.e. large files) a black screen with "Error to load the document" appears, if that concerns you. (The unhacked QLPreviewController knows how to handle these scenarios very well and present the document).

2) I'm not sure Apple are going to approve this kind of hack, although no private APIs are used here.

code:

@implementation UIWebView (QLHack)

    - (void)MyloadRequest:(NSURLRequest *)request
    {
        // Check somehow that it's the call of your QLPreviewController           
        // If not, just call the original method.

        if (!insideQLPreviewController)
        {
             // Call original implementation
             [self MyloadRequest:request];     
        }
        else
        {
             // Load the real data you want
             [self loadData:data MIMEType:mimeType textEncodingName:nil baseURL:someURL];
        }

    }

    + (void)load 
    {
        method_exchangeImplementations(class_getInstanceMethod(self, @selector(loadRequest:)), class_getInstanceMethod(self, @selector(MyloadRequest:)));
    }

@end
Katzen answered 29/4, 2012 at 9:24 Comment(0)
J
1

Actually, writing a file to a tmp directory is still insecure. The other alternative is to use UIWebView with NSURLProtocol and allow decrypting this data on the fly.

Japha answered 11/5, 2015 at 23:52 Comment(0)
C
0

One way could be.

use Temp Dir , Save File in Temp , Make NSURL From that Temp File and Display and then Delete that temp Dir after that.

Thanks.

Claire answered 31/1, 2012 at 4:43 Comment(1)
Yes. Thats going to be my last resort.Parquetry

© 2022 - 2024 — McMap. All rights reserved.