Säkra och kryptera data på iOS

Oavsett om du skapar en mobilapplikation eller en webbtjänst, är det viktigt att hålla känslig data säker och säkerhet har blivit en viktig aspekt av varje mjukvaruprodukt. I den här handledningen kommer jag att visa dig hur du säkert lagrar användaruppgifter med hjälp av programmets nyckelring och vi tar en titt på kryptering och dekryptering av användardata med hjälp av ett tredje parts bibliotek.


Introduktion

I denna handledning lär jag dig hur man skyddar känslig data på iOS-plattformen. Känslig data kan vara användarens kontouppgifter eller kreditkortsuppgifter. Typ av data är inte så viktigt. I den här handledningen använder vi IOS nyckelring och symmetrisk kryptering för att säkert lagra användarens data. Innan vi kommer in i nitty-gritty detaljer, skulle jag vilja ge dig en översikt över vad vi ska göra i denna handledning.

Även om denna handledning fokuserar på iOS, kan koncepten och teknikerna också användas på OS X.

IOS Keychain

På IOS och OS X är en nyckelring en krypterad behållare för lagring av lösenord och annan data som behöver säkras. På OS X är det möjligt att begränsa nyckelring till vissa användare eller applikationer. På IOS har varje applikation dock en egen nyckelring som endast har programmet åtkomst till. Detta säkerställer att data som är lagrade i nyckelring är säker och otillgänglig av tredje part.

Tänk på att nyckelringen endast ska användas för att lagra små bitar av data, till exempel lösenord. Med den här artikeln hoppas jag kunna övertyga dig om värdet av att använda nyckelring på IOS och OS X istället för till exempel programmets standarddatabas, som lagrar data i vanlig text utan någon form av säkerhet.

På IOS kan en applikation använda nyckelringen via Keychain Services API. API: n ger ett antal funktioner för att manipulera data som lagras i programmets nyckelring. Ta en titt på funktionerna som finns tillgängliga på iOS.

  • SecItemAdd Den här funktionen används för att lägga till ett objekt i programmets nyckelring.
  • SecItemCopyMatching Du använder den här funktionen för att hitta ett nyckelring objekt som ägs av programmet.
  • SecItemDelete Som namnet antyder kan den här funktionen användas för att ta bort ett objekt från programmets nyckelring.
  • SecItemUpdate Använd den här funktionen om du behöver uppdatera ett objekt i programmets nyckelring.

De Keychain Services API är ett C API, men jag hoppas det hindrar dig inte från att använda den. Var och en av ovanstående funktioner accepterar en ordlista (CFDictionaryRef), som innehåller en tangentvärdespar för objektklassen och valfria parametrar för attributvärden. Den exakta meningen och syftet med varje kommer att bli tydlig när vi börjar använda API: n i ett exempel.


Kryptering och dekryptering

När du diskuterar kryptering hör du vanligtvis om två typer av kryptering, symmetrisk och asymmetrisk kryptering. Symmetrisk kryptering använder å ena sidan en gemensam nyckel för kryptering och dekryptering av data. Asymmetrisk kryptering använder å andra sidan en nyckel för kryptering av data och en annan separat men relaterad nyckel för att dekryptera data.

I den här handledningen använder vi oss av Säkerhetsramar tillgänglig på iOS för att kryptera och dekryptera data. Denna process sker under huven så vi kommer inte direkt att interagera med denna ram. Vi använder symmetrisk kryptering i vårt exempelprogram.

De Säkerhetsramar erbjuder ett antal andra tjänster, till exempel Randomization-tjänster för att generera kryptografiskt säkra slumptal, certifikat, nyckel och förtroende för hantering av certifikat, offentliga och privata nycklar och förtroendepolicyer. De Säkerhetsramar är en ram på låg nivå tillgänglig på både iOS och OS X med C-baserade API.


Applikationsöversikt

I denna handledning kommer jag att visa dig hur du kan använda Keychain Services API och symmetrisk kryptering i en iOS-applikation. Vi skapar en liten applikation som säkert lagrar foton som tas av användaren.

I det här projektet använder vi Sam Soffes SSKeychain, ett Objective-C wrapper för att interagera med Keychain Services API. För kryptering och dekryptering använder vi RNCryptor, ett krypteringsbibliotek från tredje part.


