Ya sea que esté creando una aplicación móvil o un servicio web, mantener la seguridad de los datos confidenciales es importante y la seguridad se ha convertido en un aspecto esencial de cada producto de software. En este tutorial, le mostraré cómo almacenar de forma segura las credenciales de los usuarios utilizando el llavero de la aplicación y analizaremos el cifrado y descifrado de los datos de los usuarios mediante una biblioteca de terceros..
En este tutorial, le enseñaré cómo proteger datos confidenciales en la plataforma iOS. Los datos confidenciales pueden ser las credenciales de la cuenta de un usuario o los detalles de la tarjeta de crédito. El tipo de datos no es tan importante. En este tutorial, usaremos el llavero de iOS y el cifrado simétrico para almacenar de forma segura los datos del usuario. Antes de entrar en los detalles esenciales, me gustaría darles una visión general de lo que vamos a hacer en este tutorial..
Aunque este tutorial se centra en iOS, los conceptos y técnicas también se pueden usar en OS X.En iOS y OS X, un llavero es un contenedor encriptado para almacenar contraseñas y otros datos que necesitan ser protegidos. En OS X, es posible limitar el acceso de llavero a usuarios o aplicaciones particulares. Sin embargo, en iOS, cada aplicación tiene su propio llavero al que solo tiene acceso la aplicación. Esto garantiza que los datos almacenados en el llavero sean seguros y no accesibles por terceros..
Tenga en cuenta que el llavero solo debe utilizarse para almacenar pequeños fragmentos de datos, como contraseñas. Con este artículo, espero convencerlo del valor de usar el llavero en iOS y OS X en lugar de, por ejemplo, la base de datos predeterminada de usuario de la aplicación, que almacena sus datos en texto plano sin ningún tipo de seguridad..
En iOS, una aplicación puede usar el llavero a través de API de servicios de llavero. La API proporciona una serie de funciones para manipular los datos almacenados en el llavero de la aplicación. Echa un vistazo a las funciones disponibles en iOS.
SecItemAdd
Esta función se utiliza para agregar un elemento al llavero de la aplicación.SecItemCopyMatching
Utiliza esta función para encontrar un elemento de llavero de propiedad de la aplicación.SecItemDelete
Como su nombre lo indica, esta función se puede utilizar para eliminar un elemento del llavero de la aplicación..SecItemUpdate
Utilice esta función si necesita actualizar un elemento en el llavero de la aplicación.los API de servicios de llavero es una API de C, pero espero que no te impida usarla. Cada una de las funciones anteriores acepta un diccionario (CFDictionaryRef
), que contiene un par de clave-valor de clase de elemento y pares de clave-valor de atributo opcionales. El significado exacto y el propósito de cada uno quedarán claros una vez que comencemos a usar la API en un ejemplo.
Cuando se habla de cifrado, generalmente se conocen dos tipos de cifrado., simétrico y asimétrico cifrado El cifrado simétrico, por un lado, utiliza una clave compartida para cifrar y descifrar datos. El cifrado asimétrico, por otro lado, utiliza una clave para cifrar datos y otra clave separada, pero relacionada, para descifrar datos.
En este tutorial, aprovecharemos la Marco de seguridad Disponible en iOS para cifrar y descifrar datos. Este proceso tiene lugar bajo el capó, por lo que no interactuaremos directamente con este marco. Usaremos cifrado simétrico en nuestra aplicación de ejemplo.
los Marco de seguridad ofrece una serie de otros servicios, como los servicios de aleatorización para generar números aleatorios, certificados, claves y servicios de confianza criptográficamente seguros para administrar certificados, claves públicas y privadas, y políticas de confianza. los Marco de seguridad es un marco de bajo nivel disponible en iOS y OS X con API basadas en C.
En este tutorial, le mostraré cómo puede utilizar la API de Keychain Services y el cifrado simétrico en una aplicación de iOS. Crearemos una pequeña aplicación que almacene de forma segura las fotos tomadas por el usuario..
En este proyecto, usaremos Sam Soffes SSKeychain, un contenedor de Objective-C para interactuar con la API de Keychain Services. Para el cifrado y descifrado, utilizaremos RNCryptor, una biblioteca de cifrado de terceros..
La biblioteca RNCryptor es una buena opción para cifrar y descifrar datos. El proyecto es utilizado por muchos desarrolladores y mantenido activamente por sus creadores. La biblioteca ofrece una API de Objective-C fácil de usar. Si está familiarizado con Cocoa y Objective-C, lo encontrará fácil de usar. Las principales características de la biblioteca se enumeran a continuación..
Antes de comenzar a crear la aplicación, permítame mostrarle cómo se verá el flujo típico de la aplicación.
Arranque Xcode y cree un nuevo proyecto seleccionando Solicitud de vista única plantilla de la lista de plantillas.
Nombra el proyecto Fotos seguras y establecer Familia de dispositivos al iPhone. Dile a Xcode dónde quieres guardar el proyecto y pulsa Crear.
El siguiente paso es vincular el proyecto con el Seguridad y Servicios básicos móviles marcos Seleccione el proyecto en el Navegador de proyectos a la izquierda, elige el primer objetivo llamado Fotos seguras, y abre el Construir fases pestaña en la parte superior. Ampliar la Enlace Binario Con Bibliotecas cajón y vincular el proyecto contra el Seguridad y Servicios básicos móviles marcos.
Como mencioné anteriormente, usaremos la biblioteca SSKeychain y la biblioteca RNCryptor. Descargue estas dependencias y agréguelas al proyecto. Asegúrese de copiar los archivos a su proyecto y agregarlos a la Fotos seguras objetivo como se muestra en la captura de pantalla a continuación.
Mostraremos las fotos del usuario en una vista de colección, lo que significa que necesitamos subclases UICollectionViewController
tanto como UICollectionViewCell
. Seleccionar Nuevo> Archivo ... desde el Expediente menú, crear una subclase de UICollectionViewController
, y nombrarlo MTPhotosViewController
. Repita este paso una vez más para MTPhotoCollectionViewCell
, que es una subclase de UICollectionViewCell
.
Abra el guión gráfico principal del proyecto y actualícelo como se muestra en la siguiente captura de pantalla. El guión gráfico contiene dos controladores de vista, una instancia de MTViewController
, que contiene dos campos de texto y un botón, y una instancia de MTPhotosViewController
. los MTViewController
la instancia está integrada en un controlador de navegación.
También necesitamos crear un segue desde el MTViewController
instancia a la MTPhotosViewController
ejemplo. Establezca el identificador de segue en photosViewController
. los MTPhotosViewController
la instancia también debe contener un elemento de botón de barra como se muestra en la captura de pantalla a continuación.
Para hacer todo este trabajo, necesitamos actualizar la interfaz de MTViewController
Como se muestra abajo. Declaramos una salida para cada campo de texto y una acción que se activa con el botón. Realiza las conexiones necesarias en el storyboard principal del proyecto..
#importar@interface MTViewController: UIViewController @property (débil, no atómico) IBOutlet UITextField * usernameTextField; @ propiedad (débil, no atómica) IBOutlet UITextField * passwordTextField; - (IBAction) inicio de sesión: (id) remitente; @fin
En el MTPhotosViewController
clase, declarar una propiedad nombrada nombre de usuario
para almacenar el nombre de usuario del usuario que ha iniciado sesión y declarar una acción para el elemento del botón de la barra. No olvides conectar la acción con el elemento del botón de la barra en el guión gráfico principal.
#importar@interface MTPhotosViewController: UICollectionViewController @property (copy, nonatomic) NSString * username; - Fotos (IBAction): (id) remitente; @fin
MTViewController
En MTViewController.m
, añadir una declaración de importación para el MTPhotosViewController
clase, la Llavero SS
clase, y la MTAppDelegate
clase. También conformamos el MTViewController
clase a la UIAlertViewDelegate
protocolo.
# importación "MTViewController.h" # importación "SSKeychain.h" # importación "MTAppDelegate.h" # importación "MTPhotosViewController.h" @interface MTViewController ()@fin
El siguiente paso es implementar el iniciar sesión:
Acción que declaramos anteriormente. Primero verificamos si el usuario ya ha creado una cuenta buscando la contraseña de la cuenta. Si esto es cierto, usamos el llavero de la aplicación para ver si la contraseña ingresada por el usuario coincide con la almacenada en el llavero. Los métodos proporcionados por el Llavero SS La biblioteca facilita la lectura y manipulación de los datos almacenados en el llavero de la aplicación.
- (IBAction) inicio de sesión: (id) remitente if (self.usernameTextField.text.length> 0 && self.passwordTextField.text.length> 0) NSString * password = [SSKeychain passwordForService: @ "MyPhotos" cuenta: self.usernameTextField .texto]; if (password.length> 0) if ([self.passwordTextField.text isEqualToString: password]) [self performSegueWithIdentifier: @ "photosViewController" sender: nil]; else UIAlertView * alertView = [[UIAlertView alloc] initWithTitle: @ Mensaje de "Error de inicio de sesión": @ "Combinación de nombre de usuario / contraseña no válida." delegate: nil cancelButtonTitle: @ "OK" otherButtonTitles: nil]; [alertView show]; else else UIAlertView * alertView = [[UIAlertView alloc] initWithTitle: @ Mensaje de "Nueva cuenta": @ "¿Desea crear una cuenta?" delegate: self cancelButtonTitle: @ "Cancel" otherButtonTitles: @ "OK", nil]; [alertView show]; else else UIAlertView * alertView = [[UIAlertView alloc] initWithTitle: @ Mensaje de "Entrada de error": @ "El nombre de usuario y / o la contraseña no pueden estar vacíos". delegate: nil cancelButtonTitle: @ "OK" otherButtonTitles: nil]; [alertView show];
Hemos establecido el controlador de vista como el delegado de la vista de alerta, lo que significa que necesitamos implementar el UIAlertViewDelegate
protocolo. Eche un vistazo a la implementación de alertView: clickedButtonAtIndex:
mostrado a continuación.
-alertView (void): (UIAlertView *) alertView clickedButtonAtIndex: (NSInteger) buttonIndex switch (buttonIndex) case 0: break; caso 1: [self createAccount]; descanso; por defecto: break;
En crear una cuenta
, aprovechamos el Llavero SS
Clase para almacenar de forma segura el nombre de usuario y la contraseña elegidos por el usuario. Entonces llamamos performSegueWithIdentifier: remitente:
.
- (void) createAccount BOOL result = [SSKeychain setPassword: self.passwordTextField.text forService: @ cuenta de "MyPhotos": self.usernameTextField.text]; if (result) [self performSegueWithIdentifier: @ "photosViewController" sender: nil];
En prepareForSegue: remitente:
, obtenemos una referencia a la MTPhotosViewController
instancia, establecer su nombre de usuario
propiedad con el valor de la usernameTextField
, y restablecer el passwordTextField
.
- (void) prepareForSegue: (UIStoryboardSegue *) segue sender: (id) sender MTPhotosViewController * photosViewController = segue.destinationViewController; photosViewController.username = self.usernameTextField.text; self.passwordTextField.text = nil;
MTPhotosCollectionViewCell
Abierto MTPhotosCollectionViewCell.h y declarar una salida llamada imageView
de tipo UIImageView
.
#importar@interface MTPhotoCollectionViewCell: UICollectionViewCell @property (débil, no atómico) IBOutlet UIImageView * imageView; @fin
Abre el guión gráfico principal y añade un UIImageView
instancia de la célula prototipo de la MTPhotosViewController
ejemplo. Seleccione la celda prototipo (no la vista de imagen) y establezca su clase en MTPhotosCollectionViewCell
en el Inspector de identidad a la derecha. Con la celda prototipo aún seleccionada, abre el Inspector de atributos y establece el identificador a Célula fotoeléctrica
.
MTPhotosViewController
Comience importando los archivos de encabezado necesarios en MTPhotosViewController.m Como se muestra abajo. También tenemos que declarar dos propiedades., fotos
para almacenar el conjunto de fotos, se mostrará la vista de colección y ruta de archivo
para mantener una referencia a la ruta del archivo. Usted puede haber notado que el MTPhotosViewController
clase se ajusta a la UIActionSheetDelegate
, UINavigationControllerDelegate
, y UIImagePickerControllerDelegate
protocolos.
#import "MTPhotosViewController.h" #import#importar "RNDecryptor.h" #importar "RNEncryptor.h" #importar "MTPhotoCollectionViewCell.h" @interface MTPhotosViewController () @property (strong, nonatomic) NSMutableArray * photos; @property (copy, nonatomic) NSString * filePath; @fin
También he implementado un método de conveniencia o de ayuda., setupUserDirectory
, para crear y configurar los directorios necesarios en los que almacenaremos los datos del usuario. En prepareData
, La aplicación descifra las imágenes que están almacenadas en el directorio seguro del usuario. Echa un vistazo a sus implementaciones a continuación..
- (void) setupUserDirectory NSArray * paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES); NSString * documents = [rutas objectAtIndex: 0]; self.filePath = [documents stringByAppendingPathComponent: self.username]; NSFileManager * fileManager = [Administrador predeterminado de NSFileManager]; if ([fileManager fileExistsAtPath: self.filePath]) NSLog (@ "Directorio ya presente."); else NSError * error = nil; [fileManager createDirectoryAtPath: self.filePath withIntermediateDirectories: YES atributos: nil error: & error]; if (error) NSLog (@ "No se puede crear el directorio para el usuario.");
- (void) prepareData self.photos = [[NSMutableArray alloc] init]; NSFileManager * fileManager = [Administrador predeterminado de NSFileManager]; NSError * error = nil; NSArray * contents = [fileManager contentsOfDirectoryAtPath: self.filePath error: & error]; if ([conteo de contenidos] &&! error) NSLog (@ "Contenido del directorio del usuario.% @", contenidos); para (NSString * fileName en el contenido) if ([fileName rangeOfString: @ ". securedData"]. length> 0) NSData * data = [NSData dataWithContentsOfFile: [self.filePath stringByAppendingPathComponent: fileName]]; NSData * decryptedData = [RNDecryptor decryptData: datos withSettings: kRNCryptorAES256Configuración de contraseña: @ "A_SECRET_PASSWORD" error: nil]; UIImage * image = [UIImage imageWithData: decryptedData]; [self.photos addObject: image]; else NSLog (@ "Este archivo no está protegido."); else else if (! [conteo de contenidos]) if (error) NSLog (@ "No se puede leer el contenido del directorio del usuario"); else NSLog (@ "El directorio del usuario está vacío.");
Invoque ambos métodos en la vista del controlador. viewDidLoad
método como se muestra a continuación.
- (void) viewDidLoad [super viewDidLoad]; [auto setupUserDirectory]; [self prepareData];
El elemento del botón de la barra en la barra de navegación del controlador de vista muestra una hoja de acción que permite al usuario elegir entre la cámara del dispositivo y la biblioteca de fotos.
- Photos (IBAction): (id) sender UIActionSheet * actionSheet = [[UIActionSheet alloc] initWithTitle: @ Delegado "Select Source": self cancelButtonTitle: @ "Cancel" destructiveButtonTitle: n otherButtonTitles: @ "Camera", , nil]; [actionSheet showFromBarButtonItem: sender animated: YES];
Vamos a implementar actionSheet: clickedButtonAtIndex:
del UIActionSheetDelegate
protocolo.
- (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];
Para manejar la selección del usuario en el controlador de selección de imágenes, necesitamos implementar imagePickerController: didFinishPickingMediaWithInfo:
del UIImagePickerControllerDelegate
protocolo como se muestra a continuación. La imagen se encripta utilizando encryptData
del Crncryptor
biblioteca. La imagen también se agrega a la fotos
matriz y la vista de colección se vuelve a cargar.
- (void) imagePickerController: (UIImagePickerController *) picker didFinishPickingMediaWithInfo: (NSDictionary *) info UIImage * image = [info objectForKey: UIImagePickerControllerEditedImage]; if (! image) [info objectForKey: UIImagePickerControllerOriginalImage]; NSData * imageData = UIImagePNGRepresentation (imagen); NSString * imageName = [NSString stringWithFormat: @ "image-% d.securedData", self.photos.count + 1]; NSData * encryptedImage = [RNEncryptor encryptData: imageData withSettings: kRNCryptorAES256Configuración de la contraseña: @ "A_SECRET_PASSWORD" error: nil]; [encryptedImage writeToFile: [self.filePath stringByAppendingPathComponent: imageName] atómicamente: YES]; [self.photos addObject: image]; [self.collectionView reloadData]; [picker dismissViewControllerAnimated: YES complete: nil];
Antes de poder generar y ejecutar la aplicación, necesitamos implementar el UICollectionViewDataSource
protocolo como se muestra a continuación.
- (NSInteger) collectionView: (UICollectionView *) collectionView numberOfItemsInSection: (NSInteger) sección return self.photos? self.photos.count: 0;
- (UICollectionViewCell *) collectionView: (UICollectionView *) collectionView cellForItemAtIndexPath: (NSIndexPath *) indexPath MTPhotoCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier: @ "PhotoCell" para el índice cell.imageView.image = [self.photos objectAtIndex: indexPath.row]; celda de retorno
Si la aplicación pasa al fondo, el usuario debe cerrar sesión. Esto es importante desde una perspectiva de seguridad. Para lograr esto, el delegado de la aplicación debe tener una referencia al controlador de navegación para que pueda aparecer en el controlador de vista raíz de la pila de navegación. Comience por declarar una propiedad nombrada control de navegación
en MTAppDelegate.h.
#importar@interface MTAppDelegate: UIResponder @property (strong, nonatomic) ventana UIWindow *; @property (strong, nonatomic) UINavigationController * navigationController; @fin
En el controlador de vista viewDidLoad
Método, configuramos la aplicación del delegado. control de navegación
propiedad como se muestra a continuación. Tenga en cuenta que esta es solo una forma de manejar esto.
He puesto la propiedad anterior en ViewController es
viewDidLoad
método como se muestra a continuación.
- (void) viewDidLoad [super viewDidLoad]; MTAppDelegate * applicationDeleagte = (MTAppDelegate *) [[UIApplication sharedApplication] delegate]; [applicationDeleagte setNavigationController: self.navigationController];
En la aplicación delegada, necesitamos actualizar applicationWillResignActive:
Como se muestra abajo. Es tan simple como eso. El resultado es que el usuario cierra la sesión cada vez que la aplicación pierde el enfoque. Protegerá las imágenes del usuario almacenadas en la aplicación de miradas indiscretas. El inconveniente es que el usuario debe iniciar sesión cuando la aplicación vuelva a estar activa..
- application (void) applicationWillResignActive: (UIApplication *) application [self.navigationController popToRootViewControllerAnimated: NO];
Construye el proyecto y ejecuta la aplicación para ponerlo a prueba..
En este tutorial, aprendió a usar la API de Keychain Services para almacenar datos confidenciales y también aprendió a cifrar datos de imagen en iOS. Deje un comentario en los comentarios a continuación si tiene alguna pregunta o comentario.