開發(fā)中常用的加密方式和實際應(yīng)用

既然說到加密,必然和隱私相關(guān)京闰,那么就從網(wǎng)絡(luò)開發(fā)兩大原則說起:

1-網(wǎng)絡(luò)上不允許傳輸用戶的明文隱私數(shù)據(jù)锨亏。
2-本地不允許保存用戶的額明文隱私數(shù)據(jù)。

那么既然不能使用明文進行傳輸和保存忙干,我們同時又需要進行相關(guān)操作的話器予,就必須要使用下面說到的各種加密了。
首先捐迫,市面上常用的加密方式分為對稱加密,非對稱加密哈希加密乾翔。

1.對稱加密:

文件加密和解密使用相同的密鑰,即加密密鑰也可以用作解密密鑰。
常用加密算法有DES,3DES,AES等.

DES  數(shù)據(jù)加密標準(用的比較少,因為強度不夠).
3DES 使用3個密鑰,對相同的數(shù)據(jù)執(zhí)行三次加密,強度增強.
AES  高級加密標準,目前美國國家安全局使用AES加密,蘋果的鑰匙串訪問就是使用AES加密反浓。

而對稱加密一般分為四種模式:ECB,CBC,CFBOFB萌丈,最為常見的是前兩種。

ECB的特點:
加密文件會被分為若干個加密塊雷则,
每個塊都是獨立進行加密辆雾,互不影響。
所以月劈,如果變動數(shù)據(jù)中的某一個地方度迂,
加密之后其對應(yīng)得塊也會發(fā)生變化,
缺點是一旦被破解某個模塊猜揪,就可以進行推理惭墓,
進而有機會獲取到重要數(shù)據(jù)。

關(guān)于ECB的終端操作代碼:

加密: $ openssl enc -des-ecb -K 616263 -nosalt -in 
msg.txt -out msgLock.bin
解密:$ openssl enc -des-ecb -K 616263 -nosalt -in 
msgLock.bin -out msg.txt -d
查看加密之后的二進制文件:$ xxd msgLock.bin
CBC的特點:
使用一個密鑰和一個初始化向量 (IV)對數(shù)據(jù)執(zhí)
行加密轉(zhuǎn)換而姐。
加密文件同樣會被分為若干個加密塊腊凶,
每個塊都依賴于上一個加密塊進行加密,互相牽制拴念。
所以伞鲫,如果變動數(shù)據(jù)中的某一個地方磨澡,
加密之后整體數(shù)據(jù)都會發(fā)生變化城须。
可以有效地保證密文的完整性

關(guān)于CBC的終端操作代碼:

加密: $ openssl enc -des-cbc -K 616263 -iv 
0000000000000000 -nosalt -in a.txt -out 
msg1.bin
解密:$ openssl enc -des-cbc -K 616263 -iv 
0000000000000000 -nosalt -in msg1.bin -out 
msg4.txt -d
查看加密之后的二進制文件:$ xxd msg1.bin

2.非對稱加密

與對稱加密算法不同拐迁,非對稱加密算法需要兩個密鑰:公開密鑰(publickey)和私有密鑰(privatekey)。也就是公鑰加密缔俄,私鑰解密或者私鑰加密弛秋,公鑰解密。常見的非對稱加密算法有RSA(加密俐载,證書生成),DSA(數(shù)字簽名)等,非對稱加密算法的保密性比較好蟹略,但加密和解密花費時間長、速度慢遏佣,它不適合于對文件加密而只適用于對少量數(shù)據(jù)進行加密挖炬。

