Sécuriser et chiffrer des données sur iOS

Que vous créiez une application mobile ou un service Web, la sécurité des données confidentielles est importante et la sécurité est devenue un aspect essentiel de chaque logiciel. Dans ce didacticiel, je vais vous montrer comment stocker en toute sécurité les informations d'identification de l'utilisateur à l'aide du trousseau de l'application. Nous verrons également comment chiffrer et déchiffrer les données de l'utilisateur à l'aide d'une bibliothèque tierce..


introduction

Dans ce tutoriel, je vais vous apprendre à sécuriser des données sensibles sur la plate-forme iOS. Les données sensibles peuvent être les informations d'identification du compte de l'utilisateur ou les détails de la carte de crédit. Le type de données n'est pas si important. Dans ce didacticiel, nous utiliserons le chiffrement symétrique et le chiffrement symétrique d'iOS pour stocker en toute sécurité les données de l'utilisateur. Avant d'entrer dans les détails, j'aimerais vous donner un aperçu de ce que nous allons faire dans ce tutoriel..

Bien que ce tutoriel se concentre sur iOS, les concepts et techniques peuvent également être utilisés sur OS X.

Porte-clés iOS

Sur iOS et OS X, un trousseau est un conteneur chiffré permettant de stocker les mots de passe et autres données à sécuriser. Sur OS X, il est possible de limiter l’accès au trousseau à des utilisateurs ou à des applications particuliers. Sur iOS, cependant, chaque application a son propre trousseau auquel seule l'application a accès. Cela garantit que les données stockées dans le trousseau sont sécurisées et inaccessibles par des tiers.

N'oubliez pas que le trousseau ne doit être utilisé que pour stocker de petites données, telles que des mots de passe. Avec cet article, j'espère vous convaincre de l'intérêt d'utiliser le trousseau sur iOS et OS X au lieu, par exemple, de la base de données par défaut de l'application, qui stocke ses données en texte brut sans aucune forme de sécurité..

Sur iOS, une application peut utiliser le trousseau via le API de services de trousseau. L'API fournit un certain nombre de fonctions pour manipuler les données stockées dans le trousseau de l'application. Jetez un coup d'œil aux fonctions disponibles sur iOS.

  • SecItemAdd Cette fonction est utilisée pour ajouter un élément au trousseau de l'application..
  • SecItemCopyMatching Vous utilisez cette fonction pour trouver un élément de trousseau appartenant à l'application.
  • SecItemDelete Comme son nom l'indique, cette fonction peut être utilisée pour supprimer un élément du trousseau de l'application..
  • SecItemUpdate Utilisez cette fonction si vous devez mettre à jour un élément du trousseau de l'application..

le API de services de trousseau est une API C, mais j'espère que cela ne vous empêchera pas de l'utiliser. Chacune des fonctions ci-dessus accepte un dictionnaire (CFDictionaryRef), qui contient une paire clé-valeur de classe d'élément et des paires attribut clé-valeur facultatives. Le sens exact et le but de chacun deviendront clairs une fois que nous commencerons à utiliser l'API dans un exemple.


Cryptage et décryptage

Lorsque vous parlez de chiffrement, vous entendez généralement parler de deux types de chiffrement., symétrique et asymétrique cryptage. Le chiffrement symétrique, d’une part, utilise une clé partagée pour chiffrer et déchiffrer les données. Le chiffrement asymétrique, quant à lui, utilise une clé pour chiffrer les données et une autre clé distincte, mais associée, pour le déchiffrement des données..

Dans ce tutoriel, nous allons tirer parti de la Cadre de sécurité disponible sur iOS pour chiffrer et déchiffrer les données. Ce processus se déroule sous le capot afin que nous n'interagissions pas directement avec ce cadre. Nous allons utiliser le cryptage symétrique dans notre exemple d'application.

le Cadre de sécurité propose un certain nombre d'autres services, tels que des services de randomisation pour la génération de nombres aléatoires sécurisés de manière cryptographique, des services de certificat, de clé et de confiance pour la gestion de certificats, des clés publiques et privées et des stratégies de confiance. le Cadre de sécurité est un framework de bas niveau disponible à la fois sur iOS et OS X avec des API basées sur C.


