iOS使用Security.framework进行RSA 加密解密签名和验证签名
iOS 上 Security.framework为我们提供了安全方面相关的api;
Security框架提供的RSA在iOS上使用的一些小结
- 支持的RSA keySize 大小有:512,768,1024,2048位
- 支持的RSA 填充方式有三种:NOPadding,PKCS1,OAEP 三种方式 ,填充方式影响最大分组加密数据块的大小
- 签名使用的填充方式PKCS1, 支持的签名算法有 sha1,sha256,sha224,sha384,sha512
- Nopadding填充最大数据块为 下面接口 SecKeyGetBlockSize 大小;
- PKCS1 填充方式最大数据为 SecKeyGetBlockSize大小 减去11
- OAEP 填充方式最大数据为 SecKeyGetBlockSize 大小减去 42
- RSA加密解密签名,适合小块的数据处理,大量数量需要处理分组逻辑;密码学中推荐使用对称加密进行数据加密,使用RSA来加密对称密钥
- iOS10,以及mac 10.12中新增加了几个接口,以下测试用的是老接口,支持iOS2.0+
在这里说明一下RSA 相关的接口使用和示例;
1. 主要接口有
/*!//生成密钥对*/ 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. 使用上面生成的密钥对进行加密解密测试
//公钥加密私钥密钥测试 /** 三种填充方式区别kSecPaddingNone = 0, 要加密的数据块大小<=SecKeyGetBlockSize的大小,如这里128kSecPaddingPKCS1 = 1, 要加密的数据块大小<=128-11kSecPaddingOAEP = 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算法
如下我封装了一个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
View Code
// // 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
View Code
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/documentation/Security/Conceptual/cryptoservices/CryptographyConcepts/CryptographyConcepts.html#//apple_ref/doc/uid/TP40011172-CH8-SW1
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
iOS使用Security.framework进行RSA 加密解密签名和验证签名相关推荐
- python rsa加密解密_RSA加密解密(python版)
RSA的算法涉及三个参数,n.e.d. 其中,n是两个大质数p.q的积,n的二进制表示时所占用的位数,就是所谓的密钥长度. e1和d是一对相关的值,e可以任意取,但要求e与(p-1)*(q-1)互质: ...
- IOS之RSA加密解密与后台之间的双向加密详解
IOS之RSA加密解密与后台之间的双向加密详解 序言 因为项目中需要用到RSA加密,刚开始也是有点乱,这两天也整理的差不多了,希望能帮到大家. 这次先上代码,我想大部分人肯定是着急解决问题,所以不要废 ...
- iOS开发 RSA加密解密与后台之间的双向加密详解
转载自:https://www.jianshu.com/p/43f7fc8d8e14 序言 因为项目中需要用到RSA加密,刚开始也是有点乱,这两天也整理的差不多了,希望能帮到大家.这次先上代码,我想大 ...
- ios php rsa加密解密,php rsa加密解密使用详解
第一条命令生成原始 RSA私钥文件 rsa_private_key.pem,第二条命令将原始 RSA私钥转换为 pkcs8格式,第三条生成RSA公钥 rsa_public_key.pem 从上面看出通 ...
- C#的RSA加密解密签名,就为了支持PEM PKCS#8格式密钥对的导入导出
差点造了一整个轮子 .Net Framework 4.5 里面的RSA功能,并未提供简单对PEM密钥格式的支持(.Net Core有咩?),差点(还远着)造了一整个轮子,就为了支持PEM PKCS#8 ...
- OC RSA加密解密
好久好久没有更新了...你们等的急不急..这不,我就姗姗来迟了...本文重点讲解一下iOS系统下的RSA加密解密问题. 一般为了安全,私钥是不会给前端暴露出来 的,只会通过私钥生成一个公开的公钥提供给 ...
- java RSA加密解密--转载
原文地址:http://www.blogjava.net/icewee/archive/2012/05/19/378570.html 该工具类中用到了BASE64,需要借助第三方类库:javabase ...
- RSA加密解密及数字签名Java实现--转
RSA公钥加密算法是1977年由罗纳德·李维斯特(Ron Rivest).阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的.当时他们三人都在麻省理工学院 ...
- Java使用RSA加密解密签名及校验
RSA加密解密类: import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; i ...
最新文章
- python如何复制一个变量_Python中变量、赋值、浅拷贝、深拷贝
- liferay remove Your request completed successfully.
- 记一次 Vue 移动端活动倒计时优化
- 关于nginx为站点绑定域名以及绑定多个域名
- Memcache安装 2
- 【python基础】ValueError: only 2 non-keyword arguments accepte
- LeetCode 5366. 检查网格中是否存在有效路径
- 《Java从入门到放弃》框架入门篇:hibernate中的多表对应关系(二)
- ASP.NET中后台实现页面加载中效果(数据过多时提高用户体验度)
- python词库介绍_解析搜狗词库(python)
- 判断是否是微信浏览器还是企业微信浏览器
- Hadoop下载地址/hbase下载地址
- vue 检测ie版本_vue判断当前浏览器为IE低版本,给出升级提示;IE11及其他浏览器正常使用-Go语言中文社区...
- 计算机网络的常用命令汇总
- 直销立法前狼奔豕突 非法传销组织如何转型
- Verilog语言语句介绍
- Sourcetree git status失败 错误代码128:error :bad signature 0x0000000
- 如何在Ubuntu中安装搜狗输入法
- linux fq队列,理解fq_codel之概述
- RTTI (Runtime Type Identification)
热门文章
- Windows Server 2008 R2 之二十九故障转移群集(一)(
- 利用NetBIOS名称与其他计算机通信
- 想要学好Go语言的必须知道的一个小技巧
- WCF学习之旅—WCF服务的WAS寄宿(十二)
- 理解RESTful架构
- 20160115广州MVP线下聚会
- OpenCV学习(20) grabcut分割算法
- 致广大关注《网络规划设计师考试案例梳理、真题透解与强化训练》读者朋友的一封信...
- 可突破任意ARP防火墙,以限制流量为目标的简单网络管理软件
- Apache2.4.x下proxy_module、proxy_fcgi_module结合PHP-FPM解决内存不足问题