使用openssl生成密鑰加密的步驟:
生成強度是 512 的 RSA 私鑰:$ openssl genrsa -out private.pem 512
以明文輸出私鑰內(nèi)容:$ openssl rsa -in private.pem -text -out 
private.txt
校驗私鑰文件:$ openssl rsa -in private.pem -check
從私鑰中提取公鑰:$ openssl rsa -in private.pem -out public.pem 
-outform PEM -pubout
以明文輸出公鑰內(nèi)容:$ openssl rsa -in public.pem -out public.txt 
-pubin -pubout -text 
使用公鑰加密小文件:$ openssl rsautl -encrypt -pubin -inkey 
public.pem -in msg.txt -out msg.bin
使用私鑰解密小文件:$ openssl rsautl -decrypt -inkey private.pem 
-in msg.bin -out a.txt
將私鑰轉(zhuǎn)換成 DER 格式:$ openssl rsa -in private.pem -out private.der 
-outform der
將公鑰轉(zhuǎn)換成 DER 格式:$ openssl rsa -in public.pem -out public.der 
-pubin -outform der
程序開發(fā)數(shù)字證書的生成:
生成私鑰:openssl genrsa -out ca.key 1024
創(chuàng)建證書請求:openssl req -new -key ca.key -out rsacert.csr
生成證書并簽名,有效期10年:openssl x509 -req -days 3650 -in rsacert.csr 
-signkey ca.key -out rsacert.crt
轉(zhuǎn)換格式(將 openssl默認生成的PEM 格式文件轉(zhuǎn)換成iOS支持的DER 格式):openssl x509 -outform der -in rsacert.crt -out 
rsacert.der
導(dǎo)入P12文件:openssl pkcs12 -export -out p.p12 -inkey 
ca.key -in rsacert.crt
iOS開發(fā)中相關(guān)的函數(shù):
使用公鑰對數(shù)據(jù)加密:SecKeyEncrypt
使用私鑰對數(shù)據(jù)解密:SecKeyDecrypt
使用公鑰對數(shù)字簽名進行驗證:SecKeyRawVerify
使用私鑰生成數(shù)字簽名:SecKeyRawSign

3.HASH加密

Hash算法特別的地方在于它是一種單向算法状婶,用戶可以通過Hash算法對目標信息生成一段特定長度(32個字符)的唯一的Hash值意敛,卻不能通過這個Hash值重新獲得目標信息。對用相同數(shù)據(jù)膛虫,加密之后的密文相同草姻。
常見的Hash算法有MD5SHA
由于加密結(jié)果固定稍刀,所以基本上原始的哈希加密已經(jīng)不再安全撩独,于是衍生出了加鹽的方式敞曹。

加鹽:先對原始數(shù)據(jù)拼接固定的字符串再進行加密。
HMAC:也叫隨機鹽综膀,給定一個密鑰澳迫,對明文進行拼接,
并且做兩次散列剧劝。其中密鑰由服務(wù)器在用戶注冊時候返回給客戶端橄登。
iOS對稱加密代碼:
 //AES - ECB 加密
    NSString * key = @"hk";
    //加密
    NSLog(@"加密: %@",[[EncryptionTools sharedEncryptionTools] encryptString:@"hello" keyString:key iv:nil]);
    //解密
    NSLog(@"解密: %@",[[EncryptionTools sharedEncryptionTools] decryptString:@"cKRPM1ALLG+0q5qCjADoaQ==" keyString:key iv:nil]);
//AES - CBC 加密
    uint8_t iv[8] = {2,3,4,5,6,7,0,0}; //直接影響加密結(jié)果!
    NSData * ivData = [NSData dataWithBytes:iv length:sizeof(iv)];
    
    NSLog(@"CBC加密: %@",[[EncryptionTools sharedEncryptionTools] encryptString:@"hello" keyString:key iv:ivData]);
    
    NSLog(@"CBC解密: %@", [[EncryptionTools sharedEncryptionTools] decryptString:@"+dv/u4juE0WE3S9XSFyibA==" keyString:key iv:ivData]);
 //DES - ECB 加密
    [EncryptionTools sharedEncryptionTools].algorithm = kCCAlgorithmDES;
    NSLog(@"DES 加密%@",[[EncryptionTools sharedEncryptionTools] encryptString:@"hello" keyString:key iv:nil]);
    NSLog(@"DES 解密: %@", [[EncryptionTools sharedEncryptionTools] decryptString:@"vTuv8E5AlWQ=" keyString:key iv:nil]);

其中EncryptionTools文件如下:

#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonCrypto.h>

