مساحة اعلانية

آخر المواضيع

تأمين وتشفير البيانات على دائرة الرقابة الداخلية


سواء كنت تقوم بإنشاء أحد تطبيقات الجوال أو خدمة ويب , احفظ البيانات الحساسة الهامة لأن الأمن جانب أساسي لكل منتجات البرنامج . فى هذا البرنامج التعليمي , سوف أظهر لك كيفية تخزين بيانات اعتماد المستخدم بإستخدام مفاتيح التطبيق الأمنة وسوف نلقي نظرة على التشفير وفك التشفير لبيانات المستخدم بإستخدام مكتبة خارجية . 

مقدمة : 
فى هذا البرنامج التعليمي سنتعلم كيفية تأمين البيانات الحساسة على منصة iOS . يمكن أن تعتمد البيانات الحساسة على حساب المستخدم أو تفاصيل بطاقة الائتمان .
نوع البيانات ليست مهمه . فى هذا البرنامج التعليمي سوف نستخدم سلسلة مفاتيح دائرة الرقابة الداخلية iOS'S keychain والتشفير المتناظر لتخزين بيانات المستخدم بشكل أمن قبل أن ندخل فى التفاصيل الجوهرية , أود أن أقدم لك عرضا عاما على ما نحن ذاهبون للقيام به فى هذا البرنامج التعليمي . 

على الرغم من أن هذا البرنامج التعليمي يركز على دائرة الرقابة الداخلية iOS, يمكن أيضا إستخدام كل من المفاهيم والتقنيات علي OS X . 
دائرة الرقابة الداخلية وسلسلة المفاتيح IOS و OS X :

دائرة الرقابة الداخلية ونظام التشغيل العاشر OS X هو مفتاح الحاوية المشفرة لتخزين كلمات المرور والبيانات الأخري التي يجب أن تكون أمنة فى نظام التشغيل العاشر . من الممكن تقييد وصول المفاتيح إلي مستخدمين معينين أو تطبيق معين فى IOS ومع ذلك, كل تطبيق له سلسلة مفاتيح خاصة به. التي فقط يمكن الوصول اليها . وهذا يضمن أن البيانات المخزنة فى سلسلة المفاتيح الخاصه هى التي فقط يمكن الوصول إليها . وهذا يعنى أن البيانات المخزنة فى سلسلة المفاتيح أمنة من أي طرف ثالث . 

ضع فى أعتبارك ان سلسلة المفاتيح , يجب استخدامها فقط لتخزين قطع صغيرة من البيانات, مثل كلمات المرور. كما فى هذه المقالة , أأمل فى أقناعكم بقيمة أستخدام سلسلة المفاتيح فى IOS ونظام التشغيل العاشر Keychain و OS X على سبيل المثال, قاعدة بيانات تطبيقات المستخدمين العادية. والتي تقوم بتخزين البيانات كنص عادي دون أى شكل من أشكال الأمان . 

فى IOS , يمكن للتطبيق إستخدام KEYCHAIN عن طريق API سلسلة مفاتيح API توفر عددا من الوظائف للتعامل مع البيانات المخزنة فى سلسلة المفاتيح الخاصة بالتطبيق . لنلقي نظرة على الوظائف المتوفرة فى IOS 

SecItemAdd هذه الدالة تستخدم لإضافة عنصر إلى سلسلة المفاتيح الخاصة بالتطبيق . 
SecitemCopyMatching أنت تستخدم هذا الدالة لإيجاد عناصر المملوكة للتطبيق .
SecItemDelete هذه الدالة كما هو مبين من إسمها تقوم بمسح اى عنصر من سلسلة مفاتيح التطبيق 
SecItemUpdate تستخدم هذه الدالة إذا كنت تريد تحديث اى عنصر فى سلسلة مفاتيح التطبيق 

- سلسلة مفاتيح API هي C API لكن أمل ان هذا لا يمنعك من أستخدامها , لانه يقبل كل الوظائف المذكورة أعلاه CFDictionaryRef يحتوي على item class key-value pair و يحتوي على عنصرattribute key-value pairs , والغرض من كل هذا أنها سوف تصبح واضحة حالما نبدأ بإستخدام API فى مثال.
التشفير وفك التشفير :

عند مناقشة فك التشفير , تسمع عموما عن نوعين من التشفير, تشفير متماثل وتشفير غير متماثل, التشفير المتماثل من ناحية يستخدم مفتاح مشترك واحد لتشفير وفك تشفير البيانات . من ناحية أخري يستخدم التشفير غير المتناظر, مفتاح واحد لتشفير البيانات ومفتاح ذات صلة أخر لتشفير البيانات . 