Kryptera data med RNCryptor

RNCryptor-biblioteket är ett bra val för kryptering och dekryptering av data. Projektet används av många utvecklare och underhålls aktivt av sina skapare. Biblioteket erbjuder ett enkelt att använda Objective-C API. Om du är bekant med kakao och objektiv-C, hittar du det lätt att använda. Bibliotekets huvudfunktioner listas nedan.

  • AES-256-kryptering
  • CBC-läge
  • Lösenord som sträcker sig med PBKDF2
  • Lösenordssaltning
  • Slumpmässig IV
  • Kryptera-sedan-Hash HMAC

Applikationsflöde

Innan vi börjar bygga applikationen, låt mig visa dig vad det typiska flödet av ansökan kommer att se ut.

  • När användaren startar programmet, presenteras hon med en syn att logga in.
  • Om hon inte har skapat ett konto än, läggs hennes uppgifter till nyckelring och hon är inloggad.
  • Om hon har ett konto, men matar in ett felaktigt lösenord visas ett felmeddelande.
  • När hon är inloggad har hon tillgång till de bilder hon tagit med ansökan. Bilderna lagras säkert av programmet.
  • När hon tar ett foto med enhetens kamera eller väljer ett foto från hennes fotobibliotek krypteras fotot och sparas i programmets Dokument katalog.
  • När hon växlar till en annan applikation eller enheten blir låst, loggas hon automatiskt ut.

Bygga applikationen

Steg 1: Projektinställningar

Avbryt Xcode och skapa ett nytt projekt genom att välja Enkel visningsprogram mall från listan med mallar.


Namn på projektet Säkra foton och ställa in Enhetsfamilj till iPhone. Berätta Xcode där du vill spara projektet och träffa Skapa.


Steg 2: Ramar

Nästa steg är att länka projektet mot säkerhet och Mobile Core Services ramar. Välj projektet i Project Navigator till vänster, välj det första målet som heter Säkra foton, och öppna Bygga faser fliken längst upp. Expandera Länk binär med bibliotek lådan och länka projektet mot säkerhet och Mobile Core Services ramar.


Steg 3: Beroenden

Som jag nämnde tidigare använder vi SSKeychain-biblioteket och RNCryptor-biblioteket. Ladda ner dessa beroenden och lägg till dem i projektet. Se till att kopiera filerna till ditt projekt och lägg till dem i Säkra foton målet som visas på skärmdumpen nedan.


Steg 4: Skapa klasser

Vi visar användarens foton i en samlingsvy, vilket innebär att vi behöver subklass UICollectionViewController såväl som UICollectionViewCell. Välj Ny> Fil ... från Fil meny, skapa en underklass av UICollectionViewController, och namnge det MTPhotosViewController. Upprepa detta steg en gång till för MTPhotoCollectionViewCell, vilket är en underklass av UICollectionViewCell.

Steg 5: Skapa användargränssnittet

Öppna projektets huvud storyboard och uppdatera storyboardet som visas på skärmdumpen nedan. Berättelsen innehåller två visningsstyrare, en förekomst av MTViewController, som innehåller två textfält och en knapp och en förekomst av MTPhotosViewController. De MTViewController Exempel är inbäddad i en navigeringsenhet.

Vi måste också skapa en segue från MTViewController instans till MTPhotosViewController exempel. Ange segusens identifierare till photosViewController. De MTPhotosViewController Instans bör också innehålla en streckknappsartikel som visas på skärmdumpen nedan.


För att göra allt detta måste vi uppdatera gränssnittet för MTViewController enligt nedanstående. Vi förklarar ett uttag för varje textfält och en åtgärd som utlöses av knappen. Gör de nödvändiga anslutningarna i projektets huvudsakliga storyboard.

 #importera  @interface MTViewController: UIViewController @property (svag, ickeatomisk) IBOutlet UITextField * användarnamnTextField; @property (svag, ickeatomisk) IBOutlet UITextField * passwordTextField; - (IBAction) inloggning: (id) avsändare; @slutet