/**
 *  終端測試指令
 *
 *  DES(ECB)加密
 *  $ echo -n hello | openssl enc -des-ecb -K 616263 -nosalt | base64
 *
  * DES(CBC)加密
 *  $ echo -n hello | openssl enc -des-cbc -iv 0102030405060708 -K 616263 -nosalt | base64
 *
 *  AES(ECB)加密
 *  $ echo -n hello | openssl enc -aes-128-ecb -K 616263 -nosalt | base64
 *
 *  AES(CBC)加密
 *  $ echo -n hello | openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt | base64
 *
 *  DES(ECB)解密
 *  $ echo -n HQr0Oij2kbo= | base64 -D | openssl enc -des-ecb -K 616263 -nosalt -d
 *
 *  DES(CBC)解密
 *  $ echo -n alvrvb3Gz88= | base64 -D | openssl enc -des-cbc -iv 0102030405060708 -K 616263 -nosalt -d
 *
 *  AES(ECB)解密
 *  $ echo -n d1QG4T2tivoi0Kiu3NEmZQ== | base64 -D | openssl enc -aes-128-ecb -K 616263 -nosalt -d
 *
 *  AES(CBC)解密
 *  $ echo -n u3W/N816uzFpcg6pZ+kbdg== | base64 -D | openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt -d
 *
 *  提示:
 *      1> 加密過程是先加密,再base64編碼
 *      2> 解密過程是先base64解碼讥此,再解密
 */
@interface EncryptionTools : NSObject

+ (instancetype)sharedEncryptionTools;

/**
 @constant   kCCAlgorithmAES     高級加密標準拢锹,128位(默認)
 @constant   kCCAlgorithmDES     數(shù)據(jù)加密標準
 */
@property (nonatomic, assign) uint32_t algorithm;

/**
 *  加密字符串并返回base64編碼字符串
 *
 *  @param string    要加密的字符串
 *  @param keyString 加密密鑰
 *  @param iv        初始化向量(8個字節(jié))
 *
 *  @return 返回加密后的base64編碼字符串
 */
- (NSString *)encryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv;

/**
 *  解密字符串
 *
 *  @param string    加密并base64編碼后的字符串
 *  @param keyString 解密密鑰
 *  @param iv        初始化向量(8個字節(jié))
 *
 *  @return 返回解密后的字符串
 */
- (NSString *)decryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv;

@end
#import "EncryptionTools.h"

@interface EncryptionTools()
@property (nonatomic, assign) int keySize;
@property (nonatomic, assign) int blockSize;
@end

@implementation EncryptionTools

+ (instancetype)sharedEncryptionTools {
    static EncryptionTools *instance;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
        instance.algorithm = kCCAlgorithmAES;
    });
    
    return instance;
}

- (void)setAlgorithm:(uint32_t)algorithm {
    _algorithm = algorithm;
    switch (algorithm) {
        case kCCAlgorithmAES:
            self.keySize = kCCKeySizeAES128;
            self.blockSize = kCCBlockSizeAES128;
            break;
        case kCCAlgorithmDES:
            self.keySize = kCCKeySizeDES;
            self.blockSize = kCCBlockSizeDES;
            break;
        default:
            break;
    }
}

