Protezione e crittografia dei dati su iOS

Sia che si stia creando un'applicazione mobile o un servizio Web, la protezione dei dati sensibili è importante e la sicurezza è diventata un aspetto essenziale di ogni prodotto software. In questo tutorial, ti mostrerò come memorizzare in modo sicuro le credenziali utente utilizzando il portachiavi dell'applicazione e daremo un'occhiata alla crittografia e alla decrittografia dei dati utente utilizzando una libreria di terze parti.


introduzione

In questo tutorial, ti insegnerò come proteggere i dati sensibili sulla piattaforma iOS. I dati sensibili possono essere le credenziali dell'account dell'utente o i dettagli della carta di credito. Il tipo di dati non è così importante. In questo tutorial, utilizzeremo il portachiavi di iOS e la crittografia simmetrica per archiviare in modo sicuro i dati dell'utente. Prima di entrare nei dettagli nitidi, vorrei darti una panoramica di ciò che faremo in questo tutorial.

Anche se questo tutorial si concentra su iOS, i concetti e le tecniche possono essere utilizzati anche su OS X.

Portachiavi iOS

Su iOS e OS X, un portachiavi è un contenitore crittografato per la memorizzazione di password e altri dati che devono essere protetti. Su OS X, è possibile limitare l'accesso al portachiavi a particolari utenti o applicazioni. Su iOS, tuttavia, ciascuna applicazione ha il proprio portachiavi a cui solo l'applicazione ha accesso. Ciò garantisce che i dati memorizzati nel portachiavi siano sicuri e inaccessibili da parte di terzi.

Tieni presente che il portachiavi deve essere utilizzato solo per la memorizzazione di piccoli pezzi di dati, come le password. Con questo articolo, spero di convincerti del valore dell'uso del portachiavi su iOS e OS X invece di, ad esempio, il database di default dell'utente dell'applicazione, che memorizza i suoi dati in testo semplice senza alcuna forma di sicurezza.

Su iOS, un'applicazione può utilizzare il portachiavi attraverso il API dei servizi portachiavi. L'API fornisce una serie di funzioni per la manipolazione dei dati memorizzati nel portachiavi dell'applicazione. Dai un'occhiata alle funzioni disponibili su iOS.

  • SecItemAdd Questa funzione è utilizzata per aggiungere un elemento al portachiavi dell'applicazione.
  • SecItemCopyMatching Si utilizza questa funzione per trovare un oggetto portachiavi appartenente all'applicazione.
  • SecItemDelete Come suggerisce il nome, questa funzione può essere utilizzata per rimuovere un elemento dal portachiavi dell'applicazione.
  • SecItemUpdate Utilizzare questa funzione se è necessario aggiornare un elemento nel portachiavi dell'applicazione.

Il API dei servizi portachiavi è una API C, ma spero che non ti impedisca di usarla. Ciascuna delle funzioni sopra elencate accetta un dizionario (CFDictionaryRef), che contiene una coppia chiave-valore classe oggetto e coppie chiave-valore attributo opzionale. Il significato e lo scopo esatti di ciascuno diventeranno chiari una volta che iniziamo a utilizzare l'API in un esempio.


Crittografia e decodifica

Quando si discute della crittografia, si sente generalmente su due tipi di crittografia, simmetrico e asimmetrico crittografia. La crittografia simmetrica, da un lato, utilizza una chiave condivisa per crittografare e decodificare i dati. La crittografia asimmetrica, d'altra parte, utilizza una chiave per la crittografia dei dati e un'altra chiave separata, ma correlata, per la decrittografia dei dati.

In questo tutorial, faremo leva sul Quadro di sicurezza disponibile su iOS per crittografare e decrittografare i dati. Questo processo avviene sotto il cofano quindi non interagiremo direttamente con questo framework. Useremo la crittografia simmetrica nella nostra applicazione di esempio.

Il Quadro di sicurezza offre una serie di altri servizi, come i servizi di randomizzazione per la generazione di numeri casuali protetti da crittografia, certificati, chiavi e servizi di fiducia per la gestione di certificati, chiavi pubbliche e private e politiche di fiducia. Il Quadro di sicurezza è una struttura di basso livello disponibile su iOS e OS X con API basate su C.