I MTPhotosViewController klass, förklara en egendom som heter Användarnamn för att lagra användarnamnet för den för närvarande inloggade användaren och förklara en åtgärd för knappknappsposten. Glöm inte att ansluta åtgärden med knappknappsposten i huvud storyboard.

 #importera  @interface MTPhotosViewController: UICollectionViewController @property (copy, nonatomic) NSString * användarnamn; - (IBAction) foton: (id) avsändare; @slutet

Steg 6: Implementering MTViewController

I MTViewController.m, lägg till ett import uttalande för MTPhotosViewController klass, the SSKeychain klass, och MTAppDelegate klass. Vi överensstämmer också med MTViewController klass till UIAlertViewDelegate protokoll.

 #import "MTViewController.h" #import "SSKeychain.h" #import "MTAppDelegate.h" #import "MTPhotosViewController.h" @interface MTViewController ()  @slutet

Nästa steg är att genomföra logga in: åtgärder vi förklarade tidigare. Vi kontrollerar först om användaren redan har skapat ett konto genom att hämta lösenordet för kontot. Om det är sant använder vi programmets nyckelring för att se om lösenordet som användaren har angett matchar den som lagrats i nyckelringen. De metoder som tillhandahålls av SSKeychain biblioteket gör det enkelt att läsa och manipulera data lagrade i programmets nyckelring.

 - (IBAction) login: (id) avsändare if (self.usernameTextField.text.length> 0 && self.passwordTextField.text.length> 0) NSString * lösenord = [SSKeychain passwordForService: @ "MyPhotos" konto: self.usernameTextField .text]; om (password.length> 0) if ([self.passwordTextField.text isEqualToString: lösenord]) [self performSegueWithIdentifier: @ "photosViewController" avsändare: nil];  annan UIAlertView * alertView = [[UIAlertView-tilldelningen] initWithTitle: @ "Fel inloggning" meddelande: @ "Ogiltigt användarnamn / lösenordskombination." delegera: noll cancelButtonTitle: @ "OK" otherButtonTitles: nil]; [alertView show];  else UIAlertView * alertView = [[UIAlertView-tilldelningen] initWithTitle: @ "New Account" -meddelandet: @ "Vill du skapa ett konto?" delegera: self cancelButtonTitle: @ "Cancel" otherButtonTitles: @ "OK", noll]; [alertView show];  annat UIAlertView * alertView = [[UIAlertView-tilldelningen] initWithTitle: @ "Felinmatning" meddelande: @ "Användarnamn och / eller lösenord kan inte vara tomt." delegera: noll cancelButtonTitle: @ "OK" otherButtonTitles: nil]; [alertView show]; 

Vi har ställt vyskontrollen som varningsvyns delegat, vilket innebär att vi måste implementera UIAlertViewDelegate protokoll. Ta en titt på genomförandet av alertView: clickedButtonAtIndex: visas nedan.

 -(void) alertView: (UIAlertView *) alertVisa clickedButtonAtIndex: (NSInteger) buttonIndex switch (buttonIndex) fall 0: break; fall 1: [self createAccount]; ha sönder; standard: break; 

I skapa konto, vi utnyttjar SSKeychain klass för att säkert lagra användarnamnet och lösenordet som användaren valt. Vi ringer sedan performSegueWithIdentifier: avsändare:.

 - (void) createAccount BOOL result = [SSKeychain setPassword: self.passwordTextField.text forService: @ "MyPhotos" -konto: self.usernameTextField.text]; om (resultat) [själv utförSegueWithIdentifier: @ "photosViewController" avsändare: noll]; 

I prepareForSegue: avsändare:, vi får en hänvisning till MTPhotosViewController Exempel, sätt dess Användarnamn egendom med värdet av usernameTextField, och återställ passwordTextField.

 - (void) prepareForSegue: (UIStoryboardSegue *) segue avsändare: (id) avsändare MTPhotosViewController * photosViewController = segue.destinationViewController; photosViewController.username = self.usernameTextField.text; self.passwordTextField.text = nil; 

Steg 7: Implementering MTPhotosCollectionViewCell

Öppna MTPhotosCollectionViewCell.h och deklarera ett utlopp som heter Imageview av typ UIImageView.

 #importera  @interface MTPhotoCollectionViewCell: UICollectionViewCell @property (svag, ickeatomisk) IBOutlet UIImageView * imageView; @slutet