- (NSString *)encryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv {
    
    // 設(shè)置秘鑰
    NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
    uint8_t cKey[self.keySize];
    bzero(cKey, sizeof(cKey));
    [keyData getBytes:cKey length:self.keySize];
    
    // 設(shè)置iv
    /*
     kCCOptionPKCS7Padding                      CBC 的加密方式
     kCCOptionPKCS7Padding | kCCOptionECBMode   ECB 的加密方式
     */
    uint8_t cIv[self.blockSize];
    bzero(cIv, self.blockSize);
    int option = 0;
    if (iv) {
        [iv getBytes:cIv length:self.blockSize];
        option = kCCOptionPKCS7Padding;
    } else {
        option = kCCOptionPKCS7Padding | kCCOptionECBMode;
    }
    
    // 設(shè)置輸出緩沖區(qū)
    NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
    size_t bufferSize = [data length] + self.blockSize;
    void *buffer = malloc(bufferSize);
    
    // 開始加密
    size_t encryptedSize = 0;
    /***
     CCCrypt 對稱加密算法的核心函數(shù)(加密/解密)
     參數(shù):
     1.kCCEncrypt  加密/kCCDecrypt 解密
     2.加密算法,默認使用的是  AES/DES
     3.加密選項  ECB/CBC 
         kCCOptionPKCS7Padding                      CBC 的加密方式
         kCCOptionPKCS7Padding | kCCOptionECBMode   ECB 的加密方式
     4.加密密鑰
     5.密鑰長度
     6.iv 初始化向量,ECB 不需要指定
     7.加密的數(shù)據(jù)
     8.加密的數(shù)據(jù)的長度
     9.密文的內(nèi)存地址
     10.密文緩沖區(qū)的大小
     11.加密結(jié)果大小
     */
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
                                          self.algorithm,
                                          option,
                                          cKey,
                                          self.keySize,
                                          cIv,
                                          [data bytes],
                                          [data length],
                                          buffer,
                                          bufferSize,
                                          &encryptedSize);
    
    NSData *result = nil;
    if (cryptStatus == kCCSuccess) {
        result = [NSData dataWithBytesNoCopy:buffer length:encryptedSize];
    } else {    
        free(buffer);
        NSLog(@"[錯誤] 加密失敗|狀態(tài)編碼: %d", cryptStatus);
    }
    
    return [result base64EncodedStringWithOptions:0];
}

- (NSString *)decryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv {
    
    // 設(shè)置秘鑰
    NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
    uint8_t cKey[self.keySize];
    bzero(cKey, sizeof(cKey));
    [keyData getBytes:cKey length:self.keySize];
    
    // 設(shè)置iv
    uint8_t cIv[self.blockSize];
    bzero(cIv, self.blockSize);
    int option = 0;
    if (iv) {
        [iv getBytes:cIv length:self.blockSize];
        option = kCCOptionPKCS7Padding;
    } else {
        option = kCCOptionPKCS7Padding | kCCOptionECBMode;
    }
    
    // 設(shè)置輸出緩沖區(qū)
    NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0];
    size_t bufferSize = [data length] + self.blockSize;
    void *buffer = malloc(bufferSize);
    
    // 開始解密
    size_t decryptedSize = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
                                          self.algorithm,
                                          option,
                                          cKey,
                                          self.keySize,
                                          cIv,
                                          [data bytes],
                                          [data length],
                                          buffer,
                                          bufferSize,
                                          &decryptedSize);
    
    NSData *result = nil;
    if (cryptStatus == kCCSuccess) {
        result = [NSData dataWithBytesNoCopy:buffer length:decryptedSize];
    } else {
        free(buffer);
        NSLog(@"[錯誤] 解密失敗|狀態(tài)編碼: %d", cryptStatus);
    }
    
    return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
}

@end
iOS RSA加密代碼:
- (void)viewDidLoad {
    [super viewDidLoad];
    //1.加載公鑰
    [[RSACryptor sharedRSACryptor] loadPublicKey:[[NSBundle mainBundle] pathForResource:@"rsacert.der" ofType:nil]];
    //2. 加載私鑰 - P12的文件  password : 生成P12 的時候設(shè)置的密碼
    [[RSACryptor sharedRSACryptor] loadPrivateKey:[[NSBundle mainBundle] pathForResource:@"p.p12" ofType:nil] password:@"123456"];
}


- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    NSData * reault = [[RSACryptor sharedRSACryptor] encryptData:[@"hello" dataUsingEncoding:NSUTF8StringEncoding]];
    //base64 編碼
    NSString * base64 = [reault base64EncodedStringWithOptions:0];
    NSLog(@"加密的信息: %@",base64);
    
    //解密
    NSData * jiemi = [[RSACryptor sharedRSACryptor] decryptData:reault];
    NSLog(@"%@",[[NSString alloc]initWithData:jiemi encoding:NSUTF8StringEncoding]);
    
}

RSACryptor文件如下:

#import <Foundation/Foundation.h>

@interface RSACryptor : NSObject

+ (instancetype)sharedRSACryptor;

/**
 *  生成密鑰對
 *
 *  @param keySize 密鑰尺寸暂论,可選數(shù)值(512/1024/2048)
 */
- (void)generateKeyPair:(NSUInteger)keySize;

