RSA加密現(xiàn)在有很多項(xiàng)目中都會(huì)用到,網(wǎng)上資料很多,方法也不止一種,我這里整理了其中一種,希望對(duì)大家有所幫助
上圖:
說(shuō)明
1.本文使用的RSA相關(guān)操作是使用的三方庫(kù)openSSL
2.本文RSA使用的是1024位秘鑰加密(能滿足一般的需求,除非需要非常高的安全性)
相關(guān)知識(shí)
1.生成密文的長(zhǎng)度等于密鑰長(zhǎng)度(密鑰長(zhǎng)度越大仇冯,生成密文的長(zhǎng)度就越大,加密的速度就越慢,而密文也就越難被破解掉)诊杆。
2.不管明文長(zhǎng)度是多少牧氮,RSA生成的密文長(zhǎng)度總是固定的。
3.明文長(zhǎng)度不能超過(guò)密鑰長(zhǎng)度,否則就會(huì)出問(wèn)題(使用分段加密)岭佳。
4.RSA加密每次結(jié)果是不一樣的(加密時(shí)對(duì)加密內(nèi)容使用了長(zhǎng)度為11位的隨機(jī)填充)怪瓶。
5.RSA本身不支持中文(一般使用URL編碼來(lái)解決中文問(wèn)題)。
6.公鑰加密私鑰解密,私鑰加密公鑰解密
前期準(zhǔn)備:
1.公鑰/私鑰(一般應(yīng)該只會(huì)用到一個(gè),另外一個(gè)是后臺(tái)使用),這個(gè)一般是后臺(tái)給的,自己生成也很簡(jiǎn)單,可以去網(wǎng)站上生成也可以使用終端生成
2.openSSL三方庫(kù)(建議使用coocpods導(dǎo)入)
RSA加密解密
1.私鑰/公鑰格式化(需要把密鑰/私鑰按進(jìn)行特定的格式化才能進(jìn)行 加解密)
//0.2格式化key
+ (NSString *)formattKey:(NSString *)key
{
if (key == nil) return @"";
NSInteger count = key.length / 64;
NSMutableString *formattKey = key.mutableCopy;
for (int i = 0; i < count; i ++)
{
[formattKey insertString:@"\n" atIndex:64 + (64 + 1) * i];
}
return formattKey == nil ? @"" : formattKey;
}
//0.3寫(xiě)入key文件
+ (BOOL)writeKey:(NSString *)key type:(KeyType)type;
{
NSString*keyPath = [self RSAKeyFillePath:type];
if ([[NSFileManager defaultManager] fileExistsAtPath:keyPath])
{
return YES;
}
NSString *formattKey = [self formattKey:key];
NSString *keyStr;
switch (type)
{
case eKeyTypePublic:
{
keyStr = [NSString stringWithFormat:@"-----BEGIN PUBLIC KEY-----\n%@\n-----END PUBLIC KEY-----",formattKey];
break;
}
case eKeyTypePrivate:
{
keyStr = [NSString stringWithFormat:@"-----BEGIN RSA PRIVATE KEY-----\n%@\n-----END RSA PRIVATE KEY-----",formattKey];
break;
}
default:
{
return NO;
}
}
NSLog(@"格式話處理過(guò)的key:\n%@\n",keyStr);
NSError *error = nil;
return [keyStr writeToFile:keyPath atomically:YES encoding:NSASCIIStringEncoding error:&error];
}
格式化后的結(jié)果:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLyQGhLLE9+xtR5ZzPv1vMEOIv
YJxEcMbapskPqqlYewyXD5esHa/BZwQgMTPDMSFw4TuXOUWaM/Khelon8p4iKF4O
4m1Uc5AmEY+vM4gITg1KkmyuN2f43insnBWkMiV8dhfeIB/pCCAL1RExrBfehIVd
kwe3bemsNnowLIyOHwIDAQAB
-----END PUBLIC KEY-----
這里說(shuō)明一下,密鑰/公鑰文件可以存放到沙盒中,也可以打包到項(xiàng)目中,都可以,只要使用的時(shí)候能拿到文件就行
2.加密/解密,把密鑰/私鑰處理好之后就可以開(kāi)始加密/解密了(很簡(jiǎn)單,三步)
//1.導(dǎo)入key為RSA結(jié)構(gòu)體對(duì)象
- (BOOL)importRSAKey:(KeyType)type
{
// 這里是從項(xiàng)目中讀取公鑰和私鑰,你也可以把公鑰和私鑰存入沙河中,從沙河中讀取
switch (type)
{
case eKeyTypePublic:
{
FILE *file = fopen([[[NSBundle mainBundle] pathForResource:@"public_key.pem" ofType:nil] UTF8String], "rb");
if (file == NULL) return NO;
_publicRSA = PEM_read_RSA_PUBKEY(file, NULL, NULL, NULL);
assert(_publicRSA != nil);
fclose(file);
return (_publicRSA != NULL) ? YES : NO;
}
case eKeyTypePrivate:
{
FILE *file = fopen([[[NSBundle mainBundle] pathForResource:@"private_key.pem" ofType:nil] UTF8String], "rb");
if (file == NULL) return NO;
_privateRSA = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL);
assert(_privateRSA != NULL);
fclose(file);
return (_privateRSA != NULL) ? YES : NO;
}
default:
{
return NO;
}
}
}
//2.1加密處理
- (NSData *)encryptToData:(NSString*)content type:(KeyType)type
{
if([self RSAWithType:type] == NULL)
{
if (![self importRSAKey:type]) return nil;
}
RSA *rsa = [self RSAWithType:type];
int status = 0;
int length = (int)content.length;
unsigned char input[length + 1];
bzero(input, length + 1);
for (int i = 0; i < length; i++)
{
input[i] = [content characterAtIndex:i];
}
NSInteger flen = [self getBlockSize:eRSAPaddingTypePKCS1 type:type];
char *encryptData = (char*)malloc(flen);
bzero(encryptData, flen);
switch (type)
{
case eKeyTypePublic:
status = RSA_public_encrypt(length, (unsigned char*)input, (unsigned char*)encryptData, rsa, eRSAPaddingTypePKCS1);
break;
default:
status = RSA_private_encrypt(length, (unsigned char*)input, (unsigned char*)encryptData, rsa, eRSAPaddingTypePKCS1);
break;
}
if (status)
{
NSData *returnData = [NSData dataWithBytes:encryptData length:status];
free(encryptData);
encryptData = NULL;
return returnData;
}
free(encryptData);
encryptData = NULL;
return nil;
}
//2.2解密處理
- (NSString *)decryptToData:(NSString*)content type:(KeyType)type
{
if([self RSAWithType:type] == NULL)
{
if (![self importRSAKey:type]) return nil;
}
RSA *rsa = [self RSAWithType:type];
int status;
NSData *data = [content base64Decode];
int length = (int)data.length;
NSInteger flen = [self getBlockSize:eRSAPaddingTypePKCS1 type:type];
char *decryptData = (char*)malloc(flen);
bzero(decryptData, flen);
switch (type)
{
case eKeyTypePublic:
status = RSA_public_decrypt(length, (unsigned char*)[data bytes], (unsigned char*)decryptData, rsa, eRSAPaddingTypePKCS1);
break;
default:
status = RSA_private_decrypt(length, (unsigned char*)[data bytes], (unsigned char*)decryptData, rsa, eRSAPaddingTypePKCS1);
break;
}
if (status)
{
NSMutableString *decryptString = [[NSMutableString alloc] initWithBytes:decryptData length:strlen(decryptData) encoding:NSASCIIStringEncoding];
free(decryptData);
decryptData = NULL;
return decryptString;
}
free(decryptData);
decryptData = NULL;
return nil;
}
//3.1實(shí)現(xiàn)中文兼容和分段加密
- (NSString *)encrypt:(NSString *)content type:(KeyType)type
{
content = content.URLEncode;
NSMutableString *encryptContent = @"".mutableCopy;
for (NSInteger i = 0; i < ceilf(content.length / 117.0); i ++)
{
NSString *subStr = [content substringWithRange:NSMakeRange(i * 117, MIN(117, content.length - i * 117))];
NSString *encryptSubStr = [[self encryptToData:subStr type:type] base64Encode];
[encryptContent appendString:encryptSubStr];
}
return encryptContent;
}
//3.2實(shí)現(xiàn)中文兼容和分段解密
- (NSString *)decrypt:(NSString *)content type:(KeyType)type
{
NSMutableString *decryptResult = @"".mutableCopy;
for (NSInteger i = 0; i < ceilf(content.length / 117); i ++)
{
NSString *subStr = [content substringWithRange:NSMakeRange(i * 117, 117)];
NSString *decryptSubStr = [self decryptToData:subStr type:type];
NSString *decryptStr = decryptSubStr.length <= 117 ? decryptSubStr : [decryptSubStr substringToIndex:117];
[decryptResult appendString:decryptStr];
}
return decryptResult.URLDecode;
}
這里說(shuō)明一下:
1.使用openSSL需要把公鑰/私鑰先轉(zhuǎn)成RSA結(jié)構(gòu)對(duì)象,然后進(jìn)行加密解密
2.解決中文的問(wèn)題是使用url編碼來(lái)解決的,這個(gè)要跟后臺(tái)對(duì)接好就行,一般都會(huì)使用url編碼,當(dāng)然也可以使用其他的,只不過(guò)是做了個(gè)轉(zhuǎn)換而已
3.因?yàn)槊芪拈L(zhǎng)度不能大于密鑰長(zhǎng)度,所以一般會(huì)進(jìn)行分段加密,分段加密時(shí)密文控制是117,因?yàn)榧用軙r(shí)對(duì)密文使用了長(zhǎng)度為11的填充(隨機(jī)的),加起來(lái)就是128(1024位對(duì)應(yīng)的密鑰/密文長(zhǎng)度)
//長(zhǎng)度獲取
- (int)getBlockSize:(RSAPaddingType)type type:(KeyType)keyType
{
int len = RSA_size([self RSAWithType:keyType]);
if (type != eRSAPaddingTypeNone)
{
len -= 11;
}
return len;
}
好了,RSA加密和解密就可以使用了
本文Demo