Aperçu de l'application

Dans ce didacticiel, je vais vous montrer comment utiliser l'API Keychain Services et le chiffrement symétrique dans une application iOS. Nous allons créer une petite application qui stocke de manière sécurisée les photos prises par l'utilisateur.

Dans ce projet, nous allons utiliser Sam Soffes SSKeychain, un wrapper Objective-C pour l’interaction avec l’API des services de trousseau. Pour le cryptage et le décryptage, nous utiliserons RNCryptor, une bibliothèque de cryptage tierce.


Cryptage de données avec RNCryptor

La bibliothèque RNCryptor est un bon choix pour chiffrer et déchiffrer des données. Le projet est utilisé par de nombreux développeurs et activement maintenu par ses créateurs. La bibliothèque offre une API Objective-C facile à utiliser. Si vous connaissez Cocoa et Objective-C, vous le trouverez facile à utiliser. Les principales caractéristiques de la bibliothèque sont énumérées ci-dessous..

  • Chiffrement AES-256
  • Mode CBC
  • Étirement du mot de passe avec PBKDF2
  • Salage de mot de passe
  • IV aléatoire
  • Chiffrer puis hacher HMAC

Flux d'application

Avant de commencer à créer l'application, laissez-moi vous montrer à quoi ressemblera le flux typique de l'application..

  • Lorsque l'utilisateur lance l'application, il se présente en vue de se connecter..
  • Si elle n'a pas encore créé de compte, ses informations d'identification sont ajoutées au trousseau et elle est connectée..
  • Si elle a un compte mais entre un mot de passe incorrect, un message d'erreur s'affiche.
  • Une fois qu'elle est connectée, elle a accès aux photos qu'elle a prises avec l'application. Les photos sont stockées de manière sécurisée par l'application.
  • Chaque fois qu'elle prend une photo avec l'appareil photo de l'appareil ou prend une photo de sa photothèque, la photo est cryptée et stockée dans l'application. Les documents annuaire.
  • Chaque fois qu'elle passe à une autre application ou que l'appareil est verrouillé, elle est automatiquement déconnectée..

Construire l'application

Étape 1: Configuration du projet

Lancez Xcode et créez un nouveau projet en sélectionnant le Application à vue unique modèle de la liste des modèles.


Nommez le projet Photos sécurisées Et mettre Famille de périphérique sur iPhone. Indiquez à Xcode où vous souhaitez enregistrer le projet et appuyez sur Créer.


Étape 2: Cadres

La prochaine étape consiste à relier le projet à la Sécurité et Services de base mobiles cadres. Sélectionnez le projet dans le Navigateur de projet à gauche, choisissez la première cible nommée Photos sécurisées, et ouvrez le Phases de construction onglet en haut. Étendre le Lien binaire avec des bibliothèques tiroir et lier le projet contre la Sécurité et Services de base mobiles cadres.


Étape 3: dépendances

Comme je l'ai mentionné plus tôt, nous utiliserons la bibliothèque SSKeychain et la bibliothèque RNCryptor. Téléchargez ces dépendances et ajoutez-les au projet. Assurez-vous de copier les fichiers dans votre projet et de les ajouter à la liste. Photos sécurisées cible comme indiqué dans la capture d'écran ci-dessous.


Étape 4: Création de classes

Nous allons afficher les photos de l'utilisateur dans une vue de collection, ce qui signifie que nous devons sous-classer UICollectionViewController aussi bien que UICollectionViewCell. Sélectionner Nouveau> Fichier… du Fichier menu, créer une sous-classe de UICollectionViewController, et nommez-le MTPhotosViewController. Répétez cette étape une fois de plus pour MTPhotoCollectionViewCell, qui est une sous-classe de UICollectionViewCell.

Étape 5: Création de l'interface utilisateur

Ouvrez le scénario principal du projet et mettez-le à jour, comme indiqué dans la capture d'écran ci-dessous. Le storyboard contient deux contrôleurs de vue, une instance de MTViewController, qui contient deux champs de texte et un bouton, et une instance de MTPhotosViewController. le MTViewController l'instance est intégrée dans un contrôleur de navigation.

