Secure Storage with the Good Dynamics SDK and Xamarin.iOS

Secure Storage with the Good Dynamics SDK and Xamarin.iOS

The Good Dynamics iOS SDK provides Xamarin developers a wide range of containerized security features that cover almost every security need on a mobile device.  In the previous blog post, we made a Xamarin iOS app and implemented the Good Dynamics iOS SDK that handled securely authenticating users.  A key component to mobile security is securing the persistent data stored on the device.  Good Dynamics secures data in a SQLite database and data in documents stored on the file system.  This post will serve as a step-by-step guide to implementing these in your Xamarin app, complete with code examples.

Secure SQLite

The Good Dynamics SDK comes with its own implementation of Apple’s Core Data, which is a persistence framework that serves to store data on a device and map it to your domain models.  In order to ensure security, Good Dynamics has added encryption using AES-256 bit cipher technology.  In order for the developer to take advantage of this highly secure encrypted storage, only a few adjustments need to be made to a Xamarin iOS app that currently uses Core Data.

To demonstrate this, we will be using Xamarin’s Core Data sample app on github and modifying only the necessary code required to implement Good’s encrypted storage.  If you wish to get the Good embedded app up and running, make sure to follow the Getting Started guide.

The below code sample shows the app’s NSPersistentStoreCoordinator.  This class’s job is to associate a persistent storage medium, in this case SQLite, and an object model.  More details on this and other Core Data classes can be found here.

In this example, the PersistentStoreCoordinator is a property on a view controller who’s getter either accesses the existing PersistentStoreCoordinator or makes a new one at a desired location.  After its been initialized, it calls AddPersistentStoreWithType with a few configuration options to connect the storage medium and the object model.

From monotouch-samples / ThreadedCoreData / ThreadedCoreData / APLViewController.cs:

NSPersistentStoreCoordinator PersistentStoreCoordinator {
get {
   if (persistentStoreCoordinator != null)
       return persistentStoreCoordinator;

   string storePath = ApplicationDocumentsDirectory + "/Earthquakes.sqlite";
   var storeUrl = NSUrl.FromFilename (storePath);

   persistentStoreCoordinator = new NSPersistentStoreCoordinator (ManagedObjectModel);

   NSError error;
   persistentStoreCoordinator.AddPersistentStoreWithType (NSPersistentStoreCoordinator.SQLiteStoreType, null, storeUrl, null, out error);
   if (error != null)
       Console.WriteLine (string.Format ("Unresolved error {0}", error.LocalizedDescription));

   return persistentStoreCoordinator;
   }
}

The Good Dynamics SDK has provided with GDPersistentStoreCoordinator which extends NSPersistentStoreCoordinator.  All we have to do is use this class instead of it’s parent.  Because we are swapping out NSPersistentStoreCoordinator, we need to replace NSPersistentStoreCoordinator.SQLiteStoreType with GDPersistentStoreCoordinator.GDEncryptedBinaryStoreType. This is a configuration value that sets the storage format to be Good encrypted binary files.  You can use any storage type with the GDPersistentStoreCoordinator, but the storage types that are actually encrypted using Good start with “GDEncrypted”.  You can view all available storage types here.

NSPersistentStoreCoordinator PersistentStoreCoordinator {
get {
   if (persistentStoreCoordinator != null)
       return persistentStoreCoordinator;

   string storePath = ApplicationDocumentsDirectory + "/Earthquakes.sqlite";
   var storeUrl = NSUrl.FromFilename (storePath);

   persistentStoreCoordinator = new GDPersistentStoreCoordinator (ManagedObjectModel);

   NSError error;
   persistentStoreCoordinator.AddPersistentStoreWithType (GDPersistentStoreCoordinator.SQLiteStoreType, null, storeUrl, null, out error);
   if (error != null)
       Console.WriteLine (string.Format ("Unresolved error {0}", error.LocalizedDescription));

   return persistentStoreCoordinator;
   }
}

 

This is all that is needed to setup your Xamarin iOS app with a highly secure Good Dynamics encrypted store.  As you can see, the change was fairly straightforward and can be swapped in and out with minimal effort.

Secure File Storage

The Good Dynamics SDK comes with its own secure wrapper around Apple’s NSFileManager, which is the fundamental class for saving and accessing files stored on the mobile device.  Good Dynamics has subclassed NSFileManager into their very own GDFileManager which essentially adds encryption using AES-256 bit cipher technology to the standard functionality of its parent.  Everything that NSFileManager is capable of can be found here.

Good Dynamics has made it easy to swap out the reference to NSFileManager with GDFileManager.  Every method in NSFileManager has been covered by Good’s implementation with the added layer of security.  In most cases, swapping the class is all that is needed, but there are a few considerations:

  • GDFileManager can only write to the secure store, which is the section of memory that GD iOS SDK is responsible for encrypting.
  • GDFileManager can only read from the secure store. No access to general files or directories.  No access to cloud storage.
  • GDFileManager can only be used once the user has been authorized through Good.
  • The returned path for locations of files in the secure store can only be used by members of GDFileManager.
  • GDFileManager may pass back error codes that are members of the GD iOS SDK or general NS error codes.

