34、iOS安全开发:构建钥匙串应用与数据加解密

iOS安全开发:构建钥匙串应用与数据加解密

1. 准备根证书

在开始构建应用之前,需要将证书转换为DER格式,因为安全框架期望的是DER格式的根证书,而非证书助手创建的PEM格式。具体操作步骤如下:
1. 打开终端(Terminal.app)。
2. 切换到 ~/Library/Application Support/Certificate Authority/<CA Name> 目录。
3. 执行以下命令:

openssl x509 -in <PEM file> −inform PEM -out root.der -outform DER

<PEM file> 替换为实际的文件名。

2. 创建钥匙串应用

使用Xcode创建一个新的项目,选择“Tabbed Application”模板,项目命名为“KeychainViewer”,并启用故事板(storyboards)和自动引用计数(Automatic Reference Counting)。具体步骤如下:
1. 打开Xcode,创建新项目。
2. 选择“Tabbed Application”模板。
3. 命名项目为“KeychainViewer”,启用故事板和自动引用计数。

3. 添加安全框架

项目创建完成后,需要添加安全框架(Security.framework)。操作步骤如下:
1. 在项目导航器中选择项目。
2. 在项目编辑器中选择“Keychain Viewer”目标,导航到“Build Phases”选项卡。
3. 展开“Link Binary with Libraries (3 Items)”部分,添加“Security.framework”。
4. 将“Security.framework”移动到“Frameworks”组,清理项目导航器。

4. 删除不必要的文件

替换 FirstViewController SecondViewController 的实现,删除相关文件。操作步骤如下:
1. 选择 FirstViewController.h FirstViewController.m SecondViewController.h SecondViewController.m 文件。
2. 选择“Move to Trash”删除文件。

5. 定义钥匙串项类

钥匙串API是C语言API,为了方便访问,将其封装在Objective-C类中。创建一个名为 KeychainItem 的新Objective-C类,作为 NSObject 的子类。操作步骤如下:
1. 在“Keychain Viewer”组中创建新的Objective-C类,命名为 KeychainItem
2. 选择 KeychainItem.h ,添加安全框架头文件的导入指令:

#import <Security/Security.h>
  1. KeychainItem.h 中添加以下属性:
@property (strong, nonatomic) id type;
@property (strong, nonatomic) id item;
@property (strong, nonatomic) NSDictionary *attributes;
@property (strong, nonatomic) id persistentRef;
  1. 添加初始化方法:
- (id)initWithItem:(CFTypeRef)item;
- (id)initWithData:(NSData *)data options:(NSDictionary *)options;
  1. 实现保存方法和获取属性值的方法:
- (BOOL)save:(NSError **)error;
- (id)valueForAttribute:(CFTypeRef)attr;
6. 实现 KeychainItem 类的方法

KeychainItem.m 中实现上述方法。具体代码如下:

- (id)initWithItem:(CFTypeRef)item
{
    self = [self init];
    if (self) {
        self.item = CFBridgingRelease(item);
    }
    return self;
}

- (id)initWithData:(NSData *)data options:(NSDictionary *)options
{
    return nil;
}

- (BOOL)save:(NSError **)error
{
    NSDictionary *attributes = @{
        (__bridge id)kSecValueRef : self.item,
        (__bridge id)kSecReturnPersistentRef : (id)kCFBooleanTrue
    };
    CFTypeRef cfPersistentRef;
    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)attributes, &cfPersistentRef);

    if (status != errSecSuccess) {
        NSDictionary *userInfo = nil;
        switch (status) {
            case errSecParam:
                userInfo = @{ NSLocalizedDescriptionKey : NSLocalizedString(@"errorSecParam",  
                                  @"One or more parameters passed to the function were not valid.") };
                break;
            case errSecAllocate:
                userInfo = @{ NSLocalizedDescriptionKey : NSLocalizedString(@"errSecAllocate", 
                                  @"Failed to allocate memory.") };
                break;
            case errSecDuplicateItem:
                userInfo = @{ NSLocalizedDescriptionKey : NSLocalizedString(@"errSecDuplicateItem", 
                                  @"The item already exists.") };
                break;
        }
        if (*error)
            *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:userInfo];
        return NO;
    }

    self.persistentRef = CFBridgingRelease(cfPersistentRef);
    return YES;
}

- (id)valueForAttribute:(CFTypeRef)attr
{
    return [self.attributes valueForKey:(__bridge id)attr];
}
7. 实现具体的子类