Nous devons également créer une séparation entre le MTViewController par exemple au MTPhotosViewController exemple. Définissez l'identifiant de la séquence sur photosViewController. le MTPhotosViewController instance doit également contenir un élément de bouton de barre comme indiqué dans la capture d'écran ci-dessous.


Pour que tout cela fonctionne, nous devons mettre à jour l'interface de MTViewController comme indiqué ci-dessous. Nous déclarons une sortie pour chaque champ de texte et une action déclenchée par le bouton. Établissez les connexions nécessaires dans le scénarimage principal du projet.

 #importation  @interface MTViewController: UIViewController @property (faible, non atomique) IBOutlet UITextField * usernameTextField; @property (faible, non atomique) IBOutlet UITextField * passwordTextField; - login (IBAction): expéditeur (id); @fin

dans le MTPhotosViewController classe, déclarer une propriété nommée Nom d'utilisateur pour stocker le nom d'utilisateur de l'utilisateur actuellement connecté et déclarer une action pour l'élément de bouton de barre. N'oubliez pas de connecter l'action avec le bouton de barre dans le scénario principal.

 #importation  @interface MTPhotosViewController: UICollectionViewController @property (copy, nonatomic) NSString * nom d'utilisateur; - photos (IBAction): expéditeur (id); @fin

Étape 6: Mise en œuvre MTViewController

Dans MTViewController.m, ajouter une déclaration d'importation pour le MTPhotosViewController classe, la SSKeychain classe et MTAppDelegate classe. Nous conformons aussi le MTViewController classe à la UIAlertViewDelegate protocole.

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

La prochaine étape est la mise en œuvre de la s'identifier: action que nous avons déclarée plus tôt. Nous vérifions d’abord si l’utilisateur a déjà créé un compte en récupérant le mot de passe du compte. Si cela est vrai, nous utilisons le trousseau de l'application pour voir si le mot de passe entré par l'utilisateur correspond à celui stocké dans le trousseau. Les méthodes fournies par le SSKeychain bibliothèque facilite la lecture et la manipulation des données stockées dans le trousseau de l'application.

 - (IBAction) login: (id) expéditeur if (self.usernameTextField.text.length> 0 && self.passwordTextField.text.length> 0) NSString * password = [SSKeychain passwordForService: @ "MonPhotos" compte: self.usernameTextField .texte]; if (password.length> 0) if ([self.passwordTextField.text isEqualToString: mot de passe]) [self performSegueWithIdentifier: @ "photosViewController" expéditeur: nil];  else UIAlertView * alertView = [[UIAlertView alloc] initWithTitle: @ message "Erreur de connexion": @ "Combinaison de nom d'utilisateur / mot de passe invalide." délégué: nil cancelButtonTitle: @ "OK" otherButtonTitles: nil]; [alertView show];  else UIAlertView * alertView = [[UIAlertView alloc] initWithTitle: @ message "Nouveau compte": @ "Souhaitez-vous créer un compte?" délégué: auto annulerButtonTitle: @ "Annuler" otherButtonTitles: @ "OK", nil]; [alertView show];  else UIAlertView * alertView = [[UIAlertView alloc] initWithTitle: @ message "Erreur de saisie": @ "Le nom d'utilisateur et / ou le mot de passe ne peuvent pas être vides." délégué: nil cancelButtonTitle: @ "OK" otherButtonTitles: nil]; [alertView show]; 

Nous avons défini le contrôleur de vue comme délégué de la vue d’alerte, ce qui signifie que nous devons implémenter le UIAlertViewDelegate protocole. Jetez un coup d’œil à la mise en œuvre de alertView: clickedButtonAtIndex: indiqué ci-dessous.

 -(void) alertView: (UIAlertView *) alertView clickedButtonAtIndex: (NSInteger) buttonIndex switch (buttonIndex) cas 0: pause; cas 1: [self createAccount]; Pause; défaut: break; 

Dans créer un compte, nous tirons parti de la SSKeychain classe pour stocker en toute sécurité le nom d'utilisateur et le mot de passe choisis par l'utilisateur. Nous appelons ensuite performSegueWithIdentifier: expéditeur:.

 - (void) createAccount BOOL result = [SSKeychain setPassword: self.passwordTextField.text forService: @ "MonPhotos" compte: self.usernameTextField.text]; if (result) [self performSegueWithIdentifier: @ "photosViewController" expéditeur: nil]; 