فى هذا البرنامج التعليمي , سوف نستفيد من إطار الامن المتاح فى IOS لتشفير وفك تشفير البيانات. تجري هذه العملية تحت غطاء حيث أنه لن يكون التفاعل مباشرة مع هذا الإطار . سوف نستخدم تشفير مماثل فى تطبيق المثال لدينا . 

إطار الأمن يوفر عددا من الخدمات الأخري مثل تأمين خدمات التوزيع العشوائي لتوليد التشفير , أرقام عشوائية,شهادات , والخدمات الإستئمانية لإدارة الشهادات والمفاتيح الخاصة والعامة, ونهج ثقة إطار الإمن ذات المستوى المنخفض المتوفر فى IOS ونظام التشغيل OS X مع واجهة برمجة التطبيقات C-based APIs . 
نظرة عامة حول التطبيق :

فى هذا البرنامج التعليمي سوف أظهر لك كيفية إستخدام خدمة 
API لسلسلة المفاتيح والتشفير المتماثل فى تطبيق IOS, سوف نقوم بإنشاء تطبيق صغير يقوم بتخزين الصور التي تم إلتقاطها من قبل المستخدم بشكل أمن . 

فى هذا المشروع, سوف نستخدم SamSoffes SSkeychain , و Objective-C للتفاعل مع خدمة API لسلسلة المفاتيح ل التشفير وفك التشفير , سوف نستخدم RNCryptor مكتبة تشفير الطرف الثالث . 
تشفير البيانات بإستخدام RNCryptor :

مكتبة RNCryptor خيار جيد لتشفير وفك تشفير البيانات . التطبيق مستخدم من قبل العديد من المطورين ويتم تحديثه بإستمرار بواسطة منشئية . المكتبة توفر سهولة إستخدام Objective-C API .إذا كنت معتادا على Cocoa و Objective-C سوف تجد انه سهل الإستخدام . فيما يلي السمات الرئيسية للمكتبة . 
  • تشفير AES-256 
  • خاصية cbc 
  • كلمة المرور التي تمتد مع PBKDF2 
  • كلمة مرور التلميح 
  • IV عشوائي
  • التجزئة ثم التشفير Encrypt-then-Hash HMAC 
تدفق التطبيق :

قبل أن نبدأ فى بناء التطبيق, إسمحوا لي أن اظهر لك نموذج ما سيبدو علية تدفق التطبيق.
  • عندما يقوم المستخدم بتشغيل التطبيق . سيظهر لها تسجيل الدخول .
  • إذا كانت لا تمتلك حسابا حتي الأن أوراقها سوف تضاف إلي سلسلة المفاتيح كي تقوم بتسجيل الدخول .
  • إذا كان لدية حساب بالفعل , ولكن يقوم بإدخال باسوورد خاطيء سوف تظهر له رسالة خطأ بأنه أدخل كلمة مرور غير صحيحة .
  • فى حالة سجل الدخول , يسمح لها بالوصول إلي الصور التى أتخذت بالتطبيق تم تأمين الصور من قبل التطبيق . 
  • فى كل مرة يأخذ صورة من كاميرا الجهاز او يلتقط صورة من مكتبة الصور الخاصه به. يتم تشفيرها وتخزينها فى دليل المستندات للتطبيق .
  • عندما تقوم بالتغيير إلي تطبيق أخر أو الجهاز تم غلقة يتم تلقائيا تسجيل الخروج .
بناء التطبيق :
الخطوة الأولي : إعداد المشروع 

قم بإطلاق Xcode وأنشىء مشروع جديد عن طريق تحديد طريقة العرض الواحد من قائمة القوالب 

قم بتسمية أسم لمشروع الصور وقم بتعيين الجهاز إلي iPhone . أخبر ال Xcode أين تريد أن يتم تخزين المشروع وأضغط على إنشاء . 

الخطوة الثانية : FrameWorks 

الخطوة التالية لربط المشروع ضد الفرام وورك و خدمة الموبايل Core للخدمات الأساسية المتنقلة . حدد المشروع من متصفح المشروع Project Navigator فى اليسار . قم بأختيار الهدف الأول المسمي تأمين الصور Secure photos , وقم بفتح علامة التبيويب بناء المراحل Build Phases فى الأعلي قم بتوسيع الإرتباط الثنائي, وربط المشروع ضد ال Framework و خدمة Mobile Core .

الخطوة الثالثة : التبعيات 