安全框架中定义了四种钥匙串项类型,这里只处理证书(Certificates)和身份(Identities)两种类型,因此需要定义两个具体的子类: KeychainCertificate KeychainIdentity
- KeychainCertificate

- (id)init
{
    self = [super init];
    if (self) {
        self.type = [(__bridge id)kSecClassCertificate copy];
    }
    return self;
}

- (id)initWithData:(NSData *)data options:(NSDictionary *)options
{
    SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)data);
    if (cert) {
        self = [self initWithItem:cert];
    }
    return self;
}
  • KeychainIdentity
- (id)init
{
    self = [super init];
    if (self) {
        self.type = [(__bridge id)kSecClassIdentity copy];
    }
    return self;
}

- (id)initWithData:(NSData *)data options:(NSDictionary *)options
{
    CFDataRef inPKCS12Data = (__bridge CFDataRef)data;
    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    OSStatus status = SecPKCS12Import(inPKCS12Data, (__bridge CFDictionaryRef)options, &items);
    if (status != errSecSuccess) {
        NSLog(@"Error Reading P12 file");
        abort();
    }
    CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
    SecIdentityRef identity =  
        (SecIdentityRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
    if (identity)
        self = [self initWithItem:identity];

    return self;
}
8. 定义视图控制器类

创建一个名为 KeychainItemsViewController 的抽象基类视图控制器,作为 UITableViewController 的子类。操作步骤如下:
1. 创建新的Objective-C类文件,命名为 KeychainItemsViewController
2. 在 KeychainItemsViewController.h 中添加以下属性:

@property (strong, nonatomic) NSArray *items;
  1. KeychainItemsViewController.m 中导入 KeychainItem.h 头文件,并更新表格视图数据源方法:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.items.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"KeychainItemCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier 
                                                            forIndexPath:indexPath];

    KeychainItem *item = self.items[indexPath.row];
    cell.textLabel.text = [item valueForAttribute:kSecAttrLabel];

    return cell;
}
  1. 创建具体的子类 CertificatesViewController IdentitiesViewController
9. 更新故事板

打开 MainStoryboard.storyboard ,删除原有的视图控制器,添加新的表格视图控制器,并进行相关设置。操作步骤如下:
1. 删除“First View Controller”和“Second View Controller”。
2. 拖动一个 UITableViewController 到编辑器面板。
3. 从标签栏控制器(Tab Bar Controller)控制拖动到表格视图控制器,选择“View Controllers”关系 segue。
4. 将表格视图控制器的标题重命名为“Identities”,使用“first.png”图像。
5. 将表格视图控制器的类从 UITableViewController 更改为 IdentitiesViewController
6. 选择表格视图单元格,将样式从“Custom”更改为“Basic”,设置标识符为 KeychainItemCell ,将附件更改为“Disclosure Indicator”。
7. 重复上述步骤,添加另一个表格视图控制器,标题重命名为“Certificates”,使用“second.png”图像,类更改为 CertificatesViewController

10. 加载钥匙串项

CertificatesViewController.m IdentitiesViewController.m 中更新 viewDidLoad 方法,加载所有的证书和身份。操作步骤如下:
- CertificatesViewController.m

#import "KeychainCertificate.h"

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.items = [KeychainCertificate allKeychainCertificates];
}
  • IdentitiesViewController.m
#import "KeychainIdentity.h"

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.items = [KeychainIdentity allKeychainIdentities];
}
  • 实现 allKeychainCertificates allKeychainIdentities 类方法:
+ (NSArray *)allKeychainCertificates
{
    NSMutableArray *certs = [NSMutableArray array];
    NSDictionary *query = @{
        (__bridge id)kSecClass               : (__bridge id)kSecClassCertificate,
        (__bridge id)kSecReturnRef           : (id)kCFBooleanTrue,
        (__bridge id)kSecReturnAttributes    : (id)kCFBooleanTrue,
        (__bridge id)kSecReturnPersistentRef : (id)kCFBooleanTrue,
        (__bridge id)kSecMatchLimit          : (__bridge id)kSecMatchLimitAll
    };
    CFTypeRef results = NULL;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &results);
    if (status == errSecSuccess && results != NULL) {
        for (NSDictionary *result in (__bridge NSArray *)results) {
            id itemRef = [result valueForKey:(__bridge id)kSecValueRef];
            id persistentRef = [result valueForKey:(__bridge id)kSecValuePersistentRef];
            NSMutableDictionary *attrs = [NSMutableDictionary dictionaryWithDictionary:result];
            [attrs removeObjectForKey:(__bridge id)kSecValueRef];
            [attrs removeObjectForKey:(__bridge id)kSecValuePersistentRef];
            KeychainCertificate *cert =  
                [[KeychainCertificate alloc] initWithItem:(__bridge CFTypeRef)itemRef];
            cert.persistentRef = persistentRef;
            cert.attributes = attrs;
            [certs addObject:cert];
        }
    }
    return certs;
}