Dans prepareForSegue: expéditeur:, nous obtenons une référence à la MTPhotosViewController par exemple, définissez son Nom d'utilisateur propriété avec la valeur de la nomutilisateurTextField, et réinitialiser le passwordTextField.

 - (void) prepareForSegue: (UIStoryboardSegue *) expéditeur du segue: (id) expéditeur MTPhotosViewController * photosViewController = segue.destinationViewController; photosViewController.username = self.usernameTextField.text; self.passwordTextField.text = nil; 

Étape 7: Mise en œuvre MTPhotosCollectionViewCell

Ouvrir MTPhotosCollectionViewCell.h et déclarer un point de vente nommé imageView de type UIImageView.

 #importation  @interface MTPhotoCollectionViewCell: UICollectionViewCell @property (faible, non atomique) IBOutlet UIImageView * imageView; @fin

Ouvrez le storyboard principal et ajoutez un UIImageView par exemple à la cellule prototype de la MTPhotosViewController exemple. Sélectionnez la cellule prototype (pas la vue de l'image) et définissez sa classe sur MTPhotosCollectionViewCell dans le Inspecteur d'identité sur la droite. La cellule prototype étant toujours sélectionnée, ouvrez le Inspecteur d'attributs et définissez l'identifiant sur Photocellule.

Étape 8: Mise en œuvre MTPhotosViewController

Commencez par importer les fichiers d’en-tête nécessaires dans MTPhotosViewController.m comme indiqué ci-dessous. Nous devons également déclarer deux propriétés, Photos pour stocker le tableau de photos, la vue Collection affichera et chemin du fichier conserver une référence au chemin du fichier. Vous avez peut-être remarqué que le MTPhotosViewController classe conforme à la UIActionSheetDelegate, UINavigationControllerDelegate, et UIImagePickerControllerDelegate les protocoles.

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

J'ai également implémenté une méthode de commodité ou d'assistance, setupUserDirectory, pour créer et configurer les répertoires nécessaires dans lesquels nous allons stocker les données de l'utilisateur. Dans prepareData, l'application décrypte les images stockées dans le répertoire sécurisé de l'utilisateur. Regardez leurs implémentations ci-dessous.

 - (void) setupUserDirectory NSArray * path = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES); NSString * documents = [chemins objectAtIndex: 0]; self.filePath = [documents stringByAppendingPathComponent: self.username]; NSFileManager * fileManager = [NSFileManager defaultManager]; if ([fileManager fileExistsAtPath: self.filePath]) NSLog (@ "Répertoire déjà présent.");  else NSError * error = nil; [fileManager createDirectoryAtPath: self.filePath withIntermediateDirectories: YES attributs: aucune erreur: & erreur]; if (erreur) NSLog (@ "Impossible de créer un répertoire pour l'utilisateur."); 
 - (void) prepareData self.photos = [[NSMutableArray alloc] init]; NSFileManager * fileManager = [NSFileManager defaultManager]; NSError * error = nil; NSArray * contents = [fileManager contentsOfDirectoryAtPath: erreur self.filePath: & error]; if ([content count] &&! error) NSLog (@ "Contenu du répertoire de l'utilisateur.% @", contenu); for (NSString * nom_fichier dans le contenu) if ([nomFichier rangeOfString: @ ". securedData"]. length> 0) NSData * data = [NSData dataWithContentsOfFile: [self.filePath stringByAppendingPathComponent: fileName]]; NSData * decryptedData = [RNDecryptor decryptData: données avec paramètres: kRNCryptorAES256Settings mot de passe: @ erreur "A_SECRET_PASSWORD": nil]; UIImage * image = [UIImage imageWithData: decryptedData]; [self.photos addObject: image];  else NSLog (@ "Ce fichier n'est pas sécurisé.");  else if (! [nombre de contenus]) if (erreur) NSLog (@ "Impossible de lire le contenu du répertoire de l'utilisateur.");  else NSLog (@ "Le répertoire de l'utilisateur est vide."); 