كما ذكرت مبكرا, سنقوم بإستخدام مكتبة 
SSKeychain, ومكتبة RNCryptor . قم بتحميل هذه التبعيات وإضافتها لإلى المشروع. تأكد من نسخ الملفات إلي المشروع الخاص بك وأضفها إلي تأمين الصور Secure Photos كما هو موضح فى الصور أدناه .
الخطوة الرابعة : إنشاء الفئات 

سوف نعرض صور المستخدم فى مجموعة عروض. مما يعنى أننا بحاجة إلي الفئة الفرعية 
ULCollectionViewController فضلا عن ULCollectionViewCell .قم بأختيار New>File.. من قائمة الملفات,أنشىء الفئة الفرعية ULCollectionViewController, كرر هذه الخطوة مرة أخري .من أجل MTPhotoCollectionViewCell والذي هو فئة فرعية من ULCollectionViewCell .
الخطوة الخامسة : إنشاء واجهة المستخدم 

أفتح قائمة المشاريع الرئيسية وقم بتحديث لوحة العمل كما هو مبين فى الصورة التالية . لوحة العمل تحتوي على اثنين من وحدات العرض ك MTViewController, والذي يحتوي على حقلين من النصوص والأزرار والمثيل MTPhotosViewController. المثيل MTViewController مضمن فى وحدة تحكم التنقل .

نحن أيضا بحاجة إلي إنشاء seque من MTViewController إلي MTPhotosViewController المثيل يجب أن يحتوي ايضا على بار يوجد به أزرار وعناصر كما موضح فى الصورة أدناه .

لنقوم بكل هذا العمل, نحن بحاجة إلي تحديث واجهة MTViewController كما موضح أدناه. ثم نعلن عن outlet لكل حقل نصي وإجراءات حتي يتم تشغيلها بواسطة الزر . قم بإجراء الإتصالات اللازمة من لوحة العمل الرئيسية للمشروع . 
#import <UIKit/UIKit.h>
@interface MTViewController : UIViewController
@property (weak, nonatomic) IBOutlet UITextField *usernameTextField;
@property (weak, nonatomic) IBOutlet UITextField *passwordTextField;
- (IBAction)login:(id)sender;
@end
فى فئة MTPhotosViewController, قم بتعريف أسم المستخدم لتخزين الأسم الخاص بالمستخدم الذي قام بتسجيل الدخول حاليا, وقم بعمل الأجراءات لشريط العناصر البار من لوحة العمل الرئيسية .
#import <UIKit/UIKit.h> @interface MTPhotosViewController : UICollectionViewController @property (copy, nonatomic) NSString *username; - (IBAction)photos:(id)sender; @end
الخطوة السادسة : التنفيذ 

في MTViewController.m قم بإضافة استيراد لكل من فئة MTPhotosViewController , وفئة SSKeychain و MTAppDelegate . نحن أيضا نقوم بتأكيد MTViewController إلي بروتوكول ULAlertViweDelegate . 
#import "MTViewController.h"

#import "SSKeychain.h"
#import "MTAppDelegate.h"
#import "MTPhotosViewController.h"

@interface MTViewController () <UIAlertViewDelegate>

@end

الخطوة التالية :

