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.
Î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.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.
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.
Î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.
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.
Înainte de a începe să construim aplicația, permiteți-mi să vă arăt cum va arăta fluxul tipic al aplicației.
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.
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.
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.
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
.
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
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;
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
.
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;
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];
Construiți proiectul și rulați aplicația pentru ao pune în pași.
Î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.