Securizarea și criptarea datelor pe iOS

Indiferent dacă creați o aplicație mobilă sau un serviciu web, păstrarea sigură a datelor sensibile este importantă și securitatea a devenit un aspect esențial al fiecărui produs software. În acest tutorial, vă vom arăta cum să stocați în siguranță acreditările utilizatorului folosind cheia de chei a aplicației și vom analiza criptarea și decriptarea datelor de utilizator utilizând o bibliotecă a unei terțe părți.


Introducere

În acest tutorial, vă voi învăța cum să vă asigurați date sensibile pe platforma iOS. Datele sensibile pot fi acreditările contului utilizatorului sau detaliile cărții de credit. Tipul de date nu este atât de important. În acest tutorial vom folosi keychain-ul și criptarea simetrică a iOS pentru stocarea sigură a datelor utilizatorului. Înainte de a intra în detaliile dură, aș vrea să vă dau o privire de ansamblu asupra a ceea ce vom face în acest tutorial.

Chiar dacă acest tutorial se concentrează pe iOS, conceptele și tehnicile pot fi de asemenea folosite pe OS X.

iOS Keychain

Pe iOS și OS X, un keychain este un container criptat pentru stocarea parolelor și a altor date care trebuie securizate. Pe OS X, este posibil să se limiteze accesul la cheie pentru anumiți utilizatori sau aplicații. Pe iOS, totuși, fiecare aplicație are propriul keychain la care are acces numai aplicația. Acest lucru asigură că datele stocate în brelocul de chei sunt sigure și inaccesibile de către terți.

Rețineți că brelocul trebuie utilizat numai pentru stocarea unor mici fragmente de date, cum ar fi parolele. Cu acest articol, sper să vă convingem de valoarea utilizării cheii de caractere pe iOS și OS X în loc de baza de date a aplicațiilor implicite, care stochează datele sale în text simplu, fără nici o formă de securitate.

În aplicația iOS, o aplicație poate utiliza lanțul de chei prin Keychain Services API. API-ul oferă o serie de funcții pentru manipularea datelor stocate în cheia de breloc a aplicației. Uitați-vă la funcțiile disponibile pe iOS.

  • SecItemAdd Această funcție este utilizată pentru a adăuga un element la cheia de chei a aplicației.
  • SecItemCopyMatching Utilizați această funcție pentru a găsi un element cheie cheie deținut de aplicație.
  • SecItemDelete După cum sugerează și numele, această funcție poate fi utilizată pentru a elimina un element din cheia de chei a aplicației.
  • SecItemUpdate Utilizați această funcție dacă aveți nevoie să actualizați un element din brelocul cheie al aplicației.

Keychain Services API este un API C, dar sper că nu vă împiedică să îl utilizați. Fiecare dintre funcțiile de mai sus acceptă un dicționar (CFDictionaryRef), care conține o pereche de clase de valoare de clasă de elemente și perechi opționale cheie-valoare atribut. Sensul exact și scopul fiecăruia vor deveni clare odată ce vom începe să folosim API-ul într-un exemplu.


Criptare și decriptare

Când discutăm despre criptare, aud în general despre două tipuri de criptare, simetric și asimetric criptare. Criptarea simetrică, pe de o parte, utilizează o cheie partajată pentru criptarea și decriptarea datelor. Criptarea asimetrică, pe de altă parte, utilizează o cheie pentru criptarea datelor și o altă cheie separată, dar legată de decriptarea datelor.

În acest tutorial, vom folosi levierul Cadrul de securitate disponibil pe iOS pentru a cripta și decripta datele. Acest proces are loc sub capota, astfel încât nu vom interacționa direct cu acest cadru. Vom folosi criptarea simetrică în aplicația noastră de exemplu.

Cadrul de securitate oferă o serie de alte servicii, cum ar fi serviciile de Randomizare pentru generarea de numere aleatorii securizate criptografic, certificate, cheie și servicii de încredere pentru gestionarea certificatelor, chei publice și private și politici de încredere. Cadrul de securitate este un cadru de nivel scăzut disponibil atât pe platformele iOS și OS X, cât și pe API bazate pe C.


Prezentare generală a aplicațiilor

În acest tutorial, vă voi arăta cum puteți utiliza API-ul Keychain Services și criptarea simetrică într-o aplicație iOS. Vom crea o aplicație mică care stochează în siguranță fotografiile realizate de utilizator.