هي تنفيذ login:action كما أعلنا سابقا . نتحقق أولا ما إذا قام المستخدم بإنشاء حساب ثم قام بجلب كلمة المرور للحساب . إذا كان هذا صحيحا . سوف نستخدم مفاتيح التطبيق keychain application's لنرى إذا كانت كلمة المرور التى قام المستخدم بإدخالها تطابق واحدة من المخزنة فى سلسلة المفاتيح . الأساليب التي تقدمها الفئة SSKeychain نجعل من السهل قراءة والتعامل مع البيانات المخزنه فى سلسلة المفاتيح للتطبيق . 
 (IBAction)login:(id)sender {    if (self.usernameTextField.text.length > 0 && self.passwordTextField.text.length > 0) {        NSString *password = [SSKeychain passwordForService:@"MyPhotos" account:self.usernameTextField.text];         if (password.length > 0) {            if ([self.passwordTextField.text isEqualToString:password]) {                [self performSegueWithIdentifier:@"photosViewController" sender:nil];             } else {                UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"Error Login" message:@"Invalid username/password combination." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];                [alertView show];            }         } else {            UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"New Account" message:@"Do you want to create an account?" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil];            [alertView show];        }     } else {        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error Input" message:@"Username and/or password cannot be empty." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];        [alertView show];    }}
لقد قمنا بظبط المراقب View controller كمندوب وذلك لعرض التنبيهات, والتي تعنى أننا بحاجة إلي بروتوكول ULAertViewDelegate . ألقي نظرة علي تنفيذ alertView:ClickedButtonAtIndex: كما موضح أدناه .
(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{    switch (buttonIndex) {        case 0:            break;        case 1:            [self createAccount];            break;        default:            break;    }}
فى Creat Account , علينا الإستفادة من فئة SSKeychain لتخزين إسم المستخدم وكلمة المرور التي قام بإختيارها المستخدم بشكل أمن, ثم نستدعي performSequeWithIdentifier:sender: . 
(void)createAccount {    BOOL result = [SSKeychain setPassword:self.passwordTextField.text forService:@"MyPhotos" account:self.usernameTextField.text];     if (result) {        [self performSegueWithIdentifier:@"photosViewController" sender:nil];    }}
في prepareForSeque:sender نحصل على  المثيل MTPhotosViewController لتعيين الخاصية (اسم المستخدم) "username " بقيمة ال usernameTextField وإعادة تعيين passwordTextField . 
(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{    MTPhotosViewController *photosViewController = segue.destinationViewController;    photosViewController.username = self.usernameTextField.text;    self.passwordTextField.text = nil;}
الخطوة السابعة : تنفيذ MTPhostosCollectionViewCell

قم بفتح MTPhotosCollectionViewCell.h وقم بإعلان ال imageView من نوع UIImageView.
#import <UIKit/UIKit.h> @interface MTPhotoCollectionViewCell : UICollectionViewCell @property (weak, nonatomic) IBOutlet UIImageView *imageView; @end
أفتح لوحة العمل الرئيسية وأضف المثيل UIImageView إلي الخلية prototype للمثيل MTPhotosViewController . وحدد خلية النموذج الأول (not the image view ) وقم بتعيين الفئة الخاصة به إلي MTPhotosCoolectionViewCell في هوية " Inspector " على اليمين , مع الخلية المحدده التى لا تزال مفتوحة , قم بفتح السمة Inspector وقم بتعيين المعرف إلي PhotoCell ضوئية . 
الخطوة الثامنة : تنفيذ MTPhotosViewController 

أبدا عن طريق استيراد ملفات الرأس اللازمة فى MTPhotosViweController.m كما هو موضح أدناه نحن بحاجة أيضا إلي إعلان أثنين من الخصائص , (1) صور لتخزين مجموعه من الصور التي سيتم عرضها فى طريقة عرض المجموعة (2) filePath للحفاظ على مرجع مسار الملف. ربما لاجظتم أن الفئة MTPhotosViewController تتوافق مع البروتوكولات ULActionSheetDelegate, و ULNavigationControllerDelegate, و  UIImagePickerControllerDelegate . 

#import "MTPhotosViewController.h" #import <MobileCoreServices/MobileCoreServices.h> #import "RNDecryptor.h"#import "RNEncryptor.h"#import "MTPhotoCollectionViewCell.h" @interface MTPhotosViewController () <UIActionSheetDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate> @property (strong, nonatomic) NSMutableArray *photos;@property (copy, nonatomic) NSString *filePath; @end
لقد نفذت أيضا أسلوب المساعدة والراحة , setupUserDirectory, من أجل إنشاء وإعداد الدلائل الضرورية سوف نقوم بتخزين بيانات المستخدم في prepareData, التطبيق يقوم بفك الصور التى تم تخزينها في دليل المستخدم الأمن. ألقي نظره على التطبيقات الخاصة بهم أدناه . 
(void)setupUserDirectory {    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);    NSString *documents = [paths objectAtIndex:0];    self.filePath = [documents stringByAppendingPathComponent:self.username];    NSFileManager *fileManager = [NSFileManager defaultManager];     if ([fileManager fileExistsAtPath:self.filePath]) {        NSLog(@"Directory already present.");     } else {        NSError *error = nil;        [fileManager createDirectoryAtPath:self.filePath withIntermediateDirectories:YES attributes:nil error:&error];         if (error) {            NSLog(@"Unable to create directory for user.");        }    }}

(void)prepareData {
    self.photos = [[NSMutableArray alloc] init];
    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSError *error = nil;
    NSArray *contents = [fileManager contentsOfDirectoryAtPath:self.filePath error:&error];

    if ([contents count] && !error){
        NSLog(@"Contents of the user's directory. %@", contents);

        for (NSString *fileName in contents) {
            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" error:nil];
                UIImage *image = [UIImage imageWithData:decryptedData];
                [self.photos addObject:image];

            } else {
                NSLog(@"This file is not secured.");
            }
        }

    } else if (![contents count]) {
        if (error) {
            NSLog(@"Unable to read the contents of the user's directory.");
        } else {
            NSLog(@"The user's directory is empty.");
        }
    }
}

أستدعي الأسلوبين على حد سواء فى أسلوب عرض المراقب view Controller's viewDidLoad كما هو موضح أدناه .
(void)viewDidLoad {    [super viewDidLoad];     [self setupUserDirectory];    [self prepareData];}
شريط زر الأدوات فى ViewController's يظهر شريط تنقل ورقة العمل التي تسمح للمستخدم بألأختيار بين كاميرا الجهاز ومكتبة الصور . 
 (IBAction)photos:(id)sender {    UIActionSheet *actionSheet = [[UIActionSheet alloc]initWithTitle:@"Select Source" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Camera", @"Photo Library", nil];    [actionSheet showFromBarButtonItem:sender animated:YES];}
دعونا ننفذ actionSheet:clickedButtonAtIndex: من بروتوكول UIActionSheetDelegate . 
(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];    }}
للتعامل مع تحديدات المستخدم فى وحدة تحكم منتقي الصور Image Picker, نحن بحاجة إلى تنفيذ imagePickerController:didFinishPickingMediaWithInfo: من بروتوكول UllmagePickerControllerDelegate كما موضح ادناه . الصورة مشفرة بإستخدام encryptData لمكتبة RNEncryptor . الصورة أيضا تم إضافتها إلى مجموعه من الصور . ويتم تحميل عرض الصور .
(void)imagePickerController:(UIImagePickerController *)picker 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 password:@"A_SECRET_PASSWORD" error:nil];    [encryptedImage writeToFile:[self.filePath stringByAppendingPathComponent:imageName] atomically:YES];    [self.photos addObject:image];    [self.collectionView reloadData];    [picker dismissViewControllerAnimated:YES completion:nil];}
قبل أن تتمكن من إنشاء التطبيق, تحتاج إلى تنفيذ البروتوكول ULCollectionViewDataSource كما موضح أدناه 
(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];    return cell;}
الخطوة التاسعة : التعامل مع تفاصيل التطبيق : 

