iOS RSA加密與解密

RSA加密現(xiàn)在有很多項(xiàng)目中都會(huì)用到,網(wǎng)上資料很多,方法也不止一種,我這里整理了其中一種,希望對(duì)大家有所幫助

上圖:

Untitled.gif

說(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末嚎幸,一起剝皮案震驚了整個(gè)濱河市颜矿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嫉晶,老刑警劉巖骑疆,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異替废,居然都是意外死亡箍铭,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)椎镣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)诈火,“玉大人,你說(shuō)我怎么就攤上這事状答±涫兀” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵惊科,是天一觀的道長(zhǎng)拍摇。 經(jīng)常有香客問(wèn)我,道長(zhǎng)馆截,這世上最難降的妖魔是什么充活? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮蜡娶,結(jié)果婚禮上堪唐,老公的妹妹穿的比我還像新娘。我一直安慰自己翎蹈,他們只是感情好淮菠,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著荤堪,像睡著了一般合陵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上澄阳,一...
    開(kāi)封第一講書(shū)人閱讀 49,821評(píng)論 1 290
  • 那天拥知,我揣著相機(jī)與錄音,去河邊找鬼碎赢。 笑死低剔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播襟齿,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼姻锁,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了猜欺?” 一聲冷哼從身側(cè)響起位隶,我...
    開(kāi)封第一講書(shū)人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎开皿,沒(méi)想到半個(gè)月后涧黄,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赋荆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年笋妥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窄潭。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡挽鞠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出狈孔,到底是詐尸還是另有隱情,我是刑警寧澤材义,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布均抽,位于F島的核電站,受9級(jí)特大地震影響其掂,放射性物質(zhì)發(fā)生泄漏油挥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一款熬、第九天 我趴在偏房一處隱蔽的房頂上張望深寥。 院中可真熱鬧,春花似錦贤牛、人聲如沸惋鹅。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)闰集。三九已至,卻和暖如春般卑,著一層夾襖步出監(jiān)牢的瞬間武鲁,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工蝠检, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沐鼠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像饲梭,于是被迫代替她去往敵國(guó)和親乘盖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容