iOS 上 Security.framework爲咱們提供了安全方面相關的api;html
Security框架提供的RSA在iOS上使用的一些小結ios
在這裏說明一下RSA 相關的接口使用和示例;git
1. 主要接口有github
/*! //生成密鑰對 */ OSStatus SecKeyGeneratePair(CFDictionaryRef parameters, SecKeyRef * _Nullable CF_RETURNS_RETAINED publicKey, SecKeyRef * _Nullable CF_RETURNS_RETAINED privateKey) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0); //加密 OSStatus SecKeyEncrypt( SecKeyRef key, SecPadding padding, const uint8_t *plainText, size_t plainTextLen, uint8_t *cipherText, size_t *cipherTextLen) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0); //解密 OSStatus SecKeyDecrypt( SecKeyRef key, /* Private key */ SecPadding padding, /* kSecPaddingNone, kSecPaddingPKCS1, kSecPaddingOAEP */ const uint8_t *cipherText, size_t cipherTextLen, /* length of cipherText */ uint8_t *plainText, size_t *plainTextLen) /* IN/OUT */ __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0); //簽名 OSStatus SecKeyRawSign( SecKeyRef key, SecPadding padding, const uint8_t *dataToSign, size_t dataToSignLen, uint8_t *sig, size_t *sigLen) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0); //驗證簽名 OSStatus SecKeyRawVerify( SecKeyRef key, SecPadding padding, const uint8_t *signedData, size_t signedDataLen, const uint8_t *sig, size_t sigLen) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0); //分組加密的數據塊大小 size_t SecKeyGetBlockSize(SecKeyRef key) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0);
2. 首先生成RSA密鑰對,生成1024位,便是 128字節算法
#define kRSA_KEY_SIZE 1024 @interface ViewController : UIViewController { SecKeyRef publicKeyRef; //公鑰 SecKeyRef privateKeyRef;//私鑰 } //生成RSA密鑰對,公鑰和私鑰,支持的SIZE有 // sizes for RSA keys are: 512, 768, 1024, 2048. - (void)generateRSAKeyPair:(int )keySize { OSStatus ret = 0; publicKeyRef = NULL; privateKeyRef = NULL; ret = SecKeyGeneratePair((CFDictionaryRef)@{(id)kSecAttrKeyType:(id)kSecAttrKeyTypeRSA,(id)kSecAttrKeySizeInBits:@(keySize)}, &publicKeyRef, &privateKeyRef); NSAssert(ret==errSecSuccess, @"密鑰對生成失敗:%d",ret); NSLog(@"%@",publicKeyRef); NSLog(@"%@",privateKeyRef); NSLog(@"max size:%lu",SecKeyGetBlockSize(privateKeyRef)); }
3. 使用上面生成的密鑰對進行加密解密測試api
//公鑰加密私鑰密鑰測試 /** 三種填充方式區別 kSecPaddingNone = 0, 要加密的數據塊大小<=SecKeyGetBlockSize的大小,如這裏128 kSecPaddingPKCS1 = 1, 要加密的數據塊大小<=128-11 kSecPaddingOAEP = 2, 要加密的數據塊大小<=128-42 密碼學中的設計原則,通常用RSA來加密 對稱密鑰,用對稱密鑰加密大量的數據 非對稱加密速度慢,對稱加密速度快 */ - (void)testRSAEncryptAndDecrypt { [self generateRSAKeyPair:kRSA_KEY_SIZE]; NSData *srcData = [@"0123456789" dataUsingEncoding:NSUTF8StringEncoding]; NSLog(@"%@",srcData); uint8_t encData[kRSA_KEY_SIZE/8] = {0}; uint8_t decData[kRSA_KEY_SIZE/8] = {0}; size_t blockSize = kRSA_KEY_SIZE / 8 ; OSStatus ret; ret = SecKeyEncrypt(publicKeyRef, kSecPaddingNone, srcData.bytes, srcData.length, encData, &blockSize); NSAssert(ret==errSecSuccess, @"加密失敗"); ret = SecKeyDecrypt(privateKeyRef, kSecPaddingNone, encData, blockSize, decData, &blockSize); NSAssert(ret==errSecSuccess, @"解密失敗"); NSData *dedData = [NSData dataWithBytes:decData length:blockSize]; NSLog(@"dec:%@",dedData); if (memcmp(srcData.bytes, dedData.bytes, srcData.length)==0) { NSLog(@"PASS"); } }
4. 使用公鑰密鑰進行數據簽名和驗證簽名安全
對數據簽名:首先對原始數據進行hash計算,能夠獲得數據的hash值;而後對hash值進行簽名;服務器
- (void)testSignAndVerify { [self generateRSAKeyPair:kRSA_KEY_SIZE]; NSString *tpath = [[NSBundle mainBundle] pathForResource:@"src.txt" ofType:nil]; NSData *ttDt = [NSData dataWithContentsOfFile:tpath];
//使用了下面封裝的hash接口 NSData *sha1dg = [ttDt hashDataWith:CCDIGEST_SHA1]; OSStatus ret; //私鑰簽名,公鑰驗證簽名 size_t siglen = SecKeyGetBlockSize(privateKeyRef); uint8_t *sig = malloc(siglen); bzero(sig, siglen); ret = SecKeyRawSign(privateKeyRef, kSecPaddingPKCS1SHA256, sha1dg.bytes, sha1dg.length, sig, &siglen); NSAssert(ret==errSecSuccess, @"簽名失敗"); ret = SecKeyRawVerify(publicKeyRef, kSecPaddingPKCS1SHA256, sha1dg.bytes, sha1dg.length,sig, siglen); NSAssert(ret==errSecSuccess, @"驗證簽名失敗"); if (ret==errSecSuccess) { NSLog(@"SIGN VERIFY PASS"); } }
5. 另外 iOS上 CommonCrypto/CommonDigest.h 中提供了密碼學中經常使用的hash算法app
以下我封裝了一個NSData的分類,能夠在簽名中直接使用框架
支持的hash算法有 md2,md4,md5,sha1,sha224,sha256,sha384,sha512
// // NSData+KKHASH.h // SecurityiOS // // Created by cocoa on 16/12/15. // Copyright © 2016年 dev.keke@gmail.com. All rights reserved. // #import <Foundation/Foundation.h> typedef enum : NSUInteger { //md2 16字節長度 CCDIGEST_MD2 = 1000, //md4 16字節長度 CCDIGEST_MD4, //md5 16字節長度 CCDIGEST_MD5, //sha1 20字節長度 CCDIGEST_SHA1, //SHA224 28字節長度 CCDIGEST_SHA224, //SHA256 32字節長度 CCDIGEST_SHA256, //SHA384 48字節長度 CCDIGEST_SHA384, //SHA512 64字節長度 CCDIGEST_SHA512, } CCDIGESTAlgorithm; @interface NSData (KKHASH) /** 計算數據的hash值,根據不一樣的算法 */ - (NSData *)hashDataWith:(CCDIGESTAlgorithm )ccAlgorithm; /** 返回 hex string的 data */ - (NSString *)hexString; @end
// // NSData+KKHASH.m // SecurityiOS // // Created by cocoa on 16/12/15. // Copyright © 2016年 dev.keke@gmail.com. All rights reserved. // #import "NSData+KKHASH.h" #include <CommonCrypto/CommonDigest.h> @implementation NSData (KKHASH) - (NSData *)hashDataWith:(CCDIGESTAlgorithm )ccAlgorithm { NSData *retData = nil; if (self.length <1) { return nil; } unsigned char *md; switch (ccAlgorithm) { case CCDIGEST_MD2: { md = malloc(CC_MD2_DIGEST_LENGTH); bzero(md, CC_MD2_DIGEST_LENGTH); CC_MD2(self.bytes, (CC_LONG)self.length, md); retData = [NSData dataWithBytes:md length:CC_MD2_DIGEST_LENGTH]; } break; case CCDIGEST_MD4: { md = malloc(CC_MD4_DIGEST_LENGTH); bzero(md, CC_MD4_DIGEST_LENGTH); CC_MD4(self.bytes, (CC_LONG)self.length, md); retData = [NSData dataWithBytes:md length:CC_MD4_DIGEST_LENGTH]; } break; case CCDIGEST_MD5: { md = malloc(CC_MD5_DIGEST_LENGTH); bzero(md, CC_MD5_DIGEST_LENGTH); CC_MD5(self.bytes, (CC_LONG)self.length, md); retData = [NSData dataWithBytes:md length:CC_MD5_DIGEST_LENGTH]; } break; case CCDIGEST_SHA1: { md = malloc(CC_SHA1_DIGEST_LENGTH); bzero(md, CC_SHA1_DIGEST_LENGTH); CC_SHA1(self.bytes, (CC_LONG)self.length, md); retData = [NSData dataWithBytes:md length:CC_SHA1_DIGEST_LENGTH]; } break; case CCDIGEST_SHA224: { md = malloc(CC_SHA224_DIGEST_LENGTH); bzero(md, CC_SHA224_DIGEST_LENGTH); CC_SHA224(self.bytes, (CC_LONG)self.length, md); retData = [NSData dataWithBytes:md length:CC_SHA224_DIGEST_LENGTH]; } break; case CCDIGEST_SHA256: { md = malloc(CC_SHA256_DIGEST_LENGTH); bzero(md, CC_SHA256_DIGEST_LENGTH); CC_SHA256(self.bytes, (CC_LONG)self.length, md); retData = [NSData dataWithBytes:md length:CC_SHA256_DIGEST_LENGTH]; } break; case CCDIGEST_SHA384: { md = malloc(CC_SHA384_DIGEST_LENGTH); bzero(md, CC_SHA384_DIGEST_LENGTH); CC_SHA384(self.bytes, (CC_LONG)self.length, md); retData = [NSData dataWithBytes:md length:CC_SHA384_DIGEST_LENGTH]; } break; case CCDIGEST_SHA512: { md = malloc(CC_SHA512_DIGEST_LENGTH); bzero(md, CC_SHA512_DIGEST_LENGTH); CC_SHA512(self.bytes, (CC_LONG)self.length, md); retData = [NSData dataWithBytes:md length:CC_SHA512_DIGEST_LENGTH]; } break; default: md = malloc(1); break; } free(md); md = NULL; return retData; } - (NSString *)hexString { NSMutableString *result = nil; if (self.length <1) { return nil; } result = [[NSMutableString alloc] initWithCapacity:self.length * 2]; for (size_t i = 0; i < self.length; i++) { [result appendFormat:@"%02x", ((const uint8_t *) self.bytes)[i]]; } return result; } @end
6. 另外若是密鑰由服務器生成,能夠生成p12文件,來在ios上調用接口導入開發
OSStatus SecPKCS12Import(CFDataRef pkcs12_data, CFDictionaryRef options,
CFArrayRef * __nonnull CF_RETURNS_RETAINED items) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_2_0);
7. 另外爲了配合驗證,能夠用openssl 命令對數據進行RSA加密解密
//使用公鑰加密 1024位密鑰 ,128字節 //openssl rsautl -encrypt -out pubenc.txt -in src.txt -inkey public.pem -pubin //使用私鑰解密 //openssl rsautl -decrypt -in pubenc.txt -inkey private.pem -out pridec.txt
總結:
另外RSA密鑰,私鑰保存在手機上是不安全的;
通常在非越獄的手機上,咱們能夠把生成的SecKeyRef 保存在 keychain中;
可是在越獄的手機上,也是不安全的,由於能夠導出keychain中的全部數據;
沒有絕對的安全,根據業務場景來實際開發吧
封裝工具:https://github.com/cocoajin/Security-iOS
參考:
https://developer.apple.com/library/content/samplecode/CryptoExercise/Introduction/Intro.html#//apple_ref/doc/uid/DTS40008019
https://developer.apple.com/library/content/samplecode/CryptoCompatibility/Introduction/Intro.html#//apple_ref/doc/uid/DTS40013654