+ (NSArray *)allKeychainIdentities
{
    NSMutableArray *idents = [NSMutableArray array];
    NSDictionary *query = @{
        (__bridge id)kSecClass               : (__bridge id)kSecClassIdentity,
        (__bridge id)kSecReturnRef           : (id)kCFBooleanTrue,
        (__bridge id)kSecReturnAttributes    : (id)kCFBooleanTrue,
        (__bridge id)kSecReturnPersistentRef : (id)kCFBooleanTrue,
        (__bridge id)kSecMatchLimit          : (__bridge id)kSecMatchLimitAll
    };
    CFTypeRef results = NULL;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &results);
    if (status == errSecSuccess && results != NULL) {
        for (NSDictionary *result in (__bridge NSArray *)results) {
            id itemRef = [result valueForKey:(__bridge id)kSecValueRef];
            id persistentRef = [result valueForKey:(__bridge id)kSecValuePersistentRef];
            NSMutableDictionary *attrs = [NSMutableDictionary dictionaryWithDictionary:result];
            [attrs removeObjectForKey:(__bridge id)kSecValueRef];
            [attrs removeObjectForKey:(__bridge id)kSecValuePersistentRef];
            KeychainIdentity *ident =  
                [[KeychainIdentity alloc] initWithItem:(__bridge CFTypeRef)itemRef];
            ident.persistentRef = persistentRef;
            ident.attributes = attrs;
            [idents addObject:ident];
        }
    }
    return idents;
}
11. 添加证书和身份文件

将之前创建的 root.cer cert.p12 文件添加到项目的“Supporting Files”组中,并确保勾选“Copy items to destination group’s folder (if needed)”复选框。

12. 加载钥匙串项

AppDelegate.m 中实现加载证书和身份的方法。操作步骤如下:

#import "KeychainCertificate.h"
#import "KeychainIdentity.h"

@interface AppDelegate ()
- (BOOL)isAnchorCertLoaded;
- (void)addAnchorCertificate;
- (void)addIdentity;
@end

- (BOOL)application:(UIApplication *)application 
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    if ([self isAnchorCertLoaded]) {
        [self addIdentity];
        [self addAnchorCertificate];
    }

    return YES;
}

- (BOOL)isAnchorCertLoaded
{
    return ([[NSUserDefaults standardUserDefaults] valueForKey:@"anchor_certificate"] == nil);
}

- (void)addAnchorCertificate
{
    NSString *rootCertificatePath = [[NSBundle mainBundle] pathForResource:@"root" ofType:@"der"];
    NSData *data = [[NSData alloc] initWithContentsOfFile:rootCertificatePath];
    KeychainCertificate *cert = [[KeychainCertificate alloc] initWithData:data options:nil];
    if (cert) {
        NSError *error;
        BOOL saved = [cert save:&error];
        if (!saved) {
            NSLog(@"Error Saving Certificate: %@", [error localizedDescription]);
            abort();
        }
        NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
        [userDefaults setObject:cert.persistentRef forKey:@"anchor_certificate"];
        [userDefaults synchronize];
    }
}

- (void)addIdentity
{
    NSString *identityPath = [[NSBundle mainBundle] pathForResource:@"cert" ofType:@"p12"];
    NSData *data = [[NSData alloc] initWithContentsOfFile:identityPath];
    NSString *password = @"@Super2048Cat";
    KeychainIdentity *ident =  
        [[KeychainIdentity alloc] initWithData:data 
                                       options:@{ (__bridge id)kSecImportExportPassphrase : password }];
    if (ident) {
        NSError *error;
        BOOL saved = [ident save:&error];
        if (!saved) {
            NSLog(@"Error Saving Identity: %@", [error localizedDescription]);
            abort();
        }
    }
}
13. 添加详细视图