În acest proiect, vom folosi Sam Soffes SSKeychain, un obiect Object-C pentru interacțiunea cu API-ul Keychain Services. Pentru criptare și decriptare, vom folosi RNCryptor, o bibliotecă de criptare terță parte.


Criptarea datelor cu RNCryptor

Biblioteca RNCryptor este o alegere bună pentru criptarea și decriptarea datelor. Proiectul este folosit de mulți dezvoltatori și este întreținut activ de creatorii săi. Biblioteca oferă un ușor de utilizat Obiectiv-C API. Dacă sunteți familiarizați cu Cacao și Obiectiv-C, veți găsi ușor de utilizat. Principalele caracteristici ale bibliotecii sunt enumerate mai jos.

  • AES-256 Criptare
  • Mod CBC
  • Partajarea parolei cu PBKDF2
  • Saltare cu parola
  • Random IV
  • Criptarea-Hash-Hash

Flux de aplicații

Înainte de a începe să construim aplicația, permiteți-mi să vă arăt cum va arăta fluxul tipic al aplicației.

  • Când utilizatorul lansează aplicația, ea este prezentată cu scopul de a vă conecta.
  • Dacă nu și-a creat încă un cont, acreditările ei sunt adăugate la cheia de breloc și ea sa conectat.
  • Dacă are un cont, dar introduce o parolă incorectă, este afișat un mesaj de eroare.
  • Odată ce a semnat, are acces la fotografiile pe care le-a făcut cu aplicația. Fotografiile sunt stocate în siguranță de aplicație.
  • De fiecare dată când face o fotografie cu camera aparatului sau alege o fotografie din biblioteca foto, fotografia este criptată și stocată în aplicația Documente director.
  • Ori de câte ori se trece la o altă aplicație sau dacă dispozitivul se blochează, se deconectează automat.

Construirea aplicației

Pasul 1: Configurarea proiectului

Activați Xcode și creați un nou proiect selectând Vizualizare individuală șablon din lista de șabloane.


Denumiți proiectul Fotografiile securizate și stabilit Familia de dispozitive la iPhone. Spuneți Xcode unde doriți să salvați proiectul și să loviți Crea.


Pasul 2: Cadre

Următorul pas este legarea proiectului împotriva Securitate și Servicii mobile de bază cadre. Selectați proiectul din Project Navigator în partea stângă, alegeți prima țintă numită Fotografiile securizate, și deschideți Construiți faze în partea de sus. Extindeți Link binar cu biblioteci sertarul și leagă proiectul împotriva Securitate și Servicii mobile de bază cadre.


Pasul 3: Dependențe

După cum am menționat mai devreme, vom folosi biblioteca SSKeychain și biblioteca RNCryptor. Descărcați aceste dependențe și adăugați-le în proiect. Asigurați-vă că ați copiat fișierele în proiectul dvs. și adăugați-le la Fotografiile securizate vizați imaginea de mai jos.


Pasul 4: Crearea de clase

Vom afișa fotografiile utilizatorului într-o vizualizare de colecție, ceea ce înseamnă că avem nevoie de subclasă UICollectionViewController precum și UICollectionViewCell. Selectați Nou> Fișier ... de la Fişier meniu, creați o subclasă de UICollectionViewController, și numește-o MTPhotosViewController. Repetați acest pas pentru încă o dată MTPhotoCollectionViewCell, care este o subclasă de UICollectionViewCell.

Pasul 5: Crearea interfeței utilizator

Deschideți tabloul de bord principal al proiectului și actualizați tabloul de bord, așa cum se arată în imaginea de mai jos. Tabloul de bord conține două controale de vizualizare, o instanță de MTViewController, care conține două câmpuri de text și un buton și o instanță de MTPhotosViewController. MTViewController instanța este încorporată într-un controler de navigare.

Trebuie, de asemenea, să creeze o problemă din partea MTViewController exemplu la MTPhotosViewController instanță. Setați identificatorul segului la photosViewController. MTPhotosViewController instanța ar trebui să conțină, de asemenea, un element din butonul de bare, așa cum se arată în imaginea de mai jos.


