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 加密解密签名和验证签名相关推荐

  1. python rsa加密解密_RSA加密解密(python版)

    RSA的算法涉及三个参数,n.e.d. 其中,n是两个大质数p.q的积,n的二进制表示时所占用的位数,就是所谓的密钥长度. e1和d是一对相关的值,e可以任意取,但要求e与(p-1)*(q-1)互质: ...

  2. IOS之RSA加密解密与后台之间的双向加密详解

    IOS之RSA加密解密与后台之间的双向加密详解 序言 因为项目中需要用到RSA加密,刚开始也是有点乱,这两天也整理的差不多了,希望能帮到大家. 这次先上代码,我想大部分人肯定是着急解决问题,所以不要废 ...

  3. iOS开发 RSA加密解密与后台之间的双向加密详解

    转载自:https://www.jianshu.com/p/43f7fc8d8e14 序言 因为项目中需要用到RSA加密,刚开始也是有点乱,这两天也整理的差不多了,希望能帮到大家.这次先上代码,我想大 ...

  4. ios php rsa加密解密,php rsa加密解密使用详解

    第一条命令生成原始 RSA私钥文件 rsa_private_key.pem,第二条命令将原始 RSA私钥转换为 pkcs8格式,第三条生成RSA公钥 rsa_public_key.pem 从上面看出通 ...

  5. C#的RSA加密解密签名,就为了支持PEM PKCS#8格式密钥对的导入导出

    差点造了一整个轮子 .Net Framework 4.5 里面的RSA功能,并未提供简单对PEM密钥格式的支持(.Net Core有咩?),差点(还远着)造了一整个轮子,就为了支持PEM PKCS#8 ...

  6. OC RSA加密解密

    好久好久没有更新了...你们等的急不急..这不,我就姗姗来迟了...本文重点讲解一下iOS系统下的RSA加密解密问题. 一般为了安全,私钥是不会给前端暴露出来 的,只会通过私钥生成一个公开的公钥提供给 ...

  7. java RSA加密解密--转载

    原文地址:http://www.blogjava.net/icewee/archive/2012/05/19/378570.html 该工具类中用到了BASE64,需要借助第三方类库:javabase ...

  8. RSA加密解密及数字签名Java实现--转

    RSA公钥加密算法是1977年由罗纳德·李维斯特(Ron Rivest).阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的.当时他们三人都在麻省理工学院 ...

  9. Java使用RSA加密解密签名及校验

    RSA加密解密类: import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; i ...

最新文章

  1. python如何复制一个变量_Python中变量、赋值、浅拷贝、深拷贝
  2. liferay remove Your request completed successfully.
  3. 记一次 Vue 移动端活动倒计时优化
  4. 关于nginx为站点绑定域名以及绑定多个域名
  5. Memcache安装 2
  6. 【python基础】ValueError: only 2 non-keyword arguments accepte
  7. LeetCode 5366. 检查网格中是否存在有效路径
  8. 《Java从入门到放弃》框架入门篇:hibernate中的多表对应关系(二)
  9. ASP.NET中后台实现页面加载中效果(数据过多时提高用户体验度)
  10. python词库介绍_解析搜狗词库(python)
  11. 判断是否是微信浏览器还是企业微信浏览器
  12. Hadoop下载地址/hbase下载地址
  13. vue 检测ie版本_vue判断当前浏览器为IE低版本,给出升级提示;IE11及其他浏览器正常使用-Go语言中文社区...
  14. 计算机网络的常用命令汇总
  15. 直销立法前狼奔豕突 非法传销组织如何转型
  16. Verilog语言语句介绍
  17. Sourcetree git status失败 错误代码128:error :bad signature 0x0000000
  18. 如何在Ubuntu中安装搜狗输入法
  19. linux fq队列,理解fq_codel之概述
  20. RTTI (Runtime Type Identification)

热门文章

  1. Windows Server 2008 R2 之二十九故障转移群集(一)(
  2. 利用NetBIOS名称与其他计算机通信
  3. 想要学好Go语言的必须知道的一个小技巧
  4. WCF学习之旅—WCF服务的WAS寄宿(十二)
  5. 理解RESTful架构
  6. 20160115广州MVP线下聚会
  7. OpenCV学习(20) grabcut分割算法
  8. 致广大关注《网络规划设计师考试案例梳理、真题透解与强化训练》读者朋友的一封信...
  9. 可突破任意ARP防火墙,以限制流量为目标的简单网络管理软件
  10. Apache2.4.x下proxy_module、proxy_fcgi_module结合PHP-FPM解决内存不足问题