封装了一些iOS上使用的NSData分类,主要用于 RSA加密,AES加密,数据签名,签名校验,MD5 SHA1 SHA256 常用hash等工具。
主要使用的是iOS上 Security.framework 和 CommonCrypto 接口
支持iOS2.0+开发
接口文件和源码 NSData+KKHASH.h,NSData+KKHASH.m
支持的hash算法有
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;
调用接口
/** 计算数据的hash值,根据不同的算法 */ - (NSData *)hashDataWith:(CCDIGESTAlgorithm )ccAlgorithm; /** 返回 hex string的 data */ - (NSString *)hexString;
调用示例
//测试哈希函数 - (void)testKKHASHTools { //以下结果由openssl dtsg 命令得出的结果,并和这里相比对 NSString *tpath = [[NSBundle mainBundle] pathForResource:@"src.txt" ofType:nil]; NSData *test = [NSData dataWithContentsOfFile:tpath]; NSLog(@"%@",[[test hashDataWith:CCDIGEST_MD2] hexString]); if([[[test hashDataWith:CCDIGEST_MD4] hexString] isEqualToString:@"a695ea9f14a89c4e82ca5cf52a28d45d"]) { NSLog(@"MD4 TEST PASS"); } if([[[test hashDataWith:CCDIGEST_MD5] hexString] isEqualToString:@"781e5e245d69b566979b86e28d23f2c7"]) { NSLog(@"MD5 TEST PASS"); } if([[[test hashDataWith:CCDIGEST_SHA1] hexString] isEqualToString:@"87acec17cd9dcd20a716cc2cf67417b71c8a7016"]) { NSLog(@"SHA1 TEST PASS"); } if([[[test hashDataWith:CCDIGEST_SHA224] hexString] isEqualToString:@"f28ad8ecd48ba6f914c114821685ad08f0d6103649ff156599a90426"]) { NSLog(@"SHA224 TEST PASS"); } if([[[test hashDataWith:CCDIGEST_SHA256] hexString] isEqualToString:@"84d89877f0d4041efb6bf91a16f0248f2fd573e6af05c19f96bedb9f882f7882"]) { NSLog(@"SHA256 TEST PASS"); } if([[[test hashDataWith:CCDIGEST_SHA384] hexString] isEqualToString:@"90ae531f24e48697904a4d0286f354c50a350ebb6c2b9efcb22f71c96ceaeffc11c6095e9ca0df0ec30bf685dcf2e5e5"]) { NSLog(@"SHA384 TEST PASS"); } if([[[test hashDataWith:CCDIGEST_SHA512] hexString] isEqualToString:@"bb96c2fc40d2d54617d6f276febe571f623a8dadf0b734855299b0e107fda32cf6b69f2da32b36445d73690b93cbd0f7bfc20e0f7f28553d2a4428f23b716e90"]) { NSLog(@"SHA512 TEST PASS"); } }
接口文件和源码 NSData+KKAES.h,NSData+KKAES.m
支持AES cbc,ecb两种模式,默认使用的填充方式 kCCOptionPKCS7Padding.
可以参考更多 iOS CommonCrypto 对称加密 AES ecb,cbc
主要接口
/** AES cbc 模式加密, @key 长度16字节,24字节,32字节 @iv 16字节 */ - (NSData *)AES_CBC_EncryptWith:(NSData *)key iv:(NSData *)iv; /** AES cbc 模式解密, @key 长度16字节,24字节,32字节 @iv 16字节 */ - (NSData *)AES_CBC_DecryptWith:(NSData *)key iv:(NSData *)iv; /** AES ecb 模式加密, @key 长度16字节,24字节,32字节 */ - (NSData *)AES_ECB_EncryptWith:(NSData *)key; /** AES ecb 模式解密, @key 长度16字节,24字节,32字节 */ - (NSData *)AES_ECB_DecryptWith:(NSData *)key;
调用示例
NSData *key16 = [@"0123456789123456" dataUsingEncoding:NSUTF8StringEncoding]; NSData *key24 = [@"012345678901234567891234" dataUsingEncoding:NSUTF8StringEncoding]; NSData *key32 = [@"01234567890123456789012345678912" dataUsingEncoding:NSUTF8StringEncoding]; NSData *iv16 = [@"0123456789654321" dataUsingEncoding:NSUTF8StringEncoding]; NSData *srcData = [@"this is src test data" dataUsingEncoding:NSUTF8StringEncoding]; NSLog(@"%@",srcData); NSData *dec16 = [[srcData AES_CBC_EncryptWith:key16 iv:iv16] AES_CBC_DecryptWith:key16 iv:iv16]; if (memcmp(srcData.bytes, dec16.bytes, srcData.length)==0) { NSLog(@"AES_cbc_16 PASS"); } NSData *dec24 = [[srcData AES_CBC_EncryptWith:key24 iv:iv16] AES_CBC_DecryptWith:key24 iv:iv16]; if (memcmp(srcData.bytes, dec24.bytes, srcData.length)==0) { NSLog(@"AES_cbc_24 PASS"); } NSData *dec32 = [[srcData AES_CBC_EncryptWith:key32 iv:iv16] AES_CBC_DecryptWith:key32 iv:iv16]; if (memcmp(srcData.bytes, dec32.bytes, srcData.length)==0) { NSLog(@"AES_cbc_32 PASS"); } NSData *edec16 = [[srcData AES_ECB_EncryptWith:key16] AES_ECB_DecryptWith:key16]; if (memcmp(srcData.bytes, edec16.bytes, srcData.length)==0) { NSLog(@"AES_ecb_16 PASS"); } NSData *edec24 = [[srcData AES_ECB_EncryptWith:key24] AES_ECB_DecryptWith:key24]; if (memcmp(srcData.bytes, edec24.bytes, srcData.length)==0) { NSLog(@"AES_ecb_24 PASS"); } NSData *edec32 = [[srcData AES_ECB_EncryptWith:key32] AES_ECB_DecryptWith:key32]; if (memcmp(srcData.bytes, edec32.bytes, srcData.length)==0) { NSLog(@"AES_ecb_32 PASS"); }
接口和源码 NSData+KKRSA.h,NSData+KKRSA.m
支持的RSA密钥位数:512,768,1024,2048等; 可以参考更多 iOS使用Security.framework进行RSA 加密解密签名和验证签名
支持的填充方式
//分组加密,支持最大的加密块为 block 和填充方式有关 typedef enum : NSUInteger { //不填充,最大数据块为 blockSize RSAPaddingNONE, //填充方式pkcs1,最大数据块为 blockSize -11 RSAPaddingPKCS1, //填充方式OAEP, 最大数据块为 blockSize -42 RSAPaddingOAEP, } RSAPaddingTYPE;
调用接口
/** 公钥加密 */ - (NSData *)RSAEncryptWith:(SecKeyRef )publicKey paddingType:(RSAPaddingTYPE )pdType; /** 私钥解密 */ - (NSData *)RSADecryptWith:(SecKeyRef )privateKey paddingType:(RSAPaddingTYPE )pdType;
调用示例
//生成RSA密钥对,公钥和私钥,支持的SIZE有 // sizes for RSA keys are: 512, 768, 1024, 2048. - (void)generateRSAKeyPair:(int )keySize { if (publicKeyRef) { return; } 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)); } //生成密钥对 [self generateRSAKeyPair:kRSA_KEY_SIZE]; NSData *srcData = [@"01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567" dataUsingEncoding:NSASCIIStringEncoding]; //max 128 NSData *decData = [[srcData RSAEncryptWith:publicKeyRef paddingType:RSAPaddingNONE] RSADecryptWith:privateKeyRef paddingType:RSAPaddingNONE]; if (memcmp(srcData.bytes, decData.bytes, srcData.length)==0) { NSLog(@"RSA RSAPaddingNONE TEST PASS"); } NSData *srcData2 = [@"012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456" dataUsingEncoding:NSASCIIStringEncoding]; //max 128-11 117 NSData *decData2 = [[srcData2 RSAEncryptWith:publicKeyRef paddingType:RSAPaddingPKCS1] RSADecryptWith:privateKeyRef paddingType:RSAPaddingPKCS1]; if (memcmp(srcData2.bytes, decData2.bytes, srcData2.length)==0) { NSLog(@"RSA RSAPaddingPKCS1 TEST PASS"); } NSData *srcData3 = [@"01234567890123456789012345678901234567890123456789012345678901234567890123456789123456" dataUsingEncoding:NSASCIIStringEncoding]; //max 128-42 86 NSData *decData3 = [[srcData3 RSAEncryptWith:publicKeyRef paddingType:RSAPaddingOAEP] RSADecryptWith:privateKeyRef paddingType:RSAPaddingOAEP]; if (memcmp(srcData3.bytes, decData3.bytes, srcData3.length)==0) { NSLog(@"RSA RSAPaddingOAEP TEST PASS"); }
接口文件和源码 NSData+KKSignVerify.h,NSData+KKSignVerify.m
数据签名一般签名数据的hash值,配合上面的HASH函数使用
支持的签名算法
//主要使用PKCS1 方式的填充,最大签名数据长度为blockSize-11 //签名算法从ios5以后不再支持md5,md2 typedef enum : NSUInteger { SEC_PKCS1SHA1 = 2000, SEC_PKCS1SHA224, SEC_PKCS1SHA256, SEC_PKCS1SHA384, SEC_PKCS1SHA512, } SEC_PKCS1_ALGORITHM;
主要接口
/** 根据不同的算法,签名数据, */ - (NSData *)signDataWith:(SecKeyRef)privateKey algorithm:(SEC_PKCS1_ALGORITHM )ccAlgorithm; /** 验证签名数据 */ - (BOOL)verifySignWith:(SecKeyRef)publicKey signData:(NSData *)signData algorithm:(SEC_PKCS1_ALGORITHM )ccAlgorithm;
调用示例
//生成RSA密钥对, [self generateRSAKeyPair:kRSA_KEY_SIZE]; NSString *tpath = [[NSBundle mainBundle] pathForResource:@"src.txt" ofType:nil]; NSData *ttDt = [NSData dataWithContentsOfFile:tpath]; NSData *sha1dg = [ttDt hashDataWith:CCDIGEST_MD4]; if ([sha1dg verifySignWith:publicKeyRef signData:[sha1dg signDataWith:privateKeyRef algorithm:SEC_PKCS1SHA1] algorithm:SEC_PKCS1SHA1]) { NSLog(@"SIGN-VERIFY sha1 PASS"); } if ([sha1dg verifySignWith:publicKeyRef signData:[sha1dg signDataWith:privateKeyRef algorithm:SEC_PKCS1SHA224] algorithm:SEC_PKCS1SHA224]) { NSLog(@"SIGN-VERIFY sha224 PASS"); } if ([sha1dg verifySignWith:publicKeyRef signData:[sha1dg signDataWith:privateKeyRef algorithm:SEC_PKCS1SHA256] algorithm:SEC_PKCS1SHA256]) { NSLog(@"SIGN-VERIFY sha256 PASS"); } if ([sha1dg verifySignWith:publicKeyRef signData:[sha1dg signDataWith:privateKeyRef algorithm:SEC_PKCS1SHA384] algorithm:SEC_PKCS1SHA384]) { NSLog(@"SIGN-VERIFY sha384 PASS"); } if ([sha1dg verifySignWith:publicKeyRef signData:[sha1dg signDataWith:privateKeyRef algorithm:SEC_PKCS1SHA512] algorithm:SEC_PKCS1SHA512]) { NSLog(@"SIGN-VERIFY sha512 PASS"); }
接口文件和源码 SecKeyTools.h,SecKeyTools.m
主要用于读取密钥文件中的密钥,如从证书中读取公钥,从p12文件中读取私钥等操作; RSA相关的密钥放在手机上是不安全的,但是也没有绝对的安全,视业务情况来定吧。
主要接口
/** 从x509 cer证书中读取公钥 */ + (SecKeyRef )publicKeyFromCer:(NSString *)cerFile; /** 从 p12 文件中读取私钥,一般p12都有密码 */ + (SecKeyRef )privateKeyFromP12:(NSString *)p12File password:(NSString *)pwd; /** iOS 10 上可用如下接口SecKeyCreateWithData 从pem文件中读取私钥或公钥 */ + (SecKeyRef )publicKeyFromPem:(NSString *)pemFile keySize:(size_t )size; + (SecKeyRef )privaKeyFromPem:(NSString *)pemFile keySize:(size_t )size;
调用示例
NSString *cerPA = [[NSBundle mainBundle] pathForResource:@"CPPUB.cer" ofType:nil]; NSString *p12PA = [[NSBundle mainBundle] pathForResource:@"CPPRI.p12" ofType:nil]; SecKeyRef pubkey = [SecKeyTools publicKeyFromCer:cerPA]; SecKeyRef prikey = [SecKeyTools privateKeyFromP12:p12PA password:@"test"]; NSLog(@"%@",pubkey); NSLog(@"%@",prikey); NSLog(@"%lu",SecKeyGetBlockSize(prikey)); NSData *srcData = [@"0123456789" dataUsingEncoding:NSUTF8StringEncoding]; NSLog(@"%@",srcData); NSData *encDT = [srcData RSAEncryptWith:pubkey paddingType:RSAPaddingPKCS1]; NSData *decDT = [encDT RSADecryptWith:prikey paddingType:RSAPaddingPKCS1]; NSLog(@"%@",decDT); NSString *pripem = [[NSBundle mainBundle] pathForResource:@"private.pem" ofType:nil]; NSString *pubpem = [[NSBundle mainBundle] pathForResource:@"public.pem" ofType:nil]; SecKeyRef pubKK = [SecKeyTools publicKeyFromPem:pubpem keySize:kRSA_KEY_SIZE]; SecKeyRef priKK = [SecKeyTools privaKeyFromPem:pripem keySize:kRSA_KEY_SIZE]; NSLog(@"%@",pubKK); NSLog(@"%@",priKK); NSLog(@"%lu",SecKeyGetBlockSize(priKK)); NSData *srcData2 = [@"0123456789" dataUsingEncoding:NSUTF8StringEncoding]; NSLog(@"%@",srcData2); NSData *encDT2 = [srcData2 RSAEncryptWith:pubKK paddingType:RSAPaddingPKCS1]; NSData *decDT2 = [encDT2 RSADecryptWith:priKK paddingType:RSAPaddingPKCS1]; NSLog(@"%@",decDT2);
- 密钥对的生成
//生成RSA密钥对,公钥和私钥,支持的SIZE有 // sizes for RSA keys are: 512, 768, 1024, 2048. - (void)generateRSAKeyPair:(int )keySize { if (publicKeyRef) { return; } 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)); }
- RSA上几种填充方式的区别
/** 三种填充方式区别 kSecPaddingNone = 0, 要加密的数据块大小<=SecKeyGetBlockSize的大小,如这里128 kSecPaddingPKCS1 = 1, 要加密的数据块大小<=128-11 kSecPaddingOAEP = 2, 要加密的数据块大小<=128-42 密码学中的设计原则,一般用RSA来加密 对称密钥,用对称密钥加密大量的数据 非对称加密速度慢,对称加密速度快 */
- 示例工程
SecurityiOS.xcodeproj