The below code sample is from the SecureStore Xamarin app from Good’s website.  This Filemanager class showcases a handful of the many usages of the GDFileManager class.

public class FileManager : IFileManager
{
 
    public FileManager()
    {
    }
 
    public IList<string> FindSecureDocsAtPath(string securePath)
    {
        NSError error;
        var files = GDFileManager.DefaultManager.ContentsOfDirectoryAtPath(securePath, out error)
            .Select(r => new NSString(r.ToString())).ToList();
 
        if (files.Count > 0)
        {
            return files.Select(str => str.ToString()).OrderBy(str => str).ToList();
        }
        else
        {
            if (error != null)
            {
                Console.WriteLine("FindSecureDocsAtPath error domain={0} code={1:d} userinfo={2}", error.Domain,
                    (long)error.Code, error.UserInfo.Description);
            }
        }
 
        return new List<string>();
    }
 
    public FileInfo GetFileInfo(string filepath)
    {
        NSError error;
        FileInfo info = new FileInfo();
 
        NSDictionary attributes = GDFileManager.DefaultManager.AttributesOfItemAtPath(filepath, out error);
        if (attributes != null)
        {
            info.RealFileName = new NSString(filepath).LastPathComponent;
            info.LastModifiedDate = (NSDate)attributes.ObjectForKey(NSFileManager.ModificationDate);
            info.FileSize = ((NSNumber)attributes.ObjectForKey(NSFileManager.Size)).UnsignedLongValue;
            info.Extension = info.RealFileName.PathExtension;
            bool isDirectory;
            GDFileManager.DefaultManager.FileExistsAtPath(filepath, out isDirectory);
            info.IsFolder = isDirectory;
        }
        if (error != null)
        {
            Console.WriteLine("GetFileStat error domain={0} code={1:d} userinfo={2}",
                error.Domain, (long)error.Code,
                error.UserInfo.Description);
        }
 
        return info;
    }
 
    public bool FileExistsAtPath(string filePath, out bool isDirectory)
    {
        return GDFileManager.DefaultManager.FileExistsAtPath(filePath, out isDirectory);
    }
 
    public bool CreateDirectory(string path, bool withIntermediateDirectories, NSDictionary attributes, NSError error)
    {
        return GDFileManager.DefaultManager.CreateDirectoryAtPath(path, withIntermediateDirectories, attributes, out error);
    }
 
    public bool CreateFile(string path)
    {
        string contents = "testing 123";
 
        return GDFileManager.DefaultManager.CreateFileAtPath(path, NSData.FromString(contents), null);
    }
 
    public bool WriteToFile(NSData data, string fileName)
    {
        NSError error = null;
        var result = GDFileManager.DefaultManager.CreateFileAtPath(fileName, data, null);
 
        if (!result && error != null)
        {
            Console.WriteLine("WriteToFile error domain={0} code={1:d} userinfo={2}",
                error.Domain, (long)error.Code,
                error.UserInfo.Description);
        }
 
        return result;
    }
 
    public NSData ReadFromFile(string filename, out NSError error)
    {
        error = null;
        return GDFileManager.DefaultManager.ContentsAtPath(filename);
    }
 
    public bool RemoveFile(string filePath)
    {
        NSError error;
        var result = GDFileManager.DefaultManager.RemoveItemAtPath(filePath, out error);
        if (!result && error != null)
        {
            Console.WriteLine("RemoveFile error domain={0} code={1:d} userinfo={2}",
                error.Domain, (long)error.Code, error.UserInfo.Description);
        }
 
        return result;
    }
}

Good Dynamics makes it easy to for developers to take advantage of Good’s encrypted file storage system.  This is all that is needed to setup your Xamarin iOS app with a highly secure Good Dynamics encrypted file storage system.

Aside from securing persistent data such as databases and file storage, Good Dynamics brings a wealth of security features to your mobile experience.  This post is part of a series of building secure enterprise mobile applications using Good Dynamics & Xamarin. To read more click here.

Our mobility consulting professionals have extensive experience developing enterprise and consumer mobile apps using Xamarin on the Apple iOS, Android, and Windows Phone platforms. We’ve built numerous apps utilizing Good Dynamics.  Contact us for more information on how we can work with you to build highly secure, cross-platform mobile applications.

Your email address will not be published. Required fields are marked *

Phone: 312-602-4000
Email: marketing@westmonroepartners.com
222 W. Adams
Chicago, IL 60606
Show Buttons
Share On Facebook
Share On Twitter
Share on LinkedIn
Hide Buttons