Pentru a face toate aceste lucruri, trebuie să actualizăm interfața MTViewController așa cum se arată mai jos. Declarăm o priză pentru fiecare câmp de text și o acțiune declanșată de buton. Asigurați conexiunile necesare în tabloul de bord al proiectului.

 #import  @interface MTViewController: UIViewController @property (slab, nonatomic) IBOutlet UITextField * usernameTextField; @property (slab, nonatomic) IBOutlet UITextField * passwordTextField; - (IBAction) autentificare: (id) expeditor; @Sfârșit

În MTPhotosViewController clasă, să declare o proprietate numită nume de utilizator pentru a memora numele de utilizator al utilizatorului conectat în prezent și a declara o acțiune pentru elementul butonului de bare. Nu uitați să conectați acțiunea cu elementul butonului bar din tabloul de bord principal.

 #import  @interface MTPhotosViewController: UICollectionViewController @property (copie, nonatomic) NSString * username; - (IBAction) fotografii: (id) expeditor; @Sfârșit

Pasul 6: Implementarea MTViewController

În MTViewController.m, adăugați o declarație de import pentru MTPhotosViewController clasa, SSKeychain clasa, și MTAppDelegate clasă. De asemenea, ne conformăm MTViewController clasa la UIAlertViewDelegate protocol.

 #import "MTViewController.h" #import "SSKeychain.h" #import "MTAppDelegate.h" #import "MTPhotosViewController.h" @interface MTViewController ()  @Sfârșit

Următorul pas este implementarea Logare: acțiune pe care am declarat-o mai devreme. Mai întâi verificăm dacă utilizatorul a creat deja un cont prin preluarea parolei pentru cont. Dacă este adevărat, folosim cheia de chei a aplicației pentru a vedea dacă parola introdusă de utilizator corespunde cu cea stocată în breloc. Metodele furnizate de SSKeychain biblioteca facilitează citirea și manipularea datelor stocate în lanțul de chei al aplicației.

 - (Id) expeditorul if (self.usernameTextField.text.length> 0 && self.passwordTextField.text.length> 0) NSString * password = [SSKeychain passwordForService: @ contul "MyPhotos": self.usernameTextField .text]; dacă (password.length> 0) if ([self.passwordTextField.text esteEqualToString: parola]) [self performSegueWithIdentifier: @ "photosViewController" expeditor: nil];  altceva UIAlertView * alertView = [[UIAlertView alloc] initWithTitle: @ Mesaj "Error Login": @ "Combinație de nume de utilizator / parolă nevalidă". delegat: nil cancelButtonTitle: @ "OK" altButtonTitle: nil]; [alertView show];  altceva UIAlertView * alertView = [[UIAlertView alloc] initWithTitle: @ Mesaj "Cont nou": @ "Doriți să creați un cont?" delegat: self cancelButtonTitle: @ "Anulare" altButtonTitles: @ "OK", nul]; [alertView show];  altceva UIAlertView * alertView = [[UIAlertView alloc] initWithTitle: @ Mesajul "Error Input": @ "Numele de utilizator și / sau parola nu pot fi goale". delegat: nil cancelButtonTitle: @ "OK" altButtonTitle: nil]; [alertView show]; 

Am setat controlerul de vizualizare ca delegat al vizualizării de avertizare, ceea ce înseamnă că trebuie să implementăm UIAlertViewDelegate protocol. Aruncați o privire la punerea în aplicare a alertView: clickedButtonAtIndex: prezentat mai jos.

 -(void) alertView: (UIAlertView *) alertView clickedButtonAtIndex: (NSInteger) buttonIndex comutator (buttonIndex) caz 0: break; cazul 1: [self createAccount]; pauză; prestabilit: pauză; 

În creează cont, noi folosim SSKeychain pentru a păstra în siguranță numele de utilizator și parola alese de utilizator. Apoi sunăm performSegueWithIdentifier: expeditor:.

 - (void) createAccount BOOL rezultat = [SSKeychain setPassword: self.passwordTextField.text forService: @ contul "MyPhotos": self.usernameTextField.text]; dacă (rezultat) [self performSegueWithIdentifier: @ "photosViewController" expeditor: nil]; 

În prepareForSegue: expeditor:, avem o referință la MTPhotosViewController exemplu, setați-o nume de utilizator proprietate cu valoarea usernameTextField, și resetați passwordTextField.

 - (void) prepareForSegue: (UIStoryboardSegue *) expeditor segue: (id) expeditor MTPhotosViewController * photosViewController = segue.destinationViewController; photosViewController.username = auto.usernameTextField.text; auto.passwordTextField.text = nil; 