/**
 *  加載公鑰
 *
 *  @param publicKeyPath 公鑰路徑
 *
 @code
 # 生成證書
 $ openssl genrsa -out ca.key 1024
 # 創(chuàng)建證書請求
 $ openssl req -new -key ca.key -out rsacert.csr
 # 生成證書并簽名
 $ openssl x509 -req -days 3650 -in rsacert.csr -signkey ca.key -out rsacert.crt
 # 轉(zhuǎn)換格式
 $ openssl x509 -outform der -in rsacert.crt -out rsacert.der
 @endcode
 */
- (void)loadPublicKey:(NSString *)publicKeyPath;

/**
 *  加載私鑰
 *
 *  @param privateKeyPath p12文件路徑
 *  @param password       p12文件密碼
 *
 @code
 openssl pkcs12 -export -out p.p12 -inkey ca.key -in rsacert.crt
 @endcode
 */
- (void)loadPrivateKey:(NSString *)privateKeyPath password:(NSString *)password;

/**
 *  加密數(shù)據(jù)
 *
 *  @param plainData 明文數(shù)據(jù)
 *
 *  @return 密文數(shù)據(jù)
 */
- (NSData *)encryptData:(NSData *)plainData;

/**
 *  解密數(shù)據(jù)
 *
 *  @param cipherData 密文數(shù)據(jù)
 *
 *  @return 明文數(shù)據(jù)
 */
- (NSData *)decryptData:(NSData *)cipherData;

@end

#import "RSACryptor.h"

// 填充模式
// kSecPaddingNone 每次加密結(jié)果是固定的   kSecPaddingPKCS1 是隨機的
#define kTypeOfWrapPadding      kSecPaddingPKCS1

// 公鑰/私鑰標簽
#define kPublicKeyTag           "com.itheima.sample.publickey"
#define kPrivateKeyTag          "com.itheima.sample.privatekey"

static const uint8_t publicKeyIdentifier[]      = kPublicKeyTag;
static const uint8_t privateKeyIdentifier[]     = kPrivateKeyTag;

@interface RSACryptor() {
    SecKeyRef publicKeyRef;                             // 公鑰引用
    SecKeyRef privateKeyRef;                            // 私鑰引用
}

@property (nonatomic, retain) NSData *publicTag;        // 公鑰標簽
@property (nonatomic, retain) NSData *privateTag;       // 私鑰標簽

@end

@implementation RSACryptor

+ (instancetype)sharedRSACryptor {
    static id instance;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        // 查詢密鑰的標簽
        _privateTag = [[NSData alloc] initWithBytes:privateKeyIdentifier length:sizeof(privateKeyIdentifier)];
        _publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
    }
    return self;
}

#pragma mark - 加密 & 解密數(shù)據(jù)
- (NSData *)encryptData:(NSData *)plainData {
    OSStatus sanityCheck = noErr;
    size_t cipherBufferSize = 0;
    size_t keyBufferSize = 0;
    
    NSAssert(plainData != nil, @"明文數(shù)據(jù)為空");
    NSAssert(publicKeyRef != nil, @"公鑰為空");
    
    NSData *cipher = nil;
    uint8_t *cipherBuffer = NULL;
    
    // 計算緩沖區(qū)大小
    cipherBufferSize = SecKeyGetBlockSize(publicKeyRef);
    keyBufferSize = [plainData length];
    
    if (kTypeOfWrapPadding == kSecPaddingNone) {
        NSAssert(keyBufferSize <= cipherBufferSize, @"加密內(nèi)容太大");
    } else {
        NSAssert(keyBufferSize <= (cipherBufferSize - 11), @"加密內(nèi)容太大");
    }
    
    // 分配緩沖區(qū)
    cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
    memset((void *)cipherBuffer, 0x0, cipherBufferSize);
    
    // 使用公鑰加密
    sanityCheck = SecKeyEncrypt(publicKeyRef,
                                kTypeOfWrapPadding,
                                (const uint8_t *)[plainData bytes],
                                keyBufferSize,
                                cipherBuffer,
                                &cipherBufferSize
                                );
    
    NSAssert(sanityCheck == noErr, @"加密錯誤面褐,OSStatus == %d", sanityCheck);
    
    // 生成密文數(shù)據(jù)
    cipher = [NSData dataWithBytes:(const void *)cipherBuffer length:(NSUInteger)cipherBufferSize];
    
    if (cipherBuffer) free(cipherBuffer);
    
    return cipher;
}