创建一个名为 KeychainItemViewController 的新Objective-C类,作为 UIViewController 的子类,用于显示钥匙串项的详细信息。操作步骤如下:
1. 在 KeychainItemViewController.h 中添加以下属性和方法:

@class KeychainItem;

@property (strong, nonatomic) KeychainItem *item;
@property (weak, nonatomic) IBOutlet UITextView *textView;
- (IBAction)done:(id)sender;
  1. KeychainItemViewController.m 中实现 done: 方法:
- (IBAction)done:(id)sender
{
    [self dismissViewControllerAnimated:YES completion:nil];
}
  1. 在故事板中添加 UIViewController 场景,设置背景颜色为黑色,添加 UITextView UIButton ,并进行相关设置。
  2. 使用身份检查器(Identity Inspector)将视图控制器的类更改为 KeychainItemViewController
  3. 控制拖动设置 textView 输出口和 done: 动作。
  4. KeychainItemViewController.m 中实现 viewWillAppear 方法,填充文本视图:
- (void)viewWillAppear:(BOOL)animated
{
    if (self.item) {
        NSMutableString *itemInfo = [NSMutableString string];
        [itemInfo appendFormat:@"AccessGroup: %@\n", 
                               [self.item valueForAttribute:kSecAttrAccessGroup]];
        [itemInfo appendFormat:@"CreationDate: %@\n", 
                               [self.item valueForAttribute:kSecAttrCreationDate]];
        [itemInfo appendFormat:@"CertificateEncoding: %@\n", 
                               [self.item valueForAttribute:kSecAttrCertificateEncoding]];
        [itemInfo appendFormat:@"CreationDate: %@\n", [self.item valueForAttribute:kSecClass]];
        [itemInfo appendFormat:@"Issuer: %@\n", [self.item valueForAttribute:kSecAttrIssuer]];
        [itemInfo appendFormat:@"Label: %@\n", [self.item valueForAttribute:kSecAttrLabel]];
        [itemInfo appendFormat:@"ModificationDate: %@\n", 
                               [self.item valueForAttribute:kSecAttrModificationDate]];
        [itemInfo appendFormat:@"Accessible: %@\n", [self.item valueForAttribute:kSecAttrAccessible]];
        [itemInfo appendFormat:@"PublicKeyHash: %@\n", 
                               [self.item valueForAttribute:kSecAttrPublicKeyHash]];
        [itemInfo appendFormat:@"SerialNumber: %@\n", 
                               [self.item valueForAttribute:kSecAttrSerialNumber]];
        [itemInfo appendFormat:@"Subject: %@\n", [self.item valueForAttribute:kSecAttrSubject]];
        self.textView.text = itemInfo;
    }
}
  1. CertificatesViewController.m IdentitiesViewController.m 中实现 prepareForSegue:sender: 方法,设置钥匙串项:
    - CertificatesViewController.m
#import "KeychainItemViewController.h"

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"CertificateSegue"]) {
        NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
        KeychainCertificate *cert = self.items[indexPath.row];
        KeychainItemViewController *kivc = [segue destinationViewController];
        kivc.item = cert;
    }
}
  • IdentitiesViewController.m
#import "KeychainItemViewController.h"

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"IdentitySegue"]) {
        NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
        KeychainIdentity *ident = self.items[indexPath.row];
        KeychainItemViewController *kivc = [segue destinationViewController];
        kivc.item = ident;
    }
}

以下是整个流程的mermaid流程图:

graph LR
    A[准备根证书] --> B[创建钥匙串应用]
    B --> C[添加安全框架]
    C --> D[删除不必要的文件]
    D --> E[定义钥匙串项类]
    E --> F[实现具体的子类]
    F --> G[定义视图控制器类]
    G --> H[更新故事板]
    H --> I[加载钥匙串项]
    I --> J[添加证书和身份文件]
    J --> K[加载钥匙串项]
    K --> L[添加详细视图]

通过以上步骤,我们完成了一个简单的iOS钥匙串应用的开发,实现了证书和身份的加载、显示和管理。接下来,我们将进一步增强数字身份的功能,实现数据的加密和解密。

iOS安全开发:构建钥匙串应用与数据加解密

14. 增强数字身份功能

为了实现数据的加密和解密,需要在 KeychainIdentity.h 中添加两个方法:

- (NSData *)encrypt:(NSData *)data;
- (NSData *)decrypt:(NSData *)data;