Öppna huvud storyboardet och lägg till en UIImageView Exempel på prototypcellen i MTPhotosViewController exempel. Välj prototypcellen (inte bildvyn) och sätt dess klass till MTPhotosCollectionViewCell i Identitetsinspektör till höger. Med prototypcellen fortfarande vald, öppna Attribut Inspector och ange identifieraren till FOTOCELL.

Steg 8: Implementering MTPhotosViewController

Börja med att importera nödvändiga rubrikfiler i MTPhotosViewController.m enligt nedanstående. Vi måste också deklarera två egenskaper, foton för att lagra en uppsättning bilder som samlingsvisningen kommer att visa och sökväg för att behålla en referens till filbanan. Du kanske har märkt att MTPhotosViewController klassen överensstämmer med UIActionSheetDelegate, UINavigationControllerDelegate, och UIImagePickerControllerDelegate protokoll.

 #import "MTPhotosViewController.h" #import  #import "RNDecryptor.h" #import "RNEncryptor.h" #import "MTPhotoCollectionViewCell.h" @interface MTPhotosViewController ()  @property (stark, icke-atomisk) NSMutableArray * foton; @property (copy, nonatomic) NSString * filePath; @slutet

Jag har också implementerat en bekvämlighets- eller hjälparätt, setupUserDirectory, för att skapa och konfigurera nödvändiga kataloger där vi lagrar användarens data. I prepareData, programmet dekrypterar bilderna som lagras i användarens säkra katalog. Ta en titt på deras implementeringar nedan.

 - (void) setupUserDirectory NSArray * paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES); NSString * documents = [paths objectAtIndex: 0]; self.filePath = [documents stringByAppendingPathComponent: self.username]; NSFileManager * fileManager = [NSFileManager defaultManager]; om ([fileManager fileExistsAtPath: self.filePath]) NSLog (@ "Directory redan närvarande.");  else NSError * error = nil; [fileManager createDirectoryAtPath: self.filePath withIntermediateDirectories: JA attribut: nil error: & error]; om (fel) NSLog (@ "Kan inte skapa katalog för användare."); 
 - (void) prepareData self.photos = [[NSMutableArray alloc] init]; NSFileManager * fileManager = [NSFileManager defaultManager]; NSError * error = nil; NSArray * innehåll = [fileManager contentOfDirectoryAtPath: self.filePath error: & error]; om ([content count] &&! error) NSLog (@ "Innehållet i användarens katalog.% @", innehåll); för (NSString * filnamn i innehåll) if ([fileName rangeOfString: @ ".DecuredData"]. längd> 0) NSData * data = [NSData dataWithContentsOfFile: [self.filePath stringByAppendingPathComponent: fileName]]; NSData * decryptedData = [RNDecryptor decryptData: data withSettings: kRNCryptorAES256Sättnings lösenord: @ "A_SECRET_PASSWORD" fel: noll]; UIImage * image = [UIImage imageWithData: decryptedData]; [self.photos addObject: bild];  else NSLog (@ "Den här filen är inte säker.");  annars om (! [innehållsräkning]) om (fel) NSLog (@ "Kan inte läsa innehållet i användarens katalog.");  else NSLog (@ "Användarens katalog är tom."); 

Invoke båda metoderna i visningskontrollerns viewDidLoad metod som visas nedan.

 - (void) viewDidLoad [super viewDidLoad]; [self setupUserDirectory]; [self preparationData]; 

Stångknappsposten i visningsregulatorens navigeringsfält visar ett handlingsblad som gör det möjligt för användaren att välja mellan enhetens kamera och fotobiblioteket.

 - (IBAction) foton: (id) avsändare UIActionSheet * actionSheet = [[UIActionSheet alloc] initWithTitle: @ "Välj källa" delegat: self cancelButtonTitle: @ "Avbryt" destructiveButtonTitle: nil otherButtonTitles: @ "Camera", @ "Photo Library" , noll]; [actionSheet showFromBarButtonItem: avsändare animerad: YES]; 