- (NSData *)decryptData:(NSData *)cipherData {
    OSStatus sanityCheck = noErr;
    size_t cipherBufferSize = 0;
    size_t keyBufferSize = 0;
    
    NSData *key = nil;
    uint8_t *keyBuffer = NULL;
    
    SecKeyRef privateKey = NULL;
    
    privateKey = [self getPrivateKeyRef];
    NSAssert(privateKey != NULL, @"私鑰不存在");
    
    // 計算緩沖區(qū)大小
    cipherBufferSize = SecKeyGetBlockSize(privateKey);
    keyBufferSize = [cipherData length];
    
    NSAssert(keyBufferSize <= cipherBufferSize, @"解密內(nèi)容太大");
    
    // 分配緩沖區(qū)
    keyBuffer = malloc(keyBufferSize * sizeof(uint8_t));
    memset((void *)keyBuffer, 0x0, keyBufferSize);
    
    // 使用私鑰解密
    sanityCheck = SecKeyDecrypt(privateKey,
                                kTypeOfWrapPadding,
                                (const uint8_t *)[cipherData bytes],
                                cipherBufferSize,
                                keyBuffer,
                                &keyBufferSize
                                );
    
    NSAssert1(sanityCheck == noErr, @"解密錯誤拌禾,OSStatus == %d", sanityCheck);
    
    // 生成明文數(shù)據(jù)
    key = [NSData dataWithBytes:(const void *)keyBuffer length:(NSUInteger)keyBufferSize];
    
    if (keyBuffer) free(keyBuffer);
    
    return key;
}

#pragma mark - 密鑰處理
/**
 *  生成密鑰對
 */
- (void)generateKeyPair:(NSUInteger)keySize {
    OSStatus sanityCheck = noErr;
    publicKeyRef = NULL;
    privateKeyRef = NULL;
    
    NSAssert1((keySize == 512 || keySize == 1024 || keySize == 2048), @"密鑰尺寸無效 %tu", keySize);
    
    // 刪除當前密鑰對
    [self deleteAsymmetricKeys];
    
    // 容器字典
    NSMutableDictionary *privateKeyAttr = [[NSMutableDictionary alloc] init];
    NSMutableDictionary *publicKeyAttr = [[NSMutableDictionary alloc] init];
    NSMutableDictionary *keyPairAttr = [[NSMutableDictionary alloc] init];
    
    // 設(shè)置密鑰對的頂級字典
    [keyPairAttr setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    [keyPairAttr setObject:[NSNumber numberWithUnsignedInteger:keySize] forKey:(__bridge id)kSecAttrKeySizeInBits];
    
    // 設(shè)置私鑰字典
    [privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent];
    [privateKeyAttr setObject:_privateTag forKey:(__bridge id)kSecAttrApplicationTag];
    
    // 設(shè)置公鑰字典
    [publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent];
    [publicKeyAttr setObject:_publicTag forKey:(__bridge id)kSecAttrApplicationTag];
    
    // 設(shè)置頂級字典屬性
    [keyPairAttr setObject:privateKeyAttr forKey:(__bridge id)kSecPrivateKeyAttrs];
    [keyPairAttr setObject:publicKeyAttr forKey:(__bridge id)kSecPublicKeyAttrs];
    
    // SecKeyGeneratePair 返回密鑰對引用
    sanityCheck = SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttr, &publicKeyRef, &privateKeyRef);
    NSAssert((sanityCheck == noErr && publicKeyRef != NULL && privateKeyRef != NULL), @"生成密鑰對失敗");
}

/**
 *  加載公鑰
 */