这两个方法分别用于对传入的 NSData 对象进行加密和解密,并返回处理后的 NSData 对象。

同时,为了判断数字身份是否可信任,需要声明一些私有属性和方法:

@interface KeychainIdentity ()
@property (assign, nonatomic, readonly, getter=isTrusted) BOOL trusted;
@property (assign, nonatomic, readonly) SecTrustRef trust;
@property (assign, nonatomic, readonly) SecCertificateRef anchorCertificate;
@property (assign, nonatomic, readonly) SecCertificateRef certificate;
- (BOOL)recoverTrust;
@end

在实现文件中合成这些属性:

@synthesize trusted = _trusted;
@synthesize trust = _trust;
@synthesize anchorCertificate = _anchorCertificate;
@synthesize certificate = _certificate;

更新 init 方法来初始化这些属性:

- (id)init
{
    self = [super init];
    if (self) {
        self.type = [(__bridge id)kSecClassIdentity copy];
        _trusted = NO;
        _trust = NULL;
    }
    return self;
}

实现 dealloc 方法来释放信任和证书引用:

- (void)dealloc
{
    if (_trust)
        CFRelease(_trust);
    if (_certificate)
        CFRelease(_certificate);

    if (_anchorCertificate)
        CFRelease(_anchorCertificate);
}
15. 实现信任相关方法
  • 加载锚证书
- (SecCertificateRef)anchorCertificate
{
    if (_anchorCertificate == NULL) {
        id persistentRef =  
            [[NSUserDefaults standardUserDefaults] objectForKey:@"anchor_certificate"];
        NSDictionary *query = @{
                (__bridge id)kSecClass               : (__bridge id)kSecClassCertificate,
                (__bridge id)kSecValuePersistentRef  : persistentRef,
                (__bridge id)kSecReturnRef           : (id)kCFBooleanTrue,
                (__bridge id)kSecMatchLimit          : (__bridge id)kSecMatchLimitOne
        };
        OSStatus status =  
            SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&_anchorCertificate);
        if (status != errSecSuccess || _anchorCertificate == NULL) {
            NSLog(@"Error loading Anchor Certificate");
            abort();
        }
    }
    return _anchorCertificate;
}
  • 加载数字身份证书
- (SecCertificateRef)certificate
{
    if (_certificate == NULL) {
        OSStatus status =  
            SecIdentityCopyCertificate((__bridge SecIdentityRef)self.item, &_certificate);
        if (status != errSecSuccess) {
            NSLog(@"Error retrieving Identity Certificate");
            return NULL;
        }
    }
    return _certificate;
}
  • 创建信任引用
- (SecTrustRef)trust
{
    if (_trust == NULL) {
        SecPolicyRef policy = SecPolicyCreateBasicX509();
        NSArray *certs = @[ (__bridge id)self.certificate, (__bridge id)self.anchorCertificate ];
        OSStatus status =  
            SecTrustCreateWithCertificates((__bridge CFTypeRef)certs, policy, &_trust);
        if (status != errSecSuccess) {
            NSLog(@"Error Creating Trust from Certificate");
            return NULL;
        }
    }
    return _trust;
}
  • 判断是否可信任
- (BOOL)isTrusted
{
    if (_trust == NULL) {
        SecTrustResultType trustResult;
        OSStatus status = SecTrustEvaluate(self.trust, &trustResult);
        if (status == errSecSuccess) {
            switch (trustResult) {
                case kSecTrustResultInvalid:
                case kSecTrustResultDeny:
                case kSecTrustResultFatalTrustFailure:
                case kSecTrustResultOtherError:
                    _trusted = NO;
                    break;
                case kSecTrustResultProceed:
                case kSecTrustResultConfirm:
                case kSecTrustResultUnspecified:
                    _trusted = YES;
                    break;
                case kSecTrustResultRecoverableTrustFailure:
                    _trusted = [self recoverTrust];
                    break;
            }
        }
        else
            _trusted = NO;
    }
    return _trusted;
}
  • 恢复信任