إذا كان التطبيق يذهب إلي الخلفية, يحتاج المستخدم إلي تسجيل الخروج. هذا مهم من المنظور الأمني لإنجاز هذا يحتاج مندوب التطبيق للإشارة إلي navigation controller حتى تتمكن من عرض وحدة تحكم التنقل , ابدأ بتعريف خاصية navigationController في MTAppDelegate.h. . 
#import <UIKit/UIKit.h> @interface MTAppDelegate : UIResponder <UIApplicationDelegate> @property (strong, nonatomic) UIWindow *window;@property (strong, nonatomic) UINavigationController *navigationController; @end
فى وحدة تحكم عرض المراقب ViewDidLoad . نقوم بتعيين الخاصية navigationController لمندوب التطبيق كما هو موضح أدناه . أن هذا مجرد وسيلة للتعامل مع هذا .

أنا قمت بتعيين الخاصية أعلاة فى الأسلوب ViewController's في viewDidLoad كما موضح أدناه .
(void)viewDidLoad {    [super viewDidLoad];     MTAppDelegate *applicationDeleagte = (MTAppDelegate *)[[UIApplication sharedApplication] delegate];    [applicationDeleagte setNavigationController:self.navigationController];}
لتفويض التطبيق . نحن بحاجة إلي تحديث applicationWillResignActive: كما هو مبين أدناه . إنها بسيطة . والنتيجة أن يتم تسجيل خروج المستخدم عندما يفقد التطبيق التركيز . وسوف يحمي صور المستخدم المخزنة فى التطبيق من أعين المتطفلين, الجانب السلبي أن المستخدم يحتاج إلي تسجيل الدخول عندما يصبح التطبيق نشطا فى المرة القادمة .
- (void)applicationWillResignActive:(UIApplication *)application {    [self.navigationController popToRootViewControllerAnimated:NO];}
الخطوة العاشرة : البناء والتشغيل 
أنشىء المشروع ثم قم بتشغيل التطبيق لوضعه على طريق السير .

الإستنتاج 

فى هذا البرنامج التعليمي, يمكنك تعلم كيفية إستخدام API لخدمة سلسلة المفاتيح KeyChainServices وذلك لتخزين البيانات الحساسة وتعلمت أيضا كيفية تشفير بيانات الصورة فى iOS. أترك تعليقك فى قسم التعليقات بالأسفل فى حالة وجود أى اسئلة أو استفسارات .

الكــاتــب

    • مشاركة

ليست هناك تعليقات:

جميع الحقوق محفوظة لــ الشبح للمعلوميات 2019 ©