- (void)loadPublicKey:(NSString *)publicKeyPath {
    
    NSAssert(publicKeyPath.length != 0, @"公鑰路徑為空");
    
    // 刪除當前公鑰
    if (publicKeyRef) CFRelease(publicKeyRef);
    
    // 從一個 DER 表示的證書創(chuàng)建一個證書對象
    NSData *certificateData = [NSData dataWithContentsOfFile:publicKeyPath];
    SecCertificateRef certificateRef = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certificateData);
    NSAssert(certificateRef != NULL, @"公鑰文件錯誤");
    
    // 返回一個默認 X509 策略的公鑰對象取胎,使用之后需要調(diào)用 CFRelease 釋放
    SecPolicyRef policyRef = SecPolicyCreateBasicX509();
    // 包含信任管理信息的結(jié)構(gòu)體
    SecTrustRef trustRef;
    
    // 基于證書和策略創(chuàng)建一個信任管理對象
    OSStatus status = SecTrustCreateWithCertificates(certificateRef, policyRef, &trustRef);
    NSAssert(status == errSecSuccess, @"創(chuàng)建信任管理對象失敗");
    
    // 信任結(jié)果
    SecTrustResultType trustResult;
    // 評估指定證書和策略的信任管理是否有效
    status = SecTrustEvaluate(trustRef, &trustResult);
    NSAssert(status == errSecSuccess, @"信任評估失敗");
    
    // 評估之后返回公鑰子證書
    publicKeyRef = SecTrustCopyPublicKey(trustRef);
    NSAssert(publicKeyRef != NULL, @"公鑰創(chuàng)建失敗");
    
    if (certificateRef) CFRelease(certificateRef);
    if (policyRef) CFRelease(policyRef);
    if (trustRef) CFRelease(trustRef);
}

/**
 *  加載私鑰
 */