Panoramica dell'applicazione

In questo tutorial, ti mostrerò come utilizzare l'API di Keychain Services e la crittografia simmetrica in un'applicazione iOS. Creeremo una piccola applicazione che memorizza in modo sicuro le foto scattate dall'utente.

In questo progetto, utilizzeremo Sam Soffes SSKeychain, un wrapper Objective-C per l'interazione con l'API di Keychain Services. Per la crittografia e la decrittazione, utilizzeremo RNCryptor, una libreria di crittografia di terze parti.


Crittografia dei dati con RNCryptor

La libreria RNCryptor è una buona scelta per la crittografia e la decrittografia dei dati. Il progetto è utilizzato da molti sviluppatori e mantenuto attivamente dai suoi creatori. La libreria offre un'API Objective-C facile da usare. Se hai familiarità con Cocoa e Objective-C, lo troverai facile da usare. Le caratteristiche principali della libreria sono elencate di seguito.

  • Crittografia AES-256
  • Modalità CBC
  • Stiramento della password con PBKDF2
  • Salatura della password
  • Casuale IV
  • Encrypt-Then-Hash HMAC

Flusso di applicazioni

Prima di iniziare a costruire l'applicazione, lascia che ti mostri come sarà il flusso tipico dell'applicazione.

  • Quando l'utente avvia l'applicazione, viene presentata con la vista di accedere.
  • Se non ha ancora creato un account, le sue credenziali vengono aggiunte al portachiavi e lei ha effettuato l'accesso.
  • Se ha un account, ma inserisce una password errata, viene visualizzato un messaggio di errore.
  • Una volta che ha effettuato l'accesso, ha accesso alle foto che ha scattato con l'applicazione. Le foto sono archiviate in modo sicuro dall'applicazione.
  • Ogni volta che scatta una foto con la fotocamera del dispositivo o preleva una foto dalla sua libreria fotografica, la foto viene crittografata e archiviata nell'applicazione Documenti elenco.
  • Ogni volta che passa a un'altra applicazione o il dispositivo viene bloccato, viene automaticamente disconnesso.

Costruire l'applicazione

Passaggio 1: Impostazione del progetto

Accendi Xcode e crea un nuovo progetto selezionando il Applicazione vista singola modello dall'elenco di modelli.


Assegna un nome al progetto Foto sicure e impostare Famiglia di dispositivi per iPhone. Dillo a Xcode dove vuoi salvare il progetto e premere Creare.


Passaggio 2: quadri

Il prossimo passo è collegare il progetto al Sicurezza e Servizi di base mobile quadri. Seleziona il progetto nel Project Navigator a sinistra, scegli il primo bersaglio nominato Foto sicure, e apri il Costruisci fasi scheda in alto. Espandi il Collega binario con le librerie cassetto e collegare il progetto contro il Sicurezza e Servizi di base mobile quadri.


Passaggio 3: dipendenze

Come accennato in precedenza, utilizzeremo la libreria SSKeychain e la libreria RNCryptor. Scarica queste dipendenze e aggiungile al progetto. Assicurati di copiare i file nel tuo progetto e aggiungili al Foto sicure bersaglio come mostrato nello screenshot qui sotto.


Passaggio 4: creazione di classi

Visualizzeremo le foto dell'utente in una vista insieme, il che significa che dobbiamo creare una sottoclasse UICollectionViewController così come UICollectionViewCell. Selezionare Nuovo> File ... dal File menu, creare una sottoclasse di UICollectionViewController, e nominalo MTPhotosViewController. Ripetere questo passaggio ancora una volta per MTPhotoCollectionViewCell, che è una sottoclasse di UICollectionViewCell.

Passaggio 5: creazione dell'interfaccia utente

Apri lo storyboard principale del progetto e aggiorna lo storyboard come mostrato nello screenshot qui sotto. Lo storyboard contiene due controller di visualizzazione, un'istanza di MTViewController, che contiene due campi di testo e un pulsante e un'istanza di MTPhotosViewController. Il MTViewController l'istanza è incorporata in un controller di navigazione.

Abbiamo anche bisogno di creare un seguito dal MTViewController istanza al MTPhotosViewController esempio. Imposta l'identificatore del seguito su photosViewController. Il MTPhotosViewController l'istanza dovrebbe contenere anche una voce di pulsante bar come mostrato nello screenshot qui sotto.


Per far funzionare tutto questo, abbiamo bisogno di aggiornare l'interfaccia di MTViewController come mostrato di seguito. Dichiariamo uno sbocco per ogni campo di testo e un'azione che viene attivata dal pulsante. Crea le connessioni necessarie nello storyboard principale del progetto.

 #importare  @interface MTViewController: UIViewController @property (weak, nonatomic) IBOutlet UITextField * usernameTextField; @property (weak, nonatomic) IBOutlet UITextField * passwordTextField; - (IBAction) login: (id) mittente; @fine

Nel MTPhotosViewController classe, dichiara una proprietà chiamata nome utente per memorizzare il nome utente dell'utente attualmente connesso e dichiarare un'azione per la voce del pulsante della barra. Non dimenticare di connettere l'azione con l'elemento del pulsante della barra nello storyboard principale.

 #importare  @interface MTPhotosViewController: UICollectionViewController @property (copy, nonatomic) NSString * username; - Foto (IBAction): (id) mittente; @fine

Passaggio 6: implementazione MTViewController

Nel MTViewController.m, aggiungi una dichiarazione di importazione per MTPhotosViewController classe, il SSKeychain classe, e il MTAppDelegate classe. Inoltre conformiamo il MTViewController classe al UIAlertViewDelegate protocollo.

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

Il prossimo passo è l'implementazione del accesso: azione che abbiamo dichiarato in precedenza. Per prima cosa controlliamo se l'utente ha già creato un account recuperando la password per l'account. Se questo è vero, usiamo il portachiavi dell'applicazione per vedere se la password inserita dall'utente corrisponde a quella memorizzata nel portachiavi. I metodi forniti dal SSKeychain la libreria semplifica la lettura e la manipolazione dei dati memorizzati nel portachiavi dell'applicazione.

 - (IBAction) login: (id) sender if (self.usernameTextField.text.length> 0 && self.passwordTextField.text.length> 0) NSString * password = [SSKeychain passwordForService: @ account "MyPhotos": self.usernameTextField .testo]; if (password.length> 0) if ([self.passwordTextField.text isEqualToString: password]) [self performSegueWithIdentifier: @ "photosViewController" mittente: nil];  else UIAlertView * alertView = [[UIAlertView alloc] initWithTitle: @ Messaggio "Login errore": @ "Combinazione nome utente / password non valida." delegate: nil cancelButtonTitle: @ "OK" otherButtonTitles: nil]; [show di alertView];  else UIAlertView * alertView = [[UIAlertView alloc] initWithTitle: @ Messaggio "Nuovo account": @ "Vuoi creare un account?" delegate: self cancelButtonTitle: @ "Cancel" otherButtonTitles: @ "OK", nil]; [show di alertView];  else UIAlertView * alertView = [[UIAlertView alloc] initWithTitle: @ Messaggio "Error Input": @ "Nome utente e / o password non possono essere vuoti." delegate: nil cancelButtonTitle: @ "OK" otherButtonTitles: nil]; [show di alertView]; 

Abbiamo impostato il controller di visualizzazione come delegato della vista di avviso, il che significa che è necessario implementare il UIAlertViewDelegate protocollo. Dai un'occhiata all'implementazione di alertView: clickedButtonAtIndex: mostrato sotto.

 -(void) alertView: (UIAlertView *) alertView clickedButtonAtIndex: (NSInteger) buttonIndex switch (buttonIndex) caso 0: break; caso 1: [self createAccount]; rompere; default: break; 

Nel Creare un profilo, noi sfruttiamo il SSKeychain classe per memorizzare in modo sicuro il nome utente e la password scelti dall'utente. Allora chiamiamo performSegueWithIdentifier: mittente:.

 - (void) createAccount BOOL result = [SSKeychain setPassword: self.passwordTextField.text forService: @ account "MyPhotos": self.usernameTextField.text]; if (result) [self performSegueWithIdentifier: @ "photosViewController" mittente: nil]; 

Nel prepareForSegue: mittente:, otteniamo un riferimento al MTPhotosViewController esempio, impostalo nome utente proprietà con il valore del usernameTextField, e resettare il passwordTextField.

 - (vuoto) prepareForSegue: (UIStoryboardSegue *) segue mittente: (id) mittente MTPhotosViewController * photosViewController = segue.destinationViewController; photosViewController.username = self.usernameTextField.text; self.passwordTextField.text = nil; 

Passaggio 7: implementazione MTPhotosCollectionViewCell

Aperto MTPhotosCollectionViewCell.h e dichiara uno sbocco chiamato ImageView di tipo UIImageView.

 #importare  @interface MTPhotoCollectionViewCell: UICollectionViewCell @property (weak, nonatomic) IBOutlet UIImageView * imageView; @fine

Apri lo storyboard principale e aggiungi un UIImageView istanza alla cellula prototipo del MTPhotosViewController esempio. Seleziona la cella prototipo (non la vista dell'immagine) e imposta la sua classe su MTPhotosCollectionViewCell nel Identity Inspector sulla destra. Con la cella prototipo ancora selezionata, apri il Ispettore degli attributi e impostare l'identificatore su Fotocellula.

Passaggio 8: implementazione MTPhotosViewController

Inizia importando i file di intestazione necessari in MTPhotosViewController.m come mostrato di seguito. Dobbiamo anche dichiarare due proprietà, fotografie per memorizzare la serie di foto verrà visualizzata la vista raccolta e percorso del file per mantenere un riferimento al percorso del file. Potresti aver notato che il MTPhotosViewController classe conforme al UIActionSheetDelegate, UINavigationControllerDelegate, e UIImagePickerControllerDelegate protocolli.

 #import "MTPhotosViewController.h" #import  #import "RNDecryptor.h" #import "RNEncryptor.h" #import "MTPhotoCollectionViewCell.h" @interface MTPhotosViewController ()  @property (strong, nonatomic) NSMutableArray * foto; @property (copy, nonatomic) NSString * filePath; @fine

Ho anche implementato un metodo di comodità o di supporto, setupUserDirectory, per creare e impostare le directory necessarie in cui archiviamo i dati dell'utente. Nel prepareData, l'applicazione decrittografa le immagini che sono memorizzate nella directory sicura dell'utente. Dai un'occhiata alle loro implementazioni qui sotto.

 - (void) setupUserDirectory NSArray * paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES); NSString * documents = [percorsi objectAtIndex: 0]; self.filePath = [documenti stringByAppendingPathComponent: self.username]; NSFileManager * fileManager = [NSFileManager defaultManager]; if ([fileManager fileExistsAtPath: self.filePath]) NSLog (@ "Directory già presente.");  else NSError * error = nil; [fileManager createDirectoryAtPath: self.filePath withIntermediateDirectories: YES attributes: nil error: & error]; if (error) NSLog (@ "Impossibile creare la directory per l'utente."); 
 - (void) prepareData self.photos = [[NSMutableArray alloc] init]; NSFileManager * fileManager = [NSFileManager defaultManager]; NSError * error = nil; NSArray * contents = [fileManager contentsOfDirectoryAtPath: self.filePath error: & error]; if ([content count] &&! error) NSLog (@ "Contenuto della directory dell'utente.% @", contenuto); for (NSString * fileName nel contenuto) if ([fileName rangeOfString: @ ". securedData"]. length> 0) NSData * data = [NSData dataWithContentsOfFile: [self.filePath stringByAppendingPathComponent: fileName]]; NSData * decryptedData = [RNDecryptor decryptData: data withSettings: kRNCryptorAES256Settings password: @ "A_SECRET_PASSWORD" errore: nil]; UIImage * image = [UIImage imageWithData: decryptedData]; [self.photos addObject: image];  else NSLog (@ "Questo file non è protetto.");  else if (! [content count]) if (error) NSLog (@ "Impossibile leggere il contenuto della directory dell'utente.");  else NSLog (@ "La directory dell'utente è vuota."); 

Richiama entrambi i metodi nel controller della vista viewDidLoad metodo come mostrato di seguito.

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

L'elemento del pulsante della barra nella barra di navigazione del controller della vista mostra un foglio di azione che consente all'utente di scegliere tra la fotocamera del dispositivo e la libreria di foto.

 - (IBAction) foto: (id) mittente UIActionSheet * actionSheet = [[UIActionSheet alloc] initWithTitle: @ "Seleziona origine" delegato: self cancelButtonTitle: @ "Annulla" distructiveButtonTitle: nil otherButtonTitles: @ "Camera", @ "Photo Library" , nil]; [actionSheet showFromBarButtonItem: sender animated: YES]; 

Implementiamo actionSheet: clickedButtonAtIndex: del UIActionSheetDelegate protocollo.

 - (void) actionSheet: (UIActionSheet *) actionSheet clickkedButtonAtIndex: (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];  

Per gestire la selezione dell'utente nel controller del selettore di immagini, dobbiamo implementare imagePickerController: didFinishPickingMediaWithInfo: del UIImagePickerControllerDelegate protocollo come mostrato di seguito. L'immagine è criptata usando encryptData del RNEncryptor biblioteca. L'immagine è anche aggiunta al fotografie array e la vista raccolta viene ricaricata.

 - (void) imagePickerController: (UIImagePickerController *) picker didFinishPickingMediaWithInfo: (NSDictionary *) informazioni UIImage * image = [info objectForKey: UIImagePickerControllerEditedImage]; if (! image) [info objectForKey: UIImagePickerControllerOriginalImage];  NSData * imageData = UIImagePNGRepresentation (image); NSString * imageName = [NSString stringWithFormat: @ "image-% d.securedData", self.photos.count + 1]; NSData * encryptedImage = [RNEncryptor encryptData: imageData withSettings: kRNCryptorAES256Settings password: @ "A_SECRET_PASSWORD" errore: nil]; [encryptedImage writeToFile: [self.filePath stringByAppendingPathComponent: imageName] atomically: YES]; [self.photos addObject: image]; [self.collectionView reloadData]; [picker dismissViewControllerAnimated: SÌ completamento: nil]; 

Prima di poter creare ed eseguire l'applicazione, è necessario implementare il UICollectionViewDataSource protocollo come mostrato di seguito.

 - (NSInteger) collectionView: (UICollectionView *) collectionView numberOfItemsInSection: (NSInteger) section return self.photos? self.photos.count: 0; 
 - (UICollectionViewCell *) collectionView: (UICollectionView *) collectionView cellForItemAtIndexPath: (NSIndexPath *) indexPath MTPhotoCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier: @ "PhotoCell" forIndexPath: indexPath]; cell.imageView.image = [self.photos objectAtIndex: indexPath.row]; cella di ritorno; 

Passaggio 9: gestione degli stati dell'applicazione

Se l'applicazione va in secondo piano, l'utente deve essere disconnesso. Questo è importante dal punto di vista della sicurezza. Per fare ciò, il delegato dell'applicazione deve avere un riferimento al controller di navigazione in modo che possa essere inserito nel controller della vista radice dello stack di navigazione. Inizia dichiarando una proprietà chiamata navigationController nel MTAppDelegate.h.

 #importare  @interface MTAppDelegate: UIResponder  @property (strong, nonatomic) finestra UIWindow *; @property (strong, nonatomic) UINavigationController * navigationController; @fine

Nel controller della vista viewDidLoad metodo, impostiamo il delegato dell'applicazione navigationController proprietà come mostrato di seguito. Tieni presente che questo è solo un modo per gestirlo.

Ho impostato la proprietà sopra in ViewController di viewDidLoad metodo come mostrato di seguito.

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

Nel delegato dell'applicazione, è necessario aggiornare applicationWillResignActive: come mostrato di seguito. E 'così semplice. Il risultato è che l'utente è disconnesso ogni volta che l'applicazione perde l'attenzione. Proteggerà le immagini dell'utente memorizzate nell'applicazione da occhi indiscreti. Lo svantaggio è che l'utente deve accedere quando l'applicazione diventa di nuovo attiva.

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

Passaggio 10: Build and Run

Costruisci il progetto ed esegui l'applicazione per metterla alla prova.


Conclusione

In questo tutorial, hai imparato come utilizzare l'API di Keychain Services per archiviare dati sensibili e hai anche imparato a crittografare i dati delle immagini su iOS. Lascia un commento nei commenti qui sotto se hai domande o commenti.