Invoquer les deux méthodes dans le contrôleur de vue viewDidLoad méthode comme indiqué ci-dessous.

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

L'élément de bouton de barre dans la barre de navigation du contrôleur de vue affiche une feuille d'action permettant à l'utilisateur de choisir entre la caméra du périphérique et la photothèque..

 - (IBAction) photos: expéditeur (id) UIActionSheet * actionSheet = [[UIActionSheet alloc]] initWithTitle: @ délégué "Select Source": self cancelButtonTitle: @ "Cancel", destructifButtonTitle: nil otherButtonTitle: @ "Camera", @ "Photothèque". , nul]; [actionSheet showFromBarButtonItem: expéditeur animé: OUI]; 

Mettons en œuvre actionSheet: clickedButtonAtIndex: du UIActionSheetDelegate protocole.

 - (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];  

Pour gérer la sélection de l'utilisateur dans le contrôleur du sélecteur d'images, nous devons implémenter imagePickerController: didFinishPickingMediaWithInfo: du UIImagePickerControllerDelegate protocole comme indiqué ci-dessous. L'image est cryptée à l'aide de encryptData du RNEncryptor bibliothèque. L'image est également ajoutée à la Photos tableau et la vue de la collection est rechargée.

 - (void) imagePickerController: (UIImagePickerController *) sélecteur didFinishPickingMediaWithInfo: (NSDictionary *) info 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 mot de passe: @ "A_SECRET_PASSWORD" erreur: nil]; [encryptedImage writeToFile: [self.filePath stringByAppendingPathComponent: imageName] atomiquement: YES]; [self.photos addObject: image]; [self.collectionView reloadData]; [picker licencié ViewControllerAnimated: YES complétion: nil]; 

Avant de pouvoir créer et exécuter l’application, nous devons implémenter la UICollectionViewDataSource protocole comme indiqué ci-dessous.

 - (NSInteger) collectionView: (UICollectionView *) collectionView number numberOfItemsInSection: (NSInteger) section return self.photos? self.photos.count: 0; 
 - (UICollectionViewCell *) collectionView: (UICollectionView *) collectionView cellForItemAtIndexPath: (NSIndexPath *) indexPath MTPhotoCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIeuse.dentifier: @ "PhotoCell" pourIndexPath cell.imageView.image = [self.photos objectAtIndex: indexPath.row]; cellule de retour; 

Étape 9: Traitement des états d'application

Si l'application passe en arrière-plan, l'utilisateur doit être déconnecté. Ceci est important du point de vue de la sécurité. Pour ce faire, le délégué de l'application doit disposer d'une référence au contrôleur de navigation afin qu'il puisse apparaître dans le contrôleur de vue racine de la pile de navigation. Commencez par déclarer une propriété nommée navigationController dans MTAppDelegate.h.

 #importation  @interface MTAppDelegate: UIResponder  @property (strong, nonatomic) UIWindow * fenêtre; @property (strong, nonatomic) UINavigationController * navigationController; @fin

Dans le contrôleur de vue viewDidLoad méthode, nous définissons le délégué de l'application navigationController propriété comme indiqué ci-dessous. Gardez à l’esprit que ce n’est qu’un moyen de gérer cela..

J'ai défini la propriété ci-dessus dans ViewController viewDidLoad méthode comme indiqué ci-dessous.

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

Dans le délégué d'application, nous devons mettre à jour applicationWillResignActive: comme indiqué ci-dessous. C'est aussi simple que ça. Le résultat est que l'utilisateur est déconnecté chaque fois que l'application perd le focus. Cela protégera les images de l'utilisateur stockées dans l'application des regards indiscrets. L'inconvénient est que l'utilisateur doit se connecter lorsque l'application redevient active..

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

Étape 10: Construire et exécuter

Construisez le projet et exécutez l'application pour le mettre à l'épreuve.


Conclusion

Dans ce didacticiel, vous avez appris à utiliser l'API Keychain Services pour stocker des données sensibles et à chiffrer des données d'image sur iOS. Laissez un commentaire dans les commentaires ci-dessous si vous avez des questions ou des commentaires.