Pasul 7: Implementarea MTPhotosCollectionViewCell

Deschis MTPhotosCollectionViewCell.h și să declare o priză denumită ImageView de tip UIImageView.

 #import  @interface MTPhotoCollectionViewCell: UICollectionViewCell @property (slab, nonatomic) IBOutlet UIImageView * imageView; @Sfârșit

Deschideți tabloul de bord principal și adăugați unul UIImageView exemplu la celula prototip a MTPhotosViewController instanță. Selectați celula prototip (nu vizualizarea imaginii) și setați clasa la MTPhotosCollectionViewCell în Inspectorul de identitate pe dreapta. Cu celula prototipului selectată, deschideți Atribuții Inspector și setați identificatorul la fotoelement.

Pasul 8: Implementarea MTPhotosViewController

Începeți prin importarea fișierelor antet necesare în MTPhotosViewController.m așa cum se arată mai jos. De asemenea, trebuie să declarăm două proprietăți, fotografii pentru stocarea gamei de fotografii pe care va afișa vizualizarea colecției și fILEPATH pentru a păstra o referință la calea fișierului. S-ar putea să fi observat că MTPhotosViewController clasa este conformă cu UIActionSheetDelegate, UINavigationControllerDelegate, și UIImagePickerControllerDelegate protocoale.

 #import "MTPhotosViewController.h" #import  #import "RNDecryptor.h" #import "RNEncryptor.h" #import "MTPhotoCollectionViewCell.h" @interface MTPhotosViewController ()  @property (puternic, nonatomic) NSMutableArray * fotografii; @property (copy, nonatomic) NSString * filePath; @Sfârșit

Am implementat, de asemenea, o metodă de ajutor sau ajutor, setupUserDirectory, pentru crearea și configurarea directoarelor necesare în care vom stoca datele utilizatorului. În prepareData, aplicația decriptează imaginile stocate în directorul securizat al utilizatorului. Uitați-vă la implementările lor de mai jos.

 - (void) setupUserDirectory NSArray * căi = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES); NSString * documents = [căi objectAtIndex: 0]; self.filePath = [documente stringByAppendingPathComponent: nume auto.username]; NSFileManager * fișierManager = [NSFileManager defaultManager]; dacă ([fileManager fileExistsAtPath: self.filePath]) NSLog (@ "Directorul este deja prezent.");  altfel NSError * error = nil; [fileManager createDirectoryAtPath: auto.filePath cuIntermediateDirectories: YES atribute: eroare zero: & eroare]; dacă (eroare) NSLog (@ "Nu se poate crea directorul pentru utilizator."); 
 - (void) prepareData auto.photos = [[NSMutableArray alocare] init]; NSFileManager * fișierManager = [NSFileManager defaultManager]; NSError * eroare = zero; NSArray * contents = [fileManager contentsOfDirectoryAtPath: auto.filePachete eroare: & eroare]; dacă ([count count] &&! error) NSLog (@ "Conținutul directorului utilizatorului% @", conținut); pentru (NSString * numele fișierului în conținut) if ([nume_firma rangeOfString: @ "securedData"] lungime> 0) NSData * data = [NSData dataWithContentsOfFile: [self.filePath stringByAppendingPathComponent: fileName]; NSData * decryptedData = [RNDecryptor decryptData: date cuSettings: kRNCryptorAES256Setare parola: @ "A_SECRET_PASSWORD" eroare: nil]; UIImage * Imagine = [UIImage imageWithData: decryptedData]; [auto.photos addObject: imagine];  altceva NSLog (@ "Acest fișier nu este securizat.");  altfel dacă (! count count)) if (eroare) NSLog (@ "Nu se poate citi conținutul directorului utilizatorului");  altceva NSLog (@ "Directorul utilizatorului este gol"); 

Invocați ambele metode în controlerul de vizualizare viewDidLoad așa cum se arată mai jos.

 - (vid) viewDidLoad [super viewDidLoad]; [self setupUserDirectory]; [self prepareData]; 

Elementul butonului barei din bara de navigare a controlerului vizual prezintă o foaie de acțiune care permite utilizatorului să aleagă între camera foto a aparatului și biblioteca de fotografii.

 - (IBAction) fotografii: (id) expeditor UIActionSheet * actionSheet = [[UIActionSheet alocare] initWithTitle: @ "Selectare sursă" delegat: self cancelButtonTitle: @ "Cancel" destructiveButtonTitle: , nil]; [actionSheet showFromBarButtonItem: expeditor animat: DA]; 