- (void)loadPrivateKey:(NSString *)privateKeyPath password:(NSString *)password {
    
    NSAssert(privateKeyPath.length != 0, @"私鑰路徑為空");
    
    // 刪除當前私鑰
    if (privateKeyRef) CFRelease(privateKeyRef);
    
    NSData *PKCS12Data = [NSData dataWithContentsOfFile:privateKeyPath];
    CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
    CFStringRef passwordRef = (__bridge CFStringRef)password;
    
    // 從 PKCS #12 證書中提取標示和證書
    SecIdentityRef myIdentity;
    SecTrustRef myTrust;
    const void *keys[] =   {kSecImportExportPassphrase};
    const void *values[] = {passwordRef};
    CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    
    // 返回 PKCS #12 格式數(shù)據(jù)中的標示和證書
    OSStatus status = SecPKCS12Import(inPKCS12Data, optionsDictionary, &items);
    
    if (status == noErr) {
        CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
        myIdentity = (SecIdentityRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
        myTrust = (SecTrustRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust);
    }
    
    if (optionsDictionary) CFRelease(optionsDictionary);
    
    NSAssert(status == noErr, @"提取身份和信任失敗");
    
    SecTrustResultType trustResult;
    // 評估指定證書和策略的信任管理是否有效
    status = SecTrustEvaluate(myTrust, &trustResult);
    NSAssert(status == errSecSuccess, @"信任評估失敗");
    
    // 提取私鑰
    status = SecIdentityCopyPrivateKey(myIdentity, &privateKeyRef);
    NSAssert(status == errSecSuccess, @"私鑰創(chuàng)建失敗");
}

/**
 *  刪除非對稱密鑰
 */
- (void)deleteAsymmetricKeys {
    OSStatus sanityCheck = noErr;
    NSMutableDictionary *queryPublicKey = [[NSMutableDictionary alloc] init];
    NSMutableDictionary *queryPrivateKey = [[NSMutableDictionary alloc] init];
    
    // 設(shè)置公鑰查詢字典
    [queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
    [queryPublicKey setObject:_publicTag forKey:(__bridge id)kSecAttrApplicationTag];
    [queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    
    // 設(shè)置私鑰查詢字典
    [queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
    [queryPrivateKey setObject:_privateTag forKey:(__bridge id)kSecAttrApplicationTag];
    [queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    
    // 刪除私鑰
    sanityCheck = SecItemDelete((__bridge CFDictionaryRef)queryPrivateKey);
    NSAssert1((sanityCheck == noErr || sanityCheck == errSecItemNotFound), @"刪除私鑰錯誤,OSStatus == %d", sanityCheck);
    
    // 刪除公鑰
    sanityCheck = SecItemDelete((__bridge CFDictionaryRef)queryPublicKey);
    NSAssert1((sanityCheck == noErr || sanityCheck == errSecItemNotFound), @"刪除公鑰錯誤湃窍,OSStatus == %d", sanityCheck);
    
    if (publicKeyRef) CFRelease(publicKeyRef);
    if (privateKeyRef) CFRelease(privateKeyRef);
}

/**
 *  獲得私鑰引用
 */
- (SecKeyRef)getPrivateKeyRef {
    OSStatus sanityCheck = noErr;
    SecKeyRef privateKeyReference = NULL;
    
    if (privateKeyRef == NULL) {
        NSMutableDictionary * queryPrivateKey = [[NSMutableDictionary alloc] init];
        
        // 設(shè)置私鑰查詢字典
        [queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
        [queryPrivateKey setObject:_privateTag forKey:(__bridge id)kSecAttrApplicationTag];
        [queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
        [queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
        
        // 獲得密鑰
        sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPrivateKey, (CFTypeRef *)&privateKeyReference);
        
        if (sanityCheck != noErr) {
            privateKeyReference = NULL;
        }
    } else {
        privateKeyReference = privateKeyRef;
    }
    
    return privateKeyReference;
}

@end

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末闻蛀,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子您市,更是在濱河造成了極大的恐慌觉痛,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茵休,死亡現(xiàn)場離奇詭異薪棒,居然都是意外死亡,警方通過查閱死者的電腦和手機榕莺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門俐芯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人钉鸯,你說我怎么就攤上這事吧史。” “怎么了唠雕?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵贸营,是天一觀的道長。 經(jīng)常有香客問我岩睁,道長钞脂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任捕儒,我火速辦了婚禮冰啃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己亿笤,他們只是感情好翎迁,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著净薛,像睡著了一般汪榔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肃拜,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天痴腌,我揣著相機與錄音,去河邊找鬼燃领。 笑死士聪,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的猛蔽。 我是一名探鬼主播剥悟,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼曼库!你這毒婦竟也來了区岗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤毁枯,失蹤者是張志新(化名)和其女友劉穎慈缔,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體种玛,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡藐鹤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了赂韵。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片娱节。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖右锨,靈堂內(nèi)的尸體忽然破棺而出括堤,到底是詐尸還是另有隱情,我是刑警寧澤绍移,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布悄窃,位于F島的核電站,受9級特大地震影響蹂窖,放射性物質(zhì)發(fā)生泄漏轧抗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一瞬测、第九天 我趴在偏房一處隱蔽的房頂上張望横媚。 院中可真熱鬧纠炮,春花似錦、人聲如沸灯蝴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽穷躁。三九已至耕肩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間问潭,已是汗流浹背猿诸。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留狡忙,地道東北人梳虽。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像灾茁,于是被迫代替她去往敵國和親窜觉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 本文主要介紹移動端的加解密算法的分類删顶、其優(yōu)缺點特性及應(yīng)用竖螃,幫助讀者由淺入深地了解和選擇加解密算法淑廊。文中會包含算法的...
    蘋果粉閱讀 11,506評論 5 29
  • 2018-Read-Record 記錄我的2018學(xué)習(xí)歷程 文中首先解釋了加密解密的一些基礎(chǔ)知識和概念季惩,然后通過一...
    NinthDay閱讀 11,295評論 8 105
  • 這篇文章主要講述在Mobile BI(移動商務(wù)智能)開發(fā)過程中录粱,在網(wǎng)絡(luò)通信、數(shù)據(jù)存儲画拾、登錄驗證這幾個方面涉及的加密...
    雨_樹閱讀 2,433評論 0 6
  • 概述 之前一直對加密相關(guān)的算法知之甚少啥繁,只知道類似DES、RSA等加密算法能對數(shù)據(jù)傳輸進行加密青抛,且各種加密算法各有...
    Henryzhu閱讀 3,019評論 0 14
  • 今日三只青蛙: 1.冥想旗闽。 2.時間管理100講第27講。? 3.簡書總結(jié)90天收獲蜜另。 時間管理第27講 今日感悟:
    Eason媽咪閱讀 190評論 0 0