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 = ;
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 = [@"" dataUsingEncoding:NSUTF8StringEncoding];
NSLog(@"%@",srcData);
uint8_t encData[kRSA_KEY_SIZE/] = {};
uint8_t decData[kRSA_KEY_SIZE/] = {};
size_t blockSize = kRSA_KEY_SIZE / ;
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)==) {
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 = ,
//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 <) {
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();
break;
} free(md);
md = NULL; return retData; } - (NSString *)hexString
{
NSMutableString *result = nil;
if (self.length <) {
return nil;
}
result = [[NSMutableString alloc] initWithCapacity:self.length * ];
for (size_t i = ; 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/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. Cryptopp iOS 使用 RSA加密解密和签名验证签名

    Cryptopp 是一个c++写的功能完善的密码学工具,类似于openssl 官网:https://www.cryptopp.com 以下主要演示Cryptopp 在iOS上的RSA加密解密签名与验证 ...

  2. iOS常用加密之RSA加密解密

    前言: iOS常用的加密有很多种,前两天在工作中遇到了RSA加密,现在把代吗分享出来. RSA基本原理 RSA使用"秘匙对"对数据进行加密解密.在加密解密数据前,需要先生成公钥(p ...

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

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

  4. C#中RSA加密解密和签名与验证的实现

    RSA加密算法是一种非对称加密算法.在公钥加密标准和电子商业中RSA被广泛使用.RSA是1977年由罗纳德•李维斯特(Ron Rivest).阿迪•萨莫尔(Adi Shamir)和伦纳德•阿德曼(Le ...

  5. 【转】C#中RSA加密解密和签名与验证的实现

    [转]C#中RSA加密解密和签名与验证的实现 RSA加密算法是一种非对称加密算法.在公钥加密标准和电子商业中RSA被广泛使用.RSA是1977年由罗纳德•李维斯特(Ron Rivest).阿迪•萨莫尔 ...

  6. 兼容javascript和C#的RSA加密解密算法,对web提交的数据进行加密传输

    Web应用中往往涉及到敏感的数据,由于HTTP协议以明文的形式与服务器进行交互,因此可以通过截获请求的数据包进行分析来盗取有用的信息.虽然https可以对传输的数据进行加密,但是必须要申请证书(一般都 ...

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

    该工具类中用到了BASE64,需要借助第三方类库:javabase64-1.3.1.jar注意:RSA加密明文最大长度117字节,解密要求密文最大长度为128字节,所以在加密和解密的过程中需要分块进行 ...

  8. C# 与JAVA 的RSA 加密解密交互,互通,C#使用BouncyCastle来实现私钥加密,公钥解密的方法

    因为C#的RSA加密解密只有公钥加密,私钥解密,没有私钥加密,公钥解密.在网上查了很久也没有很好的实现.BouncyCastle的文档少之又少.很多人可能会说,C#也是可以的,通过Biginteger ...

  9. C# Java间进行RSA加密解密交互(二)

    原文:C# Java间进行RSA加密解密交互(二) 接着前面一篇文章C# Java间进行RSA加密解密交互,继续探讨这个问题. 在前面,虽然已经实现了C# Java间进行RSA加密解密交互,但是还是与 ...

随机推荐

  1. android下面使用SurfaceView+ mediaPlayer播放视频

    final SurfaceView surfaceView = new SurfaceView(StartupActivity.this); StartupActivity.this.mediaPla ...

  2. HttpEntity转换Inputstream(红色)加XmlPull解析

    package com.bawei.xml; import java.io.IOException; import java.io.InputStream; import java.util.Arra ...

  3. ECshop后台角色权限的添加和分配

    1.在权限文件中配置 2.在需要加权限的文件中加入权限. 例如:d:\ecshop\admin\user_account.php 3.在数据库表 ecs_admin_action中配置 4.会员权限 ...

  4. 马士兵Java视频教程 —— 学习顺序

    第一部分:J2se学习视频内容包括: 尚学堂科技_马士兵_JAVA视频教程_JDK5.0_下载-安装-配置 尚学堂科技_马士兵_JAVA视频教程_J2SE_5.0_第01章_JAVA简介_源代码_及重 ...

  5. SeleniumIDE从0到1 (Selenium IDE 安装)

    换了工作后需要学习到自动化测试,经过一系列的筛选,最终选定了Selenium,原因是因为本人熟悉一点代码,用Selenium比较容易上手.刚开始接触Selenium的小伙伴是不是会觉得不知道从何动手呢 ...

  6. Arduino学习经验(一)之解决舵机库和pwm输出冲突

    一.前言 最近在公司学习Arduino uno ,用它实现小车超声波避障功能.实现的功能很简单,就是在小车前方挂一个超声波模块,当碰到障碍物时,会通过舵机进行摆头,判断两边的距离,进行左右转弯.但是碰 ...

  7. 表单 阻止 技巧 JavaScript js

    阻止表单的提交,可以用return false 来进行阻止 长度不低于6,不高于20 if(username.length < 6 || username>20){ alert (&quo ...

  8. eclipse 用links 安装插件

    eclipse 用links 安装插件: 1.在eclipse目录下新建文件夹links 2.在links里新建文件 "控件名".link controlName.link 3.在 ...

  9. 利用wangEditor获取文章格式和内容

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs& ...

  10. xfs磁盘(文件)碎片查看和整理

    网上有些帖子说XFS不用做碎片整理,其实是错误的.XFS用延迟写入等技术确实可以减少碎片的出现,但是如果服务器用了几年,并且文件操作比较频繁,还是会出现碎片的,应该整理.注意:在Debian中XFS相 ...