Să implementăm actionSheet: clickedButtonAtIndex: din UIActionSheetDelegate protocol.

 - (void) actionSheet: (UIActionSheet *) actionSheet clickedButtonAtIndex: (NSInteger) buttonIndex if (butonIndex < 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];  

Pentru a gestiona selecția utilizatorului în controlerul de selectare a imaginilor, trebuie să implementăm imagePickerController: didFinishPickingMediaWithInfo: din UIImagePickerControllerDelegate așa cum se arată mai jos. Imaginea este criptată folosind encryptData din RNEncryptor bibliotecă. Imaginea este, de asemenea, adăugată la fotografii și vizualizarea colecției este reîncărcată.

 - (void) imagePickerController: (UIImagePickerController *) selectorul didFinishPickingMediaWithInfo: (NSDictionary *) info UIImage * image = [info objectForKey: UIImagePickerControllerEditedImage]; dacă (! image) [info objectForKey: UIImagePickerControllerOriginalImage];  NSData * imageData = UIImagePNGRpresentation (imagine); NSString * imageName = [NSString stringWithFormat: @ "imagine-% d.securedData", auto.photos.count + 1]; NSData * encryptedImage = [RNEncryptor encryptData: imageData withSettings: kRNCryptorAES256Setare parola: @ "A_SECRET_PASSWORD" eroare: nil]; [encryptedImage writeToFile: [auto.filePath stringByAppendingPathComponent: imageName] atomic: YES]; [auto.photos addObject: imagine]; [self.collectionView reloadData]; [selector dismissViewControllerAnimated: DA finalizare: nil]; 

Înainte de a putea construi și rula aplicația, trebuie să implementăm UICollectionViewDataSource așa cum se arată mai jos.

 - (NSInteger) collectionView: (UICollectionView *) colecțieVizualizare numărOfItemsInSection: (NSInteger) secțiunea return self.photos? auto.photos.count: 0; 
 - (UICollectionViewCell *) colecțieView: (UICollectionView *) colecțieVezi cellForItemAtIndexPath: (NSIndexPath *) indexPath MTPhotoCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier: @ "PhotoCell" pentruIndexPath: indexPath]; cell.imageView.image = [auto.photos obiectAtIndex: indexPath.row]; celule retur; 

Pasul 9: Manipularea statelor de aplicare

Dacă aplicația merge în fundal, utilizatorul trebuie să fie deconectat. Acest lucru este important din perspectiva securității. Pentru a realiza acest lucru, delegatul aplicației trebuie să aibă o referință la controlerul de navigație, astfel încât acesta să poată apărea la controlerul de vizualizare rădăcină al stivei de navigare. Începeți prin a declara o proprietate numită navigationController în MTAppDelegate.h.

 #import  @interface MTAppDelegate: UIResponder  @property (puternic, nonatomic) fereastra UIWindow *; @property (puternic, nonatomic) UINavigationController * navigationController; @Sfârșit

În controlerul de vizualizare viewDidLoad metoda, am setat delegatul aplicației navigationController proprietate după cum se arată mai jos. Rețineți că aceasta este doar o modalitate de a face față acestei situații.

Am setat proprietatea de mai sus în lui ViewController viewDidLoad așa cum se arată mai jos.

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

În delegatul aplicației, trebuie să ne actualizăm applicationWillResignActive: așa cum se arată mai jos. Este la fel de simplu ca asta. Rezultatul este că utilizatorul este deconectat ori de câte ori aplicația pierde focalizarea. Acesta va proteja imaginile utilizatorului stocate în aplicație de ochii curioși. Dezavantajul este că utilizatorul trebuie să se conecteze când aplicația devine din nou activă.

 - (void) applicationWillResignActive: (UIAapplication *) cerere [self.navigationController popToRootViewControllerAnimated: NO]; 

Pasul 10: Construiți și executați

Construiți proiectul și rulați aplicația pentru ao pune în pași.


Concluzie

În acest tutorial, ați învățat cum să utilizați API-ul Keychain Services pentru a stoca date sensibile și de asemenea ați învățat cum să criptați datele de imagine pe iOS. Lăsați un comentariu în comentariile de mai jos dacă aveți întrebări sau feedback.

Cod