Låt oss genomföra actionSheet: clickedButtonAtIndex: av UIActionSheetDelegate protokoll.

 - (void) actionSheet: (UIActionSheet *) actionSheet clickedButtonAtIndex: (NSInteger) buttonIndex if (buttonIndex < 2)  UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init]; imagePickerController.mediaTypes = @[(__bridge NSString *)kUTTypeImage]; imagePickerController.allowsEditing = YES; imagePickerController.delegate = self; if (buttonIndex == 0)  #if TARGET_IPHONE_SIMULATOR #else imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera; #endif  else if ( buttonIndex == 1)  imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;  [self.navigationController presentViewController:imagePickerController animated:YES completion:nil];  

För att hantera användarens val i bildväljaren måste vi implementera imagePickerController: didFinishPickingMediaWithInfo: av UIImagePickerControllerDelegate protokoll som visas nedan. Bilden är krypterad med encryptData av RNEncryptor bibliotek. Bilden läggs också till i foton array och samlingsvyn uppdateras.

 - (void) imagePickerController: (UIImagePickerController *) plockare didFinishPickingMediaWithInfo: (NSDictionary *) info UIImage * image = [info objectForKey: UIImagePickerControllerEditedImage]; om (! bild) [info objectForKey: UIImagePickerControllerOriginalImage];  NSData * imageData = UIImagePNGRepresentation (bild); NSString * imageName = [NSString stringWithFormat: @ "image-% d.securedData", self.photos.count + 1]; NSData * encryptedImage = [RNEncryptor encryptData: imageData withSettings: kRNCryptorAES256Sättnings lösenord: @ "A_SECRET_PASSWORD" fel: noll]; [encryptedImage writeToFile: [self.filePath stringByAppendingPathComponent: imageName] atomically: YES]; [self.photos addObject: bild]; [self.collectionView reloadData]; [picker dismissViewControllerAnimated: JA slutförd: noll]; 

Innan du kan bygga och köra programmet måste vi implementera UICollectionViewDataSource protokoll som visas nedan.

 - (NSInteger) collectionView: (UICollectionView *) collectionView numberOfItemsInSection: (NSInteger) avsnitt return self.photos? self.photos.count: 0; 
 - (UICollectionViewCell *) collectionView: (UICollectionView *) collectionVisa cellForItemAtIndexPath: (NSIndexPath *) indexPath MTPhotoCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier: @ "PhotoCell" förIndexPath: indexPath]; cell.imageView.image = [self.photos objectAtIndex: indexPath.row]; returcell; 

Steg 9: Hantera ansökningsstater

Om applikationen går till bakgrunden måste användaren vara utloggad. Detta är viktigt ur ett säkerhetsperspektiv. För att åstadkomma detta måste applikationsdelegatet ha en hänvisning till navigeringskontrollen så att den kan dyka upp i rotationsbildsregulatorn i navigeringsstapeln. Börja med att deklarera en egendom som heter navigationController i MTAppDelegate.h.

 #importera  @interface MTAppDelegate: UIResponder  @property (strong, nonatomic) UIWindow * fönster; @property (stark, icke-atomisk) UINavigationController * navigationController; @slutet

I visningsregulatorens viewDidLoad metod, vi ställer in ansökningsdelegatets navigationController egendom som visas nedan. Tänk på att detta bara är ett sätt att hantera detta.

Jag har satt ovanstående egendom i ViewController s viewDidLoad metod som visas nedan.

 - (void) viewDidLoad [super viewDidLoad]; MTAppDelegate * applicationDeleagte = (MTAppDelegate *) [[UIApplication sharedApplication] delegate]; [applicationDeleagte setNavigationController: self.navigationController]; 

I ansökningsdelegatet behöver vi uppdatera applicationWillResignActive: enligt nedanstående. Så enkelt är det. Resultatet är att användaren är utloggad när applikationen förlorar fokus. Det kommer att skydda användarens bilder lagrade i applikationen från nyfikna ögon. Nackdelen är att användaren måste logga in när applikationen blir aktiv igen.

 - (void) applicationWillResignActive: (UIApplication *) applikation [self.navigationController popToRootViewControllerAnimated: NO]; 

Steg 10: Bygg och kör

Bygg projektet och kör programmet för att sätta det genom sina steg.


Slutsats

I denna handledning lärde du dig hur du använder verktyget Keychain Services för att lagra känslig data och du lärde dig också hur man krypterar bilddata på iOS. Lämna en kommentar i kommentarerna nedan om du har några frågor eller feedback.