iOS RSA加密与解密

RSA加密现在有很多项目中都会用到,网上资料很多,方法也不止一种,我这里整理了其中一种,希望对大家有所帮助

上图:

Untitled.gif

说明

1.本文使用的RSA相关操作是使用的三方库openSSL
2.本文RSA使用的是1024位秘钥加密(能满足一般的需求,除非需要非常高的安全性)

相关知识

1.生成密文的长度等于密钥长度(密钥长度越大,生成密文的长度就越大,加密的速度就越慢,而密文也就越难被破解掉)。
2.不管明文长度是多少,RSA生成的密文长度总是固定的。
3.明文长度不能超过密钥长度,否则就会出问题(使用分段加密)。
4.RSA加密每次结果是不一样的(加密时对加密内容使用了长度为11位的随机填充)。
5.RSA本身不支持中文(一般使用URL编码来解决中文问题)。
6.公钥加密私钥解密,私钥加密公钥解密

前期准备:

1.公钥/私钥(一般应该只会用到一个,另外一个是后台使用),这个一般是后台给的,自己生成也很简单,可以去网站上生成也可以使用终端生成
2.openSSL三方库(建议使用coocpods导入)

RSA加密解密

1.私钥/公钥格式化(需要把密钥/私钥按进行特定的格式化才能进行 加解密)

//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写入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(@"格式话处理过的key:\n%@\n",keyStr);
    NSError *error = nil;
    return [keyStr writeToFile:keyPath atomically:YES encoding:NSASCIIStringEncoding error:&error];
}

格式化后的结果:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLyQGhLLE9+xtR5ZzPv1vMEOIv
YJxEcMbapskPqqlYewyXD5esHa/BZwQgMTPDMSFw4TuXOUWaM/Khelon8p4iKF4O
4m1Uc5AmEY+vM4gITg1KkmyuN2f43insnBWkMiV8dhfeIB/pCCAL1RExrBfehIVd
kwe3bemsNnowLIyOHwIDAQAB
-----END PUBLIC KEY-----

这里说明一下,密钥/公钥文件可以存放到沙盒中,也可以打包到项目中,都可以,只要使用的时候能拿到文件就行
2.加密/解密,把密钥/私钥处理好之后就可以开始加密/解密了(很简单,三步)

//1.导入key为RSA结构体对象
- (BOOL)importRSAKey:(KeyType)type
{
    
//    这里是从项目中读取公钥和私钥,你也可以把公钥和私钥存入沙河中,从沙河中读取
    
    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实现中文兼容和分段加密
- (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实现中文兼容和分段解密
- (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;
}

这里说明一下:
1.使用openSSL需要把公钥/私钥先转成RSA结构对象,然后进行加密解密
2.解决中文的问题是使用url编码来解决的,这个要跟后台对接好就行,一般都会使用url编码,当然也可以使用其他的,只不过是做了个转换而已
3.因为密文长度不能大于密钥长度,所以一般会进行分段加密,分段加密时密文控制是117,因为加密时对密文使用了长度为11的填充(随机的),加起来就是128(1024位对应的密钥/密文长度)

//长度获取
- (int)getBlockSize:(RSAPaddingType)type type:(KeyType)keyType
{
    int len = RSA_size([self RSAWithType:keyType]);
    
    if (type != eRSAPaddingTypeNone)
    {
        len -= 11;
    }
    
    return len;
}

好了,RSA加密和解密就可以使用了
本文Demo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容