博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS使用Security.framework进行RSA 加密解密签名和验证签名
阅读量:6980 次
发布时间:2019-06-27

本文共 9899 字,大约阅读时间需要 32 分钟。

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的大小,如这里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算法

  如下我封装了一个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 
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 
@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://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

转载地址:http://otjpl.baihongyu.com/

你可能感兴趣的文章
Vue 生命周期
查看>>
可应用于实际的14个NLP突破性研究成果(四)
查看>>
PAT A1066
查看>>
React Native Vs. Xamarin Vs. Ionic Vs. Flutter
查看>>
高效随机数算法Java实现
查看>>
算法 - 时间复杂度
查看>>
H5小游戏 【篇一】 组词游戏
查看>>
枚举的使用示例
查看>>
换个姿势学数学:学的爽,用的上
查看>>
区块链分支循环
查看>>
runC爆严重漏洞影响Kubernetes、Docker,阿里云修复runC漏洞的公告
查看>>
力扣(LeetCode)146
查看>>
Understanding HBase and BigTable 译文
查看>>
02 ifconfig:最熟悉又陌生的命令行
查看>>
Java™ 教程(泛型、继承和子类型)
查看>>
阿里开源分布式事务解决方案 Fescar 全解析
查看>>
Spring AOP
查看>>
如何优雅的构建排序公式
查看>>
angualr有哪些优缺点?
查看>>
JavaScript中的继承及实现代码
查看>>