- (BOOL)recoverTrust
{
    NSArray *anchorCerts = @[ (__bridge id)self.anchorCertificate ];
    SecTrustSetAnchorCertificates(self.trust, (__bridge CFArrayRef)anchorCerts);
    SecTrustSetAnchorCertificatesOnly(self.trust, NO);
    SecTrustResultType trustResult;
    OSStatus status = SecTrustEvaluate(self.trust, &trustResult);
    if (status == errSecSuccess) {
        switch (trustResult) {
            case kSecTrustResultInvalid:
            case kSecTrustResultDeny:
            case kSecTrustResultFatalTrustFailure:
            case kSecTrustResultOtherError:
            case kSecTrustResultRecoverableTrustFailure:
                return NO;
                break;
            case kSecTrustResultProceed:
            case kSecTrustResultConfirm:
            case kSecTrustResultUnspecified:
                return YES;
                break;
        }
    }
    return NO;
}
16. 实现加密和解密方法
  • 加密方法
- (NSData *)encrypt:(NSData *)data
{
    if (!self.isTrusted)
        return nil;

    SecKeyRef publicKey = SecTrustCopyPublicKey(self.trust);
    size_t keyBlockSize = SecKeyGetBlockSize(publicKey);
    size_t bufferSize = keyBlockSize*sizeof(uint8_t);

    uint8_t *srcBuffer = malloc(bufferSize);
    size_t srcBufferLen = keyBlockSize - 11;

    uint8_t *buffer = malloc(bufferSize);
    size_t bufferLen = keyBlockSize;

    NSMutableData *result = [[NSMutableData alloc] init];

    NSRange range = NSMakeRange(0, keyBlockSize);
    while (range.location < data.length) {
        memset(srcBuffer, 0x0, bufferSize);
        memset(buffer, 0x0, bufferSize);

        if (NSMaxRange(range) > data.length)
            range.length = data.length - range.location;

        [data getBytes:srcBuffer range:range];
        OSStatus status =  
            SecKeyEncrypt(publicKey, kSecPaddingPKCS1, srcBuffer, srcBufferLen, buffer, &bufferLen);
        if (status != errSecSuccess) {
            NSLog(@"Error Encrypting Data");
            free(buffer);
            free(srcBuffer);
            free(publicKey);
            return nil;
        }

        [result appendBytes:buffer length:bufferLen];
        range.location += srcBufferLen;
    }

    free(buffer);
    free(srcBuffer);
    free(publicKey);

    return result;
}
  • 解密方法
- (NSData *)decrypt:(NSData *)data
{
    if (!self.isTrusted)
        return nil;

    SecKeyRef privateKey;
    OSStatus status =  SecIdentityCopyPrivateKey((__bridge SecIdentityRef)self.item, &privateKey);
    if (status != errSecSuccess && privateKey != NULL) {
        CFRelease(privateKey);
        privateKey = NULL;
        return nil;
    }

    size_t keyBlockSize = SecKeyGetBlockSize(privateKey);
    size_t bufferSize = keyBlockSize*sizeof(uint8_t);

    uint8_t *srcBuffer = malloc(bufferSize);

    uint8_t *buffer = malloc(bufferSize);
    size_t bufferLen = keyBlockSize;

    NSMutableData *result = [[NSMutableData alloc] init];

    NSRange range = NSMakeRange(0, keyBlockSize);
    while (range.location < data.length) {
        memset(srcBuffer, 0x0, bufferSize);
        memset(buffer, 0x0, bufferSize);

        if (NSMaxRange(range) > data.length)
            range.length = data.length - range.location;

        [data getBytes:srcBuffer range:range];
        OSStatus status =  
            SecKeyDecrypt(privateKey, kSecPaddingPKCS1, srcBuffer, keyBlockSize, buffer, &bufferLen);
        if (status != errSecSuccess) {
            NSLog(@"Error Decrypting Data");
            free(buffer);
            free(srcBuffer);
            free(privateKey);
            return nil;
        }

        [result appendBytes:buffer length:bufferLen];
        range.location += keyBlockSize;
    }

    free(buffer);
    free(srcBuffer);
    free(privateKey);

    return result;
}
17. 创建身份视图控制器

创建一个名为 IdentityViewController KeychainItemViewController 子类,用于处理数字身份的加密和解密操作。
- 在 IdentityViewController.h 中声明按钮的输出口和动作:

@property (weak, nonatomic) IBOutlet UIButton *cryptButton;
- (IBAction)crypt:(id)sender;
  • IdentityViewController.m 中添加私有属性和方法:
@interface IdentityViewController () {
    NSData *_encryptedData;
}
- (void)encrypt;
- (void)decrypt;
@end
  • 实现 crypt: 动作方法:
- (IBAction)crypt:(id)sender
{
    if (_encryptedData)
        [self decrypt];
    else
        [self encrypt];
}
  • 实现 encrypt 方法:
- (void)encrypt
{
    KeychainIdentity *ident = (KeychainIdentity *)self.item;
    NSData *data = [self.textView.text dataUsingEncoding:NSUTF8StringEncoding];
    _encryptedData = [ident encrypt:data];
    if (_encryptedData == nil) {
        NSLog(@"Encryption Failed");
        return;
    }

    self.textView.text = [_encryptedData description];
    [self.cryptButton setTitle:@"Decrypt" forState:UIControlStateNormal];
}
  • 实现 decrypt 方法:
- (void)decrypt
{
    KeychainIdentity *ident = (KeychainIdentity *)self.item;
    NSData *data = [ident decrypt:_encryptedData];
    if (data == nil) {
        NSLog(@"Decryption Failed");
        return;
    }

    NSString *decryptedString = [[NSString alloc] initWithBytes:[data bytes]  
                                                         length:[data length]  
                                                       encoding:NSUTF8StringEncoding];
    _encryptedData = nil;
    self.textView.text = decryptedString;
    [self.cryptButton setTitle:@"Encrypt" forState:UIControlStateNormal];
}
18. 更新故事板以使用新的视图控制器
  • 打开 MainStoryboard.storyboard ,找到 IdentitiesViewController 旁边的 KeychainItemViewController
  • 将其类从 KeychainItemViewController 更改为 IdentityViewController
  • 拖动一个 UIButton 到视图的右下角,使用蓝色指南将其与 Done 按钮对齐,并将标签更改为 Encrypt
  • 控制拖动从视图控制器图标到 Encrypt 按钮,绑定到 cryptButton 输出口。
  • 控制拖动从 Encrypt 按钮到视图控制器图标,绑定到 crypt: 动作。
19. 测试应用

构建并运行应用程序,选择数字身份以打开 IdentityViewController 。点击 Encrypt 按钮对文本进行加密,然后点击 Decrypt 按钮对文本进行解密。

以下是增强数字身份功能部分的mermaid流程图:

graph LR
    A[添加加密和解密方法] --> B[实现信任相关方法]
    B --> C[实现加密方法]
    B --> D[实现解密方法]
    C --> E[创建身份视图控制器]
    D --> E
    E --> F[更新故事板]
    F --> G[测试应用]

通过以上步骤,我们完成了iOS钥匙串应用的开发,并实现了数字身份的数据加密和解密功能,进一步增强了应用的安全性。在实际开发中,可以根据需求对应用进行更多的优化和扩展。

下载前必看:https://pan.quark.cn/s/a4b39357ea24 在当前快节奏的社会背景下,快递代拿服务已演变为日常生活中不可或缺的组成部分。 基于SSM(Spring、SpringMVC、MyBatis)框架的Java快递代拿系统,正是为了迎合这一需求而进行设计和构建的。 接下来将系统性地阐述系统的功能特性、架构布局以及具体的实现步骤。 1. **系统功能**: - **用户模块**:用户具备注册账户、登录验证、提交订单、挑选快递代取服务以及完成线上支付的各项操作。 - **订单模块**:当客户提交订单后,系统将自动生成包含快递种类、取件地点、送件地点等详细信息的订单记录,用户能够实时追踪订单进展,如待接单、处理中、已完成等不同阶段。 - **管理员模块**:管理员享有高级操作权限,能够接收并处理订单,执行订单的添加、删除、查询和修改等操作,同时负责处理用户的疑问和投诉。 - **支付模块**:系统整合了在线支付接口,支持用户通过第三方支付渠道完成支付,以此保障交易过程的安全性和便利性。 2. **技术选型**: - **SSM框架**:Spring主要用于依赖注入和事务控制,SpringMVC负责处理客户端请求服务器响应,MyBatis作为数据持久化层,执行数据库交互,三者协同工作构建了一个高效且灵活的开发环境。 - **MySQL数据库**:系统内所有数据,包括用户资料、订单详情、支付历史等,均存储于MySQL数据库中,其卓越的查询性能和稳定性为系统提供了可靠的数据基础。 3. **系统架构**: - **前端**:运用HTML、CSS和JavaScript进行界面设计,可能还会引入Vue.js或jQuery等库以增强用户体验。 - **后端*...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值