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.
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.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.
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.
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.
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.
Prima di iniziare a costruire l'applicazione, lascia che ti mostri come sarà il flusso tipico dell'applicazione.
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.
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.
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.
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
.
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
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;
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
.
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;
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];
Costruisci il progetto ed esegui l'applicazione per metterla alla prova.
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.