iOS关于RSA的一些基础知识

本文最后更新于:2 年前

一、基础概念

  • RSA加密算法是最常用的非对称加密算法,目前普遍认为是最优秀的方案之一,关于RSA的数学原理和方法,这里不做说明,感兴趣的可以自行了解下。本文主要介绍RSA的一些常用的基础知识

  • RSA分为公钥和私钥,生成时会需要传入参数,秘钥位数和秘钥格式,秘钥位数有512bit1024bit2048bit等,通常使用的是1024bit,私钥格式分为PKCS8PKCS1,其中PKCS8java语言中使用的格式,PKCS1iOS相关语言中使用的格式。

  • 通常生成的秘钥会保存在.pem格式的文件中,使用文本编辑器或者代码编辑器打开,可以查看到保存的值,如果是按1024bit取模,PKCS1格式的私钥长度应该是824(包含回车),如果是PKCS8的格式的密钥长度为861

  • 通常RSA有两种用法,公钥加密私钥解密和私钥签名公钥验签,加密是为了数据保密,签名是为了防恶意攻击,例如防止别人模拟我们的客户端对我们的服务器进行攻击,导致服务器瘫痪等

二、一些在线网站

有一些网站可以在线生成RSA秘钥,例如http://web.chacuo.net/netrsakeypair,这些网站可以便捷的生成私钥,并自动得出相应的公钥,以及能在线转换格式,加密解密等

三、命令行生成命令

我们先了解下一个软件包OpenSSLOpenSSL是为网络通信提供安全及数据完整性的一种安全协议,囊括了主要的密码算法、常用的密钥和证书封装管理功能以及SSL协议,并提供了丰富的应用程序供测试或其它目的使用。LibreSSLOpenSSL加密软件库的一个分支。在OpenSSL爆出安全漏洞之后,于2014年4月创立,目标是重构OpenSSL的代码,以提供一个更安全的替代品。所以通过在电脑上安装OpenSSL或者LibreSSL后,即可通过相关的命令行生成秘钥。mac电脑已自带LibreSSL,如果没有,可以寻找相关的安装方法,此处不再扩展

1、基础生成命令
  1. 生成模长为1024bit的私钥
1
$ openssl genrsa -out /xx/xx/private_pkcs1.pem 1024

private_pkcs1.pem前为本地路径,pem文件会生成到制定目录,长度824,此命令行生成的为PKCS1格式的私钥,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDWa6g5OgPyB5xCkvEIitA1gKOBoQi21nAVJL7iVMqkA2MrhmUl
ZonITpqMW3syTtaksMualat1CmqJRQaDn+UVe0u2Wgtvvu8KaDdbDa6Tyq6S2K89
GI2l/AzouLE06daaHDh3qjtwNmEDqsK7mEcSPddIKaIOHZ7bKzOkbYnGEwIDAQAB
AoGBAIiOY8KeaijYI/JaNtuj3FpWpMtHzY70Hsm4b0EhkzTFW4E6xGv/U7yYIuFE
2b7+asDUP7chnuKZUaQ+q5lkWbYcKsSWORgGBUeYUOVoU9SuTp65oFKkc9AOtaxz
VavX536Y1VRtT+b8mB++gZwp5cZHUbJTj4BPSKH7IJawKskRAkEA7jA4c+I3ZfFD
koOl41x3cvwH+xQLi6vpL6Gw3/muJrc6mtdZbcuXwjMYlac++mXPTUt1K4b1sUmG
G3qwnkUjXwJBAOZ0b2fZhRhsDzNFihnD1PXGNl56FVG3G0aWZzQIHQX1DIE+sG4k
CJxrqxTnHWc+O2ODPU2PbJUN0fcpEuoDbc0CQQC9LpWYDUP89yy5cVDQDgBd1qos
FRa6f/d9Ooq2yqQ04fFtTMAeAcfumhDbxHO0BCsr9FQDF3WLs58NslwXyUg3AkBg
GK1b4JhfVq//8T9k/wQOeFizjLTXHkOBa7YdPETd9xD/0+Q+CUiN8Veln7njE1Aw
bslhTi04+kpThg0dB9EBAkEAiWIlUGra4M5q7u+FWbgtcJRKjMX6LIlsB1UiFHba
auSSQGwP+LMHVYDaWljzcPO/c5/MZbIDmGCZsDeImvSdMQ==
-----END RSA PRIVATE KEY-----
  1. PKCS1PKCS8互相转换
1
2
3
4
5
// PKCS1私钥转换为PKCS8,java语言使用的是PKCS8
$ openssl pkcs8 -topk8 -inform PEM -in /xx/xx/private_pkcs1.pem -outform pem -nocrypt -out /xx/xx/private_pkcs8.pem

// PKCS8私钥转换为PKCS1
$ openssl rsa -in /xx/xx/private_pkcs8.pem -out /xx/xx/private_pkcs1.pem

同样文件名之前为本地路径,生成到对应的路径下,生成的PKCS8格式私钥如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-----BEGIN PRIVATE KEY-----
MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANZrqDk6A/IHnEKS
8QiK0DWAo4GhCLbWcBUkvuJUyqQDYyuGZSVmichOmoxbezJO1qSwy5qVq3UKaolF
BoOf5RV7S7ZaC2++7wpoN1sNrpPKrpLYrz0YjaX8DOi4sTTp1pocOHeqO3A2YQOq
wruYRxI910gpog4dntsrM6RticYTAgMBAAECgYEAiI5jwp5qKNgj8lo226PcWlak
y0fNjvQeybhvQSGTNMVbgTrEa/9TvJgi4UTZvv5qwNQ/tyGe4plRpD6rmWRZthwq
xJY5GAYFR5hQ5WhT1K5OnrmgUqRz0A61rHNVq9fnfpjVVG1P5vyYH76BnCnlxkdR
slOPgE9IofsglrAqyRECQQDuMDhz4jdl8UOSg6XjXHdy/Af7FAuLq+kvobDf+a4m
tzqa11lty5fCMxiVpz76Zc9NS3UrhvWxSYYberCeRSNfAkEA5nRvZ9mFGGwPM0WK
GcPU9cY2XnoVUbcbRpZnNAgdBfUMgT6wbiQInGurFOcdZz47Y4M9TY9slQ3R9ykS
6gNtzQJBAL0ulZgNQ/z3LLlxUNAOAF3WqiwVFrp/9306irbKpDTh8W1MwB4Bx+6a
ENvEc7QEKyv0VAMXdYuznw2yXBfJSDcCQGAYrVvgmF9Wr//xP2T/BA54WLOMtNce
Q4Frth08RN33EP/T5D4JSI3xV6WfueMTUDBuyWFOLTj6SlOGDR0H0QECQQCJYiVQ
atrgzmru74VZuC1wlEqMxfosiWwHVSIUdtpq5JJAbA/4swdVgNpaWPNw879zn8xl
sgOYYJmwN4ia9J0x
-----END PRIVATE KEY-----
  1. 生成公钥

RSA公钥是由私钥中生成的,公钥不区分格式,PKCS1PKCS8都能生成公钥,生成结果一致

1
$ openssl rsa -in /xx/xx/private_pkcs1.pem -pubout -out /xx/xx/public.pem

生成结果如下

1
2
3
4
5
6
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDWa6g5OgPyB5xCkvEIitA1gKOB
oQi21nAVJL7iVMqkA2MrhmUlZonITpqMW3syTtaksMualat1CmqJRQaDn+UVe0u2
Wgtvvu8KaDdbDa6Tyq6S2K89GI2l/AzouLE06daaHDh3qjtwNmEDqsK7mEcSPddI
KaIOHZ7bKzOkbYnGEwIDAQAB
-----END PUBLIC KEY-----

以上为生成RSA秘钥基本的操作,所有的秘钥文件均以.pem格式存在,可以直接使用文本编辑器读取其中的秘钥字符串,秘钥字符串为Base64格式,AndroidiOS在使用时都可以直接使用文件或者字符串

2、iOS数字证书

.pem格式文件已能正常使用,iOS系统也可以直接使用文件中的秘钥字符串,为了进一步保证安全性,iOS系统中可以对公钥和私钥进行进一步签名,做成数字证书的形式,.der格式文件存放,.p12格式的文件存放的是私钥,.der.p12文件均为二进制文件,无法查看

  1. 生成证书请求文件.csr
1
$ openssl req -new -key /xxx/xxx/private_pkcs1.pem -out /xxx/xxx/rsa_cert.csr

这一步会提示输入国家、省份等信息,可酌情填写

  1. 生成证书.crt,并设置有效时间1年
1
$ openssl x509 -req -days 365 -in /xxx/xxx/rsa_cert.csr -signkey /xxx/xxx/private_pkcs1.pem -out /xxx/xxx/rsa_cert.crt
  1. 导出iOS使用的公钥文件.der
1
$ openssl x509 -outform der -in /xxx/xxx/rsa_cert.crt -out /xxx/xxx/public.der
  1. 导出iOS使用的私钥文件.p12
1
$ openssl pkcs12 -export -out /xxx/xxx/private.p12 -inkey /xxx/xxx/private_pkcs1.pem -in /xxx/xxx/rsa_cert.crt

这一步会提示给私钥证书.p12设置证书密码,输入即可

四、iOS代码

1、OpenSSL开源库

github上有OpenSSL的开源代码,但是工程里面无法直接引入,此处推荐另一个开源代码OpenSSL-for-iPhone,在README中作者提供了相关的编译方法

image_1.png

编译完成后,找到OpenSSL-for-iPhone工程里面编译成功的.a文件,libcrypto.alibssl.a,还有文件夹include

image_2.png

导入项目工程中,并把include文件夹添加到Build Setting中的Header Search Paths中,再检查Library Search Paths中是否能引用到.a文件,具体代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#import <Foundation/Foundation.h>
#import <openssl/rsa.h>

NS_ASSUME_NONNULL_BEGIN

typedef enum : NSUInteger {
OpenSSL_RSA_SignType_MD5,
OpenSSL_RSA_SignType_SHA1,
OpenSSL_RSA_SignType_SHA224,
OpenSSL_RSA_SignType_SHA256,
OpenSSL_RSA_SignType_SHA384,
OpenSSL_RSA_SignType_SHA512,
} OpenSSL_RSA_SignType;

@interface OpenSSLWrapper : NSObject

/**
生成一对秘钥

@param keySize 512、1024、2048
@param publicKey 公钥内存地址
@param privateKey 私钥内存地址
@return 是否成功生成
*/
+ (BOOL)generateKeyPairWithKeySize:(int)keySize publicKey:(RSA *_Nullable*_Nullable)publicKey privateKey:(RSA *_Nullable*_Nullable)privateKey;

/**
公钥加密

@param publicKey 公钥
@param plainString 源数据
@return 加密后数据
*/
+ (NSString *)opensslEncryptWithPublicKey:(RSA *)publicKey plainString:(NSString *)plainString;

/**
私钥解密

@param privateKey 私钥
@param encryptString 加密后数据
@return 源数据
*/
+ (NSString *)opensslDecryptWithPrivateKey:(RSA *)privateKey encryptString:(NSString *)encryptString;

/**
私钥签名

@param privateKey 私钥
@param signType 签名算法
@param plainString 源数据
@return 签名后数据
*/
+ (NSString *)signWithPrivateKey:(RSA *)privateKey signType:(OpenSSL_RSA_SignType)signType plainString:(NSString *)plainString;

/**
公钥验证签名

@param publicKey 公钥
@param signType 签名算法
@param plainString 源数据
@param signString 签名后数据
@return 是否验证成功
*/
+ (BOOL)verifyWithPublicKey:(RSA *)publicKey signType:(OpenSSL_RSA_SignType)signType plainString:(NSString *)plainString signString:(NSString *)signString;

// RSA转base64
+ (NSString *)base64StringFromPublicRSAKey:(RSA *)rsaKey;
+ (NSString *)base64StringFromPrivateRSAKey:(RSA *)rsaKey;
// base64转RSA秘钥
+ (RSA *)publicRSAKeyFromBase64String:(NSString *)keyString;
+ (RSA *)privateRSAKeyFromBase64String:(NSString *)keyString;

@end

NS_ASSUME_NONNULL_END
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
#import "OpenSSLWrapper.h"
#import <openssl/pem.h>
#import <CommonCrypto/CommonCrypto.h>

@implementation OpenSSLWrapper

/**
生成一对秘钥
*/
+ (BOOL)generateKeyPairWithKeySize:(int)keySize publicKey:(RSA *_Nullable*_Nullable)publicKey privateKey:(RSA *_Nullable*_Nullable)privateKey {
if (keySize == 512 || keySize == 1024 || keySize == 2048) {
RSA *rsa = RSA_generate_key(keySize, RSA_F4, NULL, NULL);
if (rsa) {
*privateKey = RSAPrivateKey_dup(rsa);
*publicKey = RSAPublicKey_dup(rsa);
if (*privateKey && *publicKey) {
return YES;
}
}
}
return NO;
}

/**
公钥加密
*/
+ (NSString *)opensslEncryptWithPublicKey:(RSA *)publicKey plainString:(NSString *)plainString {
// 参数转data
NSData *plainData = [plainString dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData *encryptData = [NSMutableData data];
// 分段加密,计算分段长度
int cipherBufferSize = RSA_size(publicKey);
NSUInteger totalSize = plainData.length;

int blockSize = cipherBufferSize - RSA_PKCS1_PADDING_SIZE;
int blockCount = ceil(totalSize * 1.f / blockSize);

// 遍历获取每个分段数据
for (int i = 0; i < blockCount; i++) {
// 取出当前分段数据
NSData *blockData;
if (i < (blockCount - 1)) {
blockData = [plainData subdataWithRange:NSMakeRange(i * blockSize, blockSize)];
} else {
blockData = [plainData subdataWithRange:NSMakeRange(i * blockSize, totalSize - i * blockSize)];
}
// 申请对应内存,公钥长度
uint8_t *cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
memset((void *)cipherBuffer, 0x0, cipherBufferSize);
// 加密当前分段数据
int result = RSA_public_encrypt((int)blockData.length, (const unsigned char *)blockData.bytes, (unsigned char *)cipherBuffer, publicKey, RSA_PKCS1_PADDING);
if (result < 0) {
// 加密失败,释放内存
if (cipherBuffer) {
free(cipherBuffer);
}
return nil;
}
NSData *cipher = [[NSData alloc] initWithBytes:cipherBuffer length:result];
[encryptData appendData:cipher];
if (cipherBuffer) {
free(cipherBuffer);
}
}
// 转base64
NSString *encryptString = [encryptData base64EncodedStringWithOptions:0];
return encryptString;
}

/**
私钥解密
*/
+ (NSString *)opensslDecryptWithPrivateKey:(RSA *)privateKey encryptString:(NSString *)encryptString {
// 参数转data
NSData *encryptData = [[NSData alloc] initWithBase64EncodedString:encryptString options:NSDataBase64DecodingIgnoreUnknownCharacters];
NSMutableData *outputPlainData = [NSMutableData data];
// 分段解密,计算分段长度
int cipherBufferSize = RSA_size(privateKey);
NSUInteger totalSize = encryptData.length;

int blockSize = cipherBufferSize;
int blockCount = ceil(totalSize * 1.f / blockSize);

// 遍历获取每个分段数据
for (int i = 0; i < blockCount; i++) {
// 取出当前分段数据
NSData *blockData;
if (i < (blockCount - 1)) {
blockData = [encryptData subdataWithRange:NSMakeRange(i * blockSize, blockSize)];
} else {
blockData = [encryptData subdataWithRange:NSMakeRange(i * blockSize, totalSize - i * blockSize)];
}
// 申请对应内存
uint8_t *keyBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
memset((void *)keyBuffer, 0x0, cipherBufferSize);
// 解密当前分段数据
int result = RSA_private_decrypt(cipherBufferSize, (const unsigned char *)blockData.bytes, (unsigned char *)keyBuffer, privateKey, RSA_PKCS1_PADDING);
if (result < 0) {
// 解密失败,释放内存
if (keyBuffer) {
free(keyBuffer);
}
return nil;
}
NSData *key = [[NSData alloc] initWithBytes:keyBuffer length:result];
[outputPlainData appendData:key];
if (keyBuffer) {
free(keyBuffer);
}
}
NSString *outputPlainString = [[NSString alloc] initWithData:outputPlainData encoding:NSUTF8StringEncoding];
return outputPlainString;
}

/**
私钥签名
*/
+ (NSString *)signWithPrivateKey:(RSA *)privateKey signType:(OpenSSL_RSA_SignType)signType plainString:(NSString *)plainString {
NSData *plainData = [plainString dataUsingEncoding:(NSUTF8StringEncoding)];

int type;
NSData *digestData = nil;
switch (signType) {
case OpenSSL_RSA_SignType_MD5:
{
unsigned char digest[CC_MD5_DIGEST_LENGTH];
CC_MD5([plainData bytes], (unsigned int)[plainData length], digest);
digestData = [NSData dataWithBytes:digest length:CC_MD5_DIGEST_LENGTH];
type = NID_md5;
}
break;
case OpenSSL_RSA_SignType_SHA1:
{
unsigned char digest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1([plainData bytes], (unsigned int)[plainData length], digest);
digestData = [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH];
type = NID_sha1;
}
break;
case OpenSSL_RSA_SignType_SHA224:
{
unsigned char digest[CC_SHA224_DIGEST_LENGTH];
CC_SHA224([plainData bytes], (unsigned int)[plainData length], digest);
digestData = [NSData dataWithBytes:digest length:CC_SHA224_DIGEST_LENGTH];
type = NID_sha224;
}
break;
case OpenSSL_RSA_SignType_SHA256:
{
unsigned char digest[CC_SHA256_DIGEST_LENGTH];
CC_SHA256([plainData bytes], (unsigned int)[plainData length], digest);
digestData = [NSData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH];
type = NID_sha256;
}
break;
case OpenSSL_RSA_SignType_SHA384:
{
unsigned char digest[CC_SHA384_DIGEST_LENGTH];
CC_SHA384([plainData bytes], (unsigned int)[plainData length], digest);
digestData = [NSData dataWithBytes:digest length:CC_SHA384_DIGEST_LENGTH];
type = NID_sha384;
}
break;
case OpenSSL_RSA_SignType_SHA512:
{
unsigned char digest[CC_SHA512_DIGEST_LENGTH];
CC_SHA512([plainData bytes], (unsigned int)[plainData length], digest);
digestData = [NSData dataWithBytes:digest length:CC_SHA512_DIGEST_LENGTH];
type = NID_sha512;
}
break;

default:
break;
}

if (digestData) {
int signedHashBytesSize = RSA_size(privateKey);

uint8_t *signedHashBytes = malloc(signedHashBytesSize * sizeof(uint8_t));
memset((void *)signedHashBytes, 0x0, signedHashBytesSize);


unsigned int siglen = 0;
int result = RSA_sign(type, (const unsigned char *)digestData.bytes, (unsigned int)digestData.length, (unsigned char *)signedHashBytes, &siglen, privateKey);
if (result == 1) {
NSData *signedHash = [NSData dataWithBytes:signedHashBytes length:siglen];
if (signedHashBytes) {
free(signedHashBytes);
}
NSString *signString = [signedHash base64EncodedStringWithOptions:0];
return signString;
}
if (signedHashBytes) {
free(signedHashBytes);
}
return nil;
} else {
return nil;
}
}

/**
公钥验证签名
*/
+ (BOOL)verifyWithPublicKey:(RSA *)publicKey signType:(OpenSSL_RSA_SignType)signType plainString:(NSString *)plainString signString:(NSString *)signString {

NSData *plainData = [plainString dataUsingEncoding:(NSUTF8StringEncoding)];
NSData *signData = [[NSData alloc] initWithBase64EncodedString:signString options:NSDataBase64DecodingIgnoreUnknownCharacters];

int type;
NSData *digestData = nil;
switch (signType) {
case OpenSSL_RSA_SignType_MD5:
{
unsigned char digest[CC_MD5_DIGEST_LENGTH];
CC_MD5([plainData bytes], (unsigned int)[plainData length], digest);
digestData = [NSData dataWithBytes:digest length:CC_MD5_DIGEST_LENGTH];
type = NID_md5;
}
break;
case OpenSSL_RSA_SignType_SHA1:
{
unsigned char digest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1([plainData bytes], (unsigned int)[plainData length], digest);
digestData = [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH];
type = NID_sha1;
}
break;
case OpenSSL_RSA_SignType_SHA224:
{
unsigned char digest[CC_SHA224_DIGEST_LENGTH];
CC_SHA224([plainData bytes], (unsigned int)[plainData length], digest);
digestData = [NSData dataWithBytes:digest length:CC_SHA224_DIGEST_LENGTH];
type = NID_sha224;
}
break;
case OpenSSL_RSA_SignType_SHA256:
{
unsigned char digest[CC_SHA256_DIGEST_LENGTH];
CC_SHA256([plainData bytes], (unsigned int)[plainData length], digest);
digestData = [NSData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH];
type = NID_sha256;
}
break;
case OpenSSL_RSA_SignType_SHA384:
{
unsigned char digest[CC_SHA384_DIGEST_LENGTH];
CC_SHA384([plainData bytes], (unsigned int)[plainData length], digest);
digestData = [NSData dataWithBytes:digest length:CC_SHA384_DIGEST_LENGTH];
type = NID_sha384;
}
break;
case OpenSSL_RSA_SignType_SHA512:
{
unsigned char digest[CC_SHA512_DIGEST_LENGTH];
CC_SHA512([plainData bytes], (unsigned int)[plainData length], digest);
digestData = [NSData dataWithBytes:digest length:CC_SHA512_DIGEST_LENGTH];
type = NID_sha512;
}
break;

default:
break;
}

if (digestData) {
int result = RSA_verify(type, (const unsigned char *)digestData.bytes, (unsigned int)digestData.length, (const unsigned char *)signData.bytes, (unsigned int)signData.length, publicKey);
if (result == 1) {
return YES;
}
return NO;
} else {
return NO;
}
}

/**
RSA转base64
*/
+ (NSString *)base64StringFromPublicRSAKey:(RSA *)rsaKey {
BIO *bio = BIO_new(BIO_s_mem());
PEM_write_bio_RSA_PUBKEY(bio, rsaKey);
BUF_MEM *buf;
BIO_get_mem_ptr(bio, &buf);
// data 后面可能会有有脏数据
NSString *pemString = [[NSString stringWithFormat:@"%s", buf->data] substringToIndex:buf->length];
BIO_set_close(bio, BIO_NOCLOSE);
BIO_free(bio);
NSString *base64 = [[pemString componentsSeparatedByString:@"-----"] objectAtIndex:2];
return base64;
}
+ (NSString *)base64StringFromPrivateRSAKey:(RSA *)rsaKey {
BIO *bio = BIO_new(BIO_s_mem());
PEM_write_bio_RSAPrivateKey(bio, rsaKey, NULL, NULL, 0, NULL, NULL);
BUF_MEM *buf;
BIO_get_mem_ptr(bio, &buf);
// data 后面可能会有有脏数据
NSString *pemString = [[NSString stringWithFormat:@"%s", buf->data] substringToIndex:buf->length];
BIO_set_close(bio, BIO_NOCLOSE);
BIO_free(bio);
NSString *base64 = [[pemString componentsSeparatedByString:@"-----"] objectAtIndex:2];
return base64;
}

/**
base64转RSA秘钥
*/
+ (RSA *)publicRSAKeyFromBase64String:(NSString *)keyString {
NSMutableString *result = [NSMutableString string];
[result appendString:@"-----BEGIN PUBLIC KEY-----\n"];
int count = 0;
for (int i = 0; i < keyString.length; i++) {
unichar c = [keyString characterAtIndex:i];
if (c == '\n' || c == '\r') {
continue;
}
[result appendFormat:@"%c", c];
count++;
if (count == 64) {
[result appendString:@"\n"];
count = 0;
}
}
[result appendString:@"\n-----END PUBLIC KEY-----"];

const char *buffer = [result UTF8String];
BIO *bio = BIO_new_mem_buf(buffer, (int)strlen(buffer));
RSA *publicKey = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL);
BIO_free_all(bio);
return publicKey;
}

+ (RSA *)privateRSAKeyFromBase64String:(NSString *)keyString {
NSMutableString *result = [NSMutableString string];
[result appendString:@"-----BEGIN RSA PRIVATE KEY-----\n"];
int count = 0;
for (int i = 0; i < keyString.length; i++) {
unichar c = [keyString characterAtIndex:i];
if (c == '\n' || c == '\r') {
continue;
}
[result appendFormat:@"%c", c];
count++;
if (count == 64) {
[result appendString:@"\n"];
count = 0;
}
}
[result appendString:@"\n-----END RSA PRIVATE KEY-----"];

const char *buffer = [result UTF8String];
BIO *bio = BIO_new_mem_buf(buffer, (int)strlen(buffer));
RSA *privateKey = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
BIO_free_all(bio);
return privateKey;
}

@end

方法调用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#import "ViewController.h"
#import "OpenSSLWrapper.h"

@interface ViewController () {
RSA *_publicKey;
RSA *_privateKey;
}
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.

_publicKey = NULL;
_privateKey = NULL;
[OpenSSLWrapper generateKeyPairWithKeySize:1024 publicKey:&_publicKey privateKey:&_privateKey];
NSString *publicKeyBase64 = [OpenSSLWrapper base64StringFromPublicRSAKey:_publicKey];
NSLog(@"公钥:%@", publicKeyBase64);
NSString *privateKeyBase64 = [OpenSSLWrapper base64StringFromPrivateRSAKey:_privateKey];
NSLog(@"私钥:%@", privateKeyBase64);

NSString *plainString = @"123qweQWE测试@#%&*";

NSString *encryptedString = [OpenSSLWrapper opensslEncryptWithPublicKey:_publicKey plainString:plainString];
NSLog(@"%@", [NSString stringWithFormat:@"公钥加密:%@", encryptedString]);

NSString *outputPlainString = [OpenSSLWrapper opensslDecryptWithPrivateKey:_privateKey encryptString:encryptedString];
NSLog(@"%@", [NSString stringWithFormat:@"私钥解密:%@", outputPlainString]);

NSString *signString = [OpenSSLWrapper signWithPrivateKey:_privateKey signType:(OpenSSL_RSA_SignType_SHA1) plainString:plainString];
NSLog(@"%@", [NSString stringWithFormat:@"私钥签名:%@", signString]);

BOOL verify = [OpenSSLWrapper verifyWithPublicKey:_publicKey signType:(OpenSSL_RSA_SignType_SHA1) plainString:plainString signString:signString];
NSLog(@"%@", [NSString stringWithFormat:@"验证 %@", verify ? @"通过" : @"不通过"]);
}

@end
2、苹果官方Security

苹果官方Security库也集成了相应的方法,在iOS 2中便已经提供,官网也提供了调用示例代码,苹果系统打包和发布均采用了RSA的操作

注:部分新的方法在`iOS 10.0`系统后提供
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#import <Foundation/Foundation.h>
#import <Security/Security.h>

NS_ASSUME_NONNULL_BEGIN

@interface SecKeyWrapper : NSObject <NSCopying, NSMutableCopying>

/**
单例方法
*/
+ (instancetype)shareInstance;

/**
生成秘钥

@param keySize 512、1024、2048
@param publicKeyRef 公钥内存地址
@param privateKeyRef 公钥内存地址
@return 是否成功生成
*/
- (BOOL)generateKeyPair:(NSUInteger)keySize publicKey:(SecKeyRef _Nullable *_Nonnull)publicKeyRef privateKey:(SecKeyRef _Nullable *_Nonnull)privateKeyRef;

/**
公钥加密

@param publicKey 公钥
@param plainString 源数据
@return 加密后数据
*/
- (NSString *)secKeyEncryptWithPublicKey:(SecKeyRef)publicKey plainString:(NSString *)plainString;

/**
私钥解密

@param privateKey 私钥
@param encryptString 加密后数据
@return 源数据
*/
- (NSString *)secKeyDecryptWithPrivateKey:(SecKeyRef)privateKey encryptString:(NSString *)encryptString;

/**
私钥签名

@param privateKey 私钥
@param secPadding 算法类型
@param plainString 源数据
@return 签名后数据
*/
- (NSString *)signWithPrivateSecKey:(SecKeyRef)privateKey secPadding:(SecPadding)secPadding plainString:(NSString *)plainString;

/**
公钥验证签名

@param publicKey 公钥
@param secPadding 算法类型
@param plainString 源数据
@param signString 签名后数据
@return 是否验证成功
*/
- (BOOL)verifyWithPublicSecKey:(SecKeyRef)publicKey secPadding:(SecPadding)secPadding plainString:(NSString *)plainString signString:(NSString *)signString;

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0

/**
SecKeyRef转base64
*/
- (NSString *)base64StringFromSecKey:(SecKeyRef)secKey;

/**
base64转SecKeyRef
*/
- (SecKeyRef)publicSecKeyFromBase64String:(NSString *)keyString;
- (SecKeyRef)privateSecKeyFromBase64String:(NSString *)keyString;

#endif

@end

NS_ASSUME_NONNULL_END
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
#import "SecKeyWrapper.h"
#import <CommonCrypto/CommonDigest.h>
#import <CommonCrypto/CommonCryptor.h>
#import <Security/SecKey.h>

@interface SecKeyWrapper () {
NSData *_publicTag;
NSData *_privateTag;
}

@end

@implementation SecKeyWrapper

static SecKeyWrapper *instance = nil;


+ (instancetype)shareInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[super allocWithZone:NULL] init];
});
return instance;
}

- (instancetype)init {
self = [super init];
if (self) {
// 单例的内部逻辑
// 初始化tag标记,此字符串长度固定,内容可更改
_privateTag = [@"com.ghtest.rsasecprivatekey" dataUsingEncoding:NSUTF8StringEncoding];
_publicTag = [@"com.ghtest.rsasecpublickey" dataUsingEncoding:NSUTF8StringEncoding];

}
return self;
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone {
return [SecKeyWrapper shareInstance];
}

- (id)copyWithZone:(NSZone *)zone {
return [SecKeyWrapper shareInstance];
}

- (id)mutableCopyWithZone:(NSZone *)zone {
return [SecKeyWrapper shareInstance];
}

/**
生成秘钥
*/
- (BOOL)generateKeyPair:(NSUInteger)keySize publicKey:(SecKeyRef _Nullable *_Nonnull)publicKeyRef privateKey:(SecKeyRef _Nullable *_Nonnull)privateKeyRef {

if (keySize == 512 || keySize == 1024 || keySize == 2048) {
// 首先删除旧的秘钥
OSStatus sanityCheck = noErr;
NSMutableDictionary *queryPublicKey = [[NSMutableDictionary alloc] init];
NSMutableDictionary *queryPrivateKey = [[NSMutableDictionary alloc] init];
// 设置公钥队列
[queryPublicKey setObject:(id)kSecClassKey forKey:(id)kSecClass];
[queryPublicKey setObject:_publicTag forKey:(id)kSecAttrApplicationTag];
[queryPublicKey setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
// 设置私钥队列
[queryPrivateKey setObject:(id)kSecClassKey forKey:(id)kSecClass];
[queryPrivateKey setObject:_privateTag forKey:(id)kSecAttrApplicationTag];
[queryPrivateKey setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];

// 删除私钥
sanityCheck = SecItemDelete((CFDictionaryRef)queryPrivateKey);
if (sanityCheck == noErr || sanityCheck == errSecItemNotFound) {

} else {
NSLog(@"Error removing private key, OSStatus == %d.", sanityCheck);
}
// 删除公钥
sanityCheck = SecItemDelete((CFDictionaryRef)queryPublicKey);
if (sanityCheck == noErr || sanityCheck == errSecItemNotFound) {

} else {
NSLog(@"Error removing public key, OSStatus == %d.", sanityCheck);
}

// Container dictionaries.
NSMutableDictionary *keyPairAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary *publicKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary *privateKeyAttr = [[NSMutableDictionary alloc] init];
// Set top level dictionary for the keypair.
[keyPairAttr setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
[keyPairAttr setObject:[NSNumber numberWithUnsignedInteger:keySize] forKey:(id)kSecAttrKeySizeInBits];
// 公钥字典,其他参数SecKey.h
[publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecAttrIsPermanent];
[publicKeyAttr setObject:_publicTag forKey:(id)kSecAttrApplicationTag];
// 私钥字典,其他参数SecKey.h
[privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecAttrIsPermanent];
[privateKeyAttr setObject:_privateTag forKey:(id)kSecAttrApplicationTag];
// Set attributes to top level dictionary.
[keyPairAttr setObject:publicKeyAttr forKey:(id)kSecPublicKeyAttrs];
[keyPairAttr setObject:privateKeyAttr forKey:(id)kSecPrivateKeyAttrs];

// 同步方法生成秘钥
OSStatus status = noErr;
status = SecKeyGeneratePair((CFDictionaryRef)keyPairAttr, publicKeyRef, privateKeyRef);
if (status == noErr && publicKeyRef != NULL && privateKeyRef != NULL) {
return YES;
} else {
NSLog(@"Something really bad went wrong with generating the key pair.");
}
}
return NO;
}

/**
公钥加密
*/
- (NSString *)secKeyEncryptWithPublicKey:(SecKeyRef)publicKey plainString:(NSString *)plainString {
// 原数据
NSData *plainData = [plainString dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData *encryptData = [NSMutableData data];
// 循环分段加密,每次最多加密公钥长度,其中还需要kSecPaddingPKCS1填充,-11位
size_t cipherBufferSize = SecKeyGetBlockSize(publicKey);
NSUInteger totalSize = plainData.length;
// 分段数量
NSUInteger blockSize = cipherBufferSize - 11;
double blockCount = ceil(totalSize * 1.f / blockSize);
for (NSInteger i = 0; i < blockCount; i++) {
// 取出当前分段数据
NSData *blockData;
if (i < (blockCount - 1)) {
blockData = [plainData subdataWithRange:NSMakeRange(i * blockSize, blockSize)];
} else {
blockData = [plainData subdataWithRange:NSMakeRange(i * blockSize, totalSize - i * blockSize)];
}
// 申请缓冲区内存,使用公钥长度申请
uint8_t *cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
memset((void *)cipherBuffer, 0x0, cipherBufferSize);
// 加密
OSStatus sanityCheck = noErr;
sanityCheck = SecKeyEncrypt(publicKey, kSecPaddingPKCS1, (const uint8_t *)[blockData bytes], [blockData length], cipherBuffer, &cipherBufferSize);
if (sanityCheck == noErr) {
// Build up cipher text blob.
NSData *cipher = [NSData dataWithBytes:(const void *)cipherBuffer length:(NSUInteger)cipherBufferSize];
[encryptData appendData:cipher];
if (cipherBuffer) {
free(cipherBuffer);
}
} else {
// 加密失败
if (cipherBuffer) {
free(cipherBuffer);
}
return nil;
}
}
// 转base64
NSString *encryptString = [encryptData base64EncodedStringWithOptions:0];
return encryptString;
}

/**
私钥解密
*/
- (NSString *)secKeyDecryptWithPrivateKey:(SecKeyRef)privateKey encryptString:(NSString *)encryptString {
// 原数据
NSData *encryptData = [[NSData alloc] initWithBase64EncodedString:encryptString options:NSDataBase64DecodingIgnoreUnknownCharacters];
NSMutableData *outputPlainData = [NSMutableData data];
// 循环分段解密,每次最多解密私钥长度
size_t cipherBufferSize = SecKeyGetBlockSize(privateKey);
NSUInteger totalSize = encryptData.length;
// 分段数量
NSUInteger blockSize = cipherBufferSize;
double blockCount = ceil(totalSize * 1.f / cipherBufferSize);
for (int i = 0; i < blockCount; i++) {
// 取出当前分段数据
NSData *blockData;
if (i < (blockCount - 1)) {
blockData = [encryptData subdataWithRange:NSMakeRange(i * blockSize, blockSize)];
} else {
blockData = [encryptData subdataWithRange:NSMakeRange(i * blockSize, totalSize - i * blockSize)];
}
// 申请当前分段加密占用内存
uint8_t *keyBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
memset((void *)keyBuffer, 0x0, cipherBufferSize);
// 解密
OSStatus status = noErr;
status = SecKeyDecrypt(privateKey, kSecPaddingPKCS1, (const uint8_t *)[blockData bytes], [blockData length], keyBuffer, &cipherBufferSize);
if (status == noErr) {
NSData *key = [NSData dataWithBytes:(const void *)keyBuffer length:(NSUInteger)cipherBufferSize];
[outputPlainData appendData:key];
if (keyBuffer) {
free(keyBuffer);
}
} else {
if (keyBuffer) {
free(keyBuffer);
}
return nil;
}
}

NSString *outputPlainString = [[NSString alloc] initWithData:outputPlainData encoding:NSUTF8StringEncoding];
return outputPlainString;
}

/**
私钥签名
*/
- (NSString *)signWithPrivateSecKey:(SecKeyRef)privateKey secPadding:(SecPadding)secPadding plainString:(NSString *)plainString {
#if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_10_0
// 原数据
NSData *plainData = [plainString dataUsingEncoding:NSUTF8StringEncoding];
// 哈希值算法,md5、sha1、sha224、sha256、sha384、sha512
NSData *hashData = nil;
NSInteger digest_length = 0;
switch (secPadding) {
case kSecPaddingPKCS1MD5:
{
digest_length = CC_MD5_DIGEST_LENGTH;
// Malloc a buffer to hold hash.
uint8_t *hashBytes = malloc(CC_MD5_DIGEST_LENGTH * sizeof(uint8_t));
memset((void *)hashBytes, 0x0, CC_MD5_DIGEST_LENGTH);
// Initialize the context.
CC_MD5_CTX ctx;
CC_MD5_Init(&ctx);
// Perform the hash.
CC_MD5_Update(&ctx, (void *)[plainData bytes], (CC_LONG)[plainData length]);
// Finalize the output.
CC_MD5_Final(hashBytes, &ctx);
// Build up the SHAMD5 blob.
hashData = [NSData dataWithBytes:(const void *)hashBytes length:(NSUInteger)CC_MD5_DIGEST_LENGTH];
if (hashBytes) {
free(hashBytes);
}
}
break;
case kSecPaddingPKCS1SHA1:
{
digest_length = CC_SHA1_DIGEST_LENGTH;
// Malloc a buffer to hold hash.
uint8_t *hashBytes = malloc(CC_SHA1_DIGEST_LENGTH * sizeof(uint8_t));
memset((void *)hashBytes, 0x0, CC_SHA1_DIGEST_LENGTH);
// Initialize the context.
CC_SHA1_CTX ctx;
CC_SHA1_Init(&ctx);
// Perform the hash.
CC_SHA1_Update(&ctx, (void *)[plainData bytes], (CC_LONG)[plainData length]);
// Finalize the output.
CC_SHA1_Final(hashBytes, &ctx);
// Build up the SHA1 blob.
hashData = [NSData dataWithBytes:(const void *)hashBytes length:(NSUInteger)CC_SHA1_DIGEST_LENGTH];
if (hashBytes) {
free(hashBytes);
}
}
break;
case kSecPaddingPKCS1SHA224:
{
digest_length = CC_SHA224_DIGEST_LENGTH;
// Malloc a buffer to hold hash.
uint8_t *hashBytes = malloc(CC_SHA224_DIGEST_LENGTH * sizeof(uint8_t));
memset((void *)hashBytes, 0x0, CC_SHA224_DIGEST_LENGTH);
// Initialize the context.
CC_SHA256_CTX ctx;
CC_SHA224_Init(&ctx);
// Perform the hash.
CC_SHA224_Update(&ctx, (void *)[plainData bytes], (CC_LONG)[plainData length]);
// Finalize the output.
CC_SHA224_Final(hashBytes, &ctx);
// Build up the SHA224 blob.
hashData = [NSData dataWithBytes:(const void *)hashBytes length:(NSUInteger)CC_SHA224_DIGEST_LENGTH];
if (hashBytes) {
free(hashBytes);
}
}
break;
case kSecPaddingPKCS1SHA256:
{
digest_length = CC_SHA256_DIGEST_LENGTH;
// Malloc a buffer to hold hash.
uint8_t *hashBytes = malloc(CC_SHA256_DIGEST_LENGTH * sizeof(uint8_t));
memset((void *)hashBytes, 0x0, CC_SHA256_DIGEST_LENGTH);
// Initialize the context.
CC_SHA256_CTX ctx;
CC_SHA256_Init(&ctx);
// Perform the hash.
CC_SHA256_Update(&ctx, (void *)[plainData bytes], (CC_LONG)[plainData length]);
// Finalize the output.
CC_SHA256_Final(hashBytes, &ctx);
// Build up the SHA256 blob.
hashData = [NSData dataWithBytes:(const void *)hashBytes length:(NSUInteger)CC_SHA256_DIGEST_LENGTH];
if (hashBytes) {
free(hashBytes);
}
}
break;
case kSecPaddingPKCS1SHA384:
{
digest_length = CC_SHA384_DIGEST_LENGTH;
// Malloc a buffer to hold hash.
uint8_t *hashBytes = malloc(CC_SHA384_DIGEST_LENGTH * sizeof(uint8_t));
memset((void *)hashBytes, 0x0, CC_SHA384_DIGEST_LENGTH);
// Initialize the context.
CC_SHA512_CTX ctx;
CC_SHA384_Init(&ctx);
// Perform the hash.
CC_SHA384_Update(&ctx, (void *)[plainData bytes], (CC_LONG)[plainData length]);
// Finalize the output.
CC_SHA384_Final(hashBytes, &ctx);
// Build up the SHA384 blob.
hashData = [NSData dataWithBytes:(const void *)hashBytes length:(NSUInteger)CC_SHA384_DIGEST_LENGTH];
if (hashBytes) {
free(hashBytes);
}
}
break;
case kSecPaddingPKCS1SHA512:
{
digest_length = CC_SHA512_DIGEST_LENGTH;
// Malloc a buffer to hold hash.
uint8_t *hashBytes = malloc(CC_SHA512_DIGEST_LENGTH * sizeof(uint8_t));
memset((void *)hashBytes, 0x0, CC_SHA512_DIGEST_LENGTH);
// Initialize the context.
CC_SHA512_CTX ctx;
CC_SHA512_Init(&ctx);
// Perform the hash.
CC_SHA512_Update(&ctx, (void *)[plainData bytes], (CC_LONG)[plainData length]);
// Finalize the output.
CC_SHA512_Final(hashBytes, &ctx);
// Build up the SHA512 blob.
hashData = [NSData dataWithBytes:(const void *)hashBytes length:(NSUInteger)CC_SHA512_DIGEST_LENGTH];
if (hashBytes) {
free(hashBytes);
}
}
break;

default:
break;
}
if (hashData) {
size_t signedHashBytesSize = SecKeyGetBlockSize(privateKey);
// Malloc a buffer to hold signature.
uint8_t *signedHashBytes = malloc(signedHashBytesSize * sizeof(uint8_t));
memset((void *)signedHashBytes, 0x0, signedHashBytesSize);
// Sign the hash Data.
OSStatus sanityCheck = noErr;
sanityCheck = SecKeyRawSign(privateKey, secPadding, (const uint8_t *)[hashData bytes], digest_length, (uint8_t *)signedHashBytes, &signedHashBytesSize);
if (sanityCheck == noErr) {
// Build up signed blob.
NSData *signedHash = [NSData dataWithBytes:(const void *)signedHashBytes length:(NSUInteger)signedHashBytesSize];
if (signedHashBytes) {
free(signedHashBytes);
}
NSString *signString = [signedHash base64EncodedStringWithOptions:0];
return signString;
} else {
if (signedHashBytes) {
free(signedHashBytes);
}
return nil;
}
} else {
return nil;
}
#else
// iOS10以上系统可以使用新的方法
// 原数据
NSData *plainData = [plainString dataUsingEncoding:NSUTF8StringEncoding];
SecKeyAlgorithm Algorithm = nil;
switch (secPadding) {
case kSecPaddingPKCS1SHA1:
{
Algorithm = kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA1;
}
break;
case kSecPaddingPKCS1SHA224:
{
Algorithm = kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA224;
}
break;
case kSecPaddingPKCS1SHA256:
{
Algorithm = kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA256;
}
break;
case kSecPaddingPKCS1SHA384:
{
Algorithm = kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA384;
}
break;
case kSecPaddingPKCS1SHA512:
{
Algorithm = kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA512;
}
break;

default:
break;
}
if (Algorithm) {
NSError *error = nil;
CFErrorRef ee = (__bridge CFErrorRef)error;
CFDataRef dataRef = SecKeyCreateSignature(privateKey, kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA1, (CFDataRef)plainData, &ee);
if (error) {
return nil;
} else {
NSData *signature = CFBridgingRelease(dataRef);
NSString *signString = [signature base64EncodedStringWithOptions:0];
return signString;
}
} else {
return nil;
}
#endif
}

/**
公钥验证签名
*/
- (BOOL)verifyWithPublicSecKey:(SecKeyRef)publicKey secPadding:(SecPadding)secPadding plainString:(NSString *)plainString signString:(NSString *)signString {
#if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_10_0
// 原数据
NSData *plainData = [plainString dataUsingEncoding:NSUTF8StringEncoding];
// 哈希值算法,md5、sha1、sha224、sha256、sha384、sha512
NSData *hashData = nil;
NSInteger digest_length = 0;
switch (secPadding) {
case kSecPaddingPKCS1MD5:
{
digest_length = CC_MD5_DIGEST_LENGTH;
// Malloc a buffer to hold hash.
uint8_t *hashBytes = malloc(CC_MD5_DIGEST_LENGTH * sizeof(uint8_t));
memset((void *)hashBytes, 0x0, CC_MD5_DIGEST_LENGTH);
// Initialize the context.
CC_MD5_CTX ctx;
CC_MD5_Init(&ctx);
// Perform the hash.
CC_MD5_Update(&ctx, (void *)[plainData bytes], (CC_LONG)[plainData length]);
// Finalize the output.
CC_MD5_Final(hashBytes, &ctx);
// Build up the SHAMD5 blob.
hashData = [NSData dataWithBytes:(const void *)hashBytes length:(NSUInteger)CC_MD5_DIGEST_LENGTH];
if (hashBytes) {
free(hashBytes);
}
}
break;
case kSecPaddingPKCS1SHA1:
{
digest_length = CC_SHA1_DIGEST_LENGTH;
// Malloc a buffer to hold hash.
uint8_t *hashBytes = malloc(CC_SHA1_DIGEST_LENGTH * sizeof(uint8_t));
memset((void *)hashBytes, 0x0, CC_SHA1_DIGEST_LENGTH);
// Initialize the context.
CC_SHA1_CTX ctx;
CC_SHA1_Init(&ctx);
// Perform the hash.
CC_SHA1_Update(&ctx, (void *)[plainData bytes], (CC_LONG)[plainData length]);
// Finalize the output.
CC_SHA1_Final(hashBytes, &ctx);
// Build up the SHA1 blob.
hashData = [NSData dataWithBytes:(const void *)hashBytes length:(NSUInteger)CC_SHA1_DIGEST_LENGTH];
if (hashBytes) {
free(hashBytes);
}
}
break;
case kSecPaddingPKCS1SHA224:
{
digest_length = CC_SHA224_DIGEST_LENGTH;
// Malloc a buffer to hold hash.
uint8_t *hashBytes = malloc(CC_SHA224_DIGEST_LENGTH * sizeof(uint8_t));
memset((void *)hashBytes, 0x0, CC_SHA224_DIGEST_LENGTH);
// Initialize the context.
CC_SHA256_CTX ctx;
CC_SHA224_Init(&ctx);
// Perform the hash.
CC_SHA224_Update(&ctx, (void *)[plainData bytes], (CC_LONG)[plainData length]);
// Finalize the output.
CC_SHA224_Final(hashBytes, &ctx);
// Build up the SHA224 blob.
hashData = [NSData dataWithBytes:(const void *)hashBytes length:(NSUInteger)CC_SHA224_DIGEST_LENGTH];
if (hashBytes) {
free(hashBytes);
}
}
break;
case kSecPaddingPKCS1SHA256:
{
digest_length = CC_SHA256_DIGEST_LENGTH;
// Malloc a buffer to hold hash.
uint8_t *hashBytes = malloc(CC_SHA256_DIGEST_LENGTH * sizeof(uint8_t));
memset((void *)hashBytes, 0x0, CC_SHA256_DIGEST_LENGTH);
// Initialize the context.
CC_SHA256_CTX ctx;
CC_SHA256_Init(&ctx);
// Perform the hash.
CC_SHA256_Update(&ctx, (void *)[plainData bytes], (CC_LONG)[plainData length]);
// Finalize the output.
CC_SHA256_Final(hashBytes, &ctx);
// Build up the SHA256 blob.
hashData = [NSData dataWithBytes:(const void *)hashBytes length:(NSUInteger)CC_SHA256_DIGEST_LENGTH];
if (hashBytes) {
free(hashBytes);
}
}
break;
case kSecPaddingPKCS1SHA384:
{
digest_length = CC_SHA384_DIGEST_LENGTH;
// Malloc a buffer to hold hash.
uint8_t *hashBytes = malloc(CC_SHA384_DIGEST_LENGTH * sizeof(uint8_t));
memset((void *)hashBytes, 0x0, CC_SHA384_DIGEST_LENGTH);
// Initialize the context.
CC_SHA512_CTX ctx;
CC_SHA384_Init(&ctx);
// Perform the hash.
CC_SHA384_Update(&ctx, (void *)[plainData bytes], (CC_LONG)[plainData length]);
// Finalize the output.
CC_SHA384_Final(hashBytes, &ctx);
// Build up the SHA384 blob.
hashData = [NSData dataWithBytes:(const void *)hashBytes length:(NSUInteger)CC_SHA384_DIGEST_LENGTH];
if (hashBytes) {
free(hashBytes);
}
}
break;
case kSecPaddingPKCS1SHA512:
{
digest_length = CC_SHA512_DIGEST_LENGTH;
// Malloc a buffer to hold hash.
uint8_t *hashBytes = malloc(CC_SHA512_DIGEST_LENGTH * sizeof(uint8_t));
memset((void *)hashBytes, 0x0, CC_SHA512_DIGEST_LENGTH);
// Initialize the context.
CC_SHA512_CTX ctx;
CC_SHA512_Init(&ctx);
// Perform the hash.
CC_SHA512_Update(&ctx, (void *)[plainData bytes], (CC_LONG)[plainData length]);
// Finalize the output.
CC_SHA512_Final(hashBytes, &ctx);
// Build up the SHA512 blob.
hashData = [NSData dataWithBytes:(const void *)hashBytes length:(NSUInteger)CC_SHA512_DIGEST_LENGTH];
if (hashBytes) {
free(hashBytes);
}
}
break;

default:
break;
}
if (hashData) {
// 签名数据
NSData *signData = [[NSData alloc] initWithBase64EncodedString:signString options:NSDataBase64DecodingIgnoreUnknownCharacters];
// Get the size of the assymetric block.
size_t signedHashBytesSize = SecKeyGetBlockSize(publicKey);
OSStatus sanityCheck = noErr;
sanityCheck = SecKeyRawVerify(publicKey, secPadding, (const uint8_t *)[hashData bytes], digest_length, (const uint8_t *)[signData bytes], signedHashBytesSize);
if (sanityCheck == noErr) {
return YES;
} else {
return NO;
}
} else {
return NO;
}
#else
// iOS10以上系统可以使用新的方法
// 原数据
NSData *plainData = [plainString dataUsingEncoding:NSUTF8StringEncoding];
// 签名数据
NSData *signData = [[NSData alloc] initWithBase64EncodedString:signString options:NSDataBase64DecodingIgnoreUnknownCharacters];
SecKeyAlgorithm Algorithm = nil;
switch (secPadding) {
case kSecPaddingPKCS1SHA1:
{
Algorithm = kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA1;
}
break;
case kSecPaddingPKCS1SHA224:
{
Algorithm = kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA224;
}
break;
case kSecPaddingPKCS1SHA256:
{
Algorithm = kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA256;
}
break;
case kSecPaddingPKCS1SHA384:
{
Algorithm = kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA384;
}
break;
case kSecPaddingPKCS1SHA512:
{
Algorithm = kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA512;
}
break;

default:
break;
}
if (Algorithm) {
NSError *error = nil;
CFErrorRef ee = (__bridge CFErrorRef)error;
Boolean boolen = SecKeyVerifySignature(publicKey, Algorithm, (CFDataRef)plainData, (CFDataRef)signData, &ee);
if (error) {
return NO;
} else {
if (boolen == TRUE) {
return YES;
} else {
return NO;
}
}
} else {
return NO;
}
#endif
}

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0

// SecKeyRef转base64
- (NSString *)base64StringFromSecKey:(SecKeyRef)secKey {
NSData *keyData = (NSData *)CFBridgingRelease(SecKeyCopyExternalRepresentation(secKey, NULL));
NSString *base64 = [keyData base64EncodedStringWithOptions:0];
return base64;
}

// base64转SecKeyRef
- (SecKeyRef)publicSecKeyFromBase64String:(NSString *)keyString {
NSData *keyData = [[NSData alloc] initWithBase64EncodedString:keyString options:NSDataBase64DecodingIgnoreUnknownCharacters];

NSMutableDictionary *options = [NSMutableDictionary dictionary];
options[(__bridge id)kSecAttrKeyType] = (__bridge id) kSecAttrKeyTypeRSA;
options[(__bridge id)kSecAttrKeyClass] = (__bridge id) kSecAttrKeyClassPublic;

NSError *error = nil;
CFErrorRef ee = (__bridge CFErrorRef)error;
SecKeyRef ref = SecKeyCreateWithData((__bridge CFDataRef)keyData, (__bridge CFDictionaryRef)options, &ee);
if (error) {
NSLog(@"%@", error.description);
return nil;
}
return ref;
}

- (SecKeyRef)privateSecKeyFromBase64String:(NSString *)keyString {
NSData *keyData = [[NSData alloc] initWithBase64EncodedString:keyString options:NSDataBase64DecodingIgnoreUnknownCharacters];

NSMutableDictionary *options = [NSMutableDictionary dictionary];
options[(__bridge id)kSecAttrKeyType] = (__bridge id) kSecAttrKeyTypeRSA;
options[(__bridge id)kSecAttrKeyClass] = (__bridge id) kSecAttrKeyClassPrivate;

NSError *error = nil;
CFErrorRef ee = (__bridge CFErrorRef)error;
SecKeyRef ref = SecKeyCreateWithData((__bridge CFDataRef)keyData, (__bridge CFDictionaryRef)options, &ee);
if (error) {
NSLog(@"%@", error.description);
return nil;
}
return ref;
}

#endif

@end

调用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#import "ViewController.h"
#import "SecKeyWrapper.h"

@interface ViewController () {
SecKeyRef _publicKeyRef;
SecKeyRef _privateKeyRef;
}
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.

_publicKeyRef = NULL;
_privateKeyRef = NULL;
SecKeyWrapper *secKeyWrapper = [SecKeyWrapper shareInstance];
[secKeyWrapper generateKeyPair:1024 publicKey:&_publicKeyRef privateKey:&_privateKeyRef];

NSString *publicKeyBase64 = [secKeyWrapper base64StringFromSecKey:_publicKeyRef];
NSLog(@"公钥:%@", publicKeyBase64);
NSString *privateKeyBase64 = [secKeyWrapper base64StringFromSecKey:_privateKeyRef];
NSLog(@"私钥:%@", privateKeyBase64);

NSString *plainString = @"123qweQWE测试@#%&*";

NSString *encryptedString = [secKeyWrapper secKeyEncryptWithPublicKey:_publicKeyRef plainString:plainString];
NSLog(@"%@", [NSString stringWithFormat:@"公钥加密:%@", encryptedString]);

NSString *outputPlainString = [secKeyWrapper secKeyDecryptWithPrivateKey:_privateKeyRef encryptString:encryptedString];
NSLog(@"%@", [NSString stringWithFormat:@"私钥解密:%@", outputPlainString]);

NSString *signString = [secKeyWrapper signWithPrivateSecKey:_privateKeyRef secPadding:kSecPaddingPKCS1SHA1 plainString:plainString];
NSLog(@"%@", [NSString stringWithFormat:@"私钥签名:%@", signString]);

BOOL verify = [secKeyWrapper verifyWithPublicSecKey:_publicKeyRef secPadding:kSecPaddingPKCS1SHA1 plainString:plainString signString:signString];
NSLog(@"%@", [NSString stringWithFormat:@"验证 %@", verify ? @"通过" : @"不通过"]);
}

@end
3、一些概念
  1. 公钥加密,私钥解密

对于二进制数据,加密时为了确定内容实际长度,所以在末尾需要用约定好的字符进行标记结尾,防止被内存中的垃圾数据等影响,这就是paddingPKCS1格式秘钥建议使用的padding为11字节

以1024位秘钥为例,公钥加密时支持最大字节数:证书位数/8-11=117,解密时支持最大字节数:证书位数/8=128

  1. 私钥签名,公钥验签

私钥签名,公钥验签是为了对数据进行验证,确保数据没有被篡改

需要使用哈希值算法获取原数据的哈希值,哈希值特点是唯一性,一旦原数据发生变化,哈希值也会变化,再使用私钥对哈希值进行摘要签名,通过公钥验证后即可确保数据的保密性

4、遗留的问题

在使用Security生成秘钥的过程中,发现一个问题:

使用Security生成公钥私钥,其中私钥转成base64字符串后,可以使用Openssl的方法读取私钥,并使用Openssl的方法进行其他操作,但是公钥长度格式不对,无法使用Openssl的方法读取使用,如果要发送公钥给第三方,只能用Openssl的方法读取到公钥并发送,正常使用过程中,Security的公钥只能给Security自己的方法使用,对加密解密的功能没有影响,暂时无法找到合理的解释。

另外,Security的部分方法在iOS 10.0后的系统才会生效,也没有找到以前以前系统中提供其他类似的方法,据网上资料了解,Openssl生成秘钥的方法有失败的可能,但是实际测试没有出现过,可能是测试数量不够多