iOS加密算法之RSA公鑰加密

RSA加密算法是一種非對(duì)稱(chēng)加密,即采用不同密鑰進(jìn)行加密解密操作的加密算法.這里不說(shuō)公鑰加密私鑰解密是因?yàn)?在算法中,公鑰和私鑰都可以進(jìn)行加密和解密操作,既可以公鑰加密私鑰解密,也可以私鑰加密公鑰解密.

iOS

對(duì)于移動(dòng)端大部分的需求都是要對(duì)采集到的特殊數(shù)據(jù)進(jìn)行加密上傳,所以本文只介紹公鑰加密的過(guò)程步驟,其余內(nèi)容以后會(huì)做補(bǔ)充,首先感謝大神在github上的分享https://github.com/ideawu/Objective-C-RSA,很完善的工具,可以直接拿來(lái)用

1.處理密鑰

首先我們會(huì)拿到一份公鑰用來(lái)加密,我們要先對(duì)公鑰進(jìn)行處理,因?yàn)槲覀兡玫降墓€是介個(gè)樣子的

"public_key": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtjqeWqj5aW8aPlleLxoj\n6o82Eq4+v/B1+/x2dm6/HOf5lD0GzC/i326BIMzNVbBc2NiEWxju1t+mTlk5ko0Q\nG8KnF6f653QtHxqJVQEVJK3yKzOp45lU6ayOnNQn5aE8klc9fqj5CrKMIpdpH/Oq\n5J+/KxEWOuAtoNYVdI5NR52dUHKnv3mNIblDmfoiz53+d93Io39tEYfDmO8mDMa6\nNgu1oKQJmHWVrW+pDChleO/Cin7eeD2GEBoSQ5cG4CRJO0ouLUoZ9PtsvkZToxHU\nTSSEmqsgb7P9W6OQ4e2phNHQ/aKftWzem7jmISgPaFJan5fw+vGGoeBtxpRZJY+C\n0KdtkEG3jpV+TmCuLJHxMFoN+6kcy+NSFos7CSCyJM+35HPKfQAdtwLH7zLSJ9Rg\nb7n7QmUsTNfesBPLpNrL/o8EcpbV/xa8onTS/JJC3B6RTXSvkkbvBsLZNTk/wXEX\nsLTPo+0CWPDUBw1gBknZK7rV72TtuQ1pYoDe+wDhSEuXWG+XjdJDdOYQAXlg/JJV\nZqhacnyIilFyw194bACuj5KvzpUijOBj7U2J/GYFgKbYz+AaaOUovAOlEdLgqWs3\n95GhNbMPFVfV787kosvoJMwLOYt1TPQ0ZcJB3DEBiEqKL8KNNnq33TAMYQt+8nPQ\nTGmlp3qNkMmTcosR5jg3mtMCAwEAAQ==\n-----END PUBLIC KEY-----\n"

我這里從后臺(tái)拿到是4096位的公鑰,RSA中公鑰位數(shù)越大,加密等級(jí)越高.從公鑰內(nèi)容中可以看出,里面有頭有尾有轉(zhuǎn)義字符,一眼看過(guò)去滿(mǎn)是不靠譜的樣子,而且,整個(gè)密鑰是經(jīng)過(guò)base64編碼的,所以我們還要經(jīng)過(guò)解碼,才能拿到可用的密鑰.然而這還不是我們可用的公鑰,我們需要把他存到鑰匙串中,然后取出可用的SecKeyRef作為加密密鑰使用,具體操作如下:

 + (SecKeyRef)addPublicKey:(NSString *)key{

      NSRange spos = [key rangeOfString:@"-----BEGIN PUBLIC KEY-----"];

      NSRange epos = [key rangeOfString:@"-----END PUBLIC KEY-----"];

      if(spos.location != NSNotFound && epos.location != NSNotFound){

          NSUInteger s = spos.location + spos.length;

          NSUInteger e = epos.location;

          NSRange range = NSMakeRange(s, e-s);

          key = [key substringWithRange:range];
      }

      key = [key stringByReplacingOccurrencesOfString:@"\r" withString:@""];

      key = [key stringByReplacingOccurrencesOfString:@"\n" withString:@""];

      key = [key stringByReplacingOccurrencesOfString:@"\t" withString:@""];

      key = [key stringByReplacingOccurrencesOfString:@" "  withString:@""];

    // This will be base64 encoded, decode it.

       NSData *data = [NSData dataFromBase64String:key];

      data = [FTZAbnormalReport stripPublicKeyHeader:data];

      if(!data){

        return nil;

       }

       //a tag to read/write keychain storage

      NSString *tag = @"RSAUtil_PubKey";

      NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]];

      // Delete any old lingering key with the same tag

      NSMutableDictionary *publicKey = [[NSMutableDictionary alloc] init];

      [publicKey setObject:(__bridge id) kSecClassKey forKey:(__bridge id)kSecClass];

      [publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];

      [publicKey setObject:d_tag forKey:(__bridge id)kSecAttrApplicationTag];

      SecItemDelete((__bridge CFDictionaryRef)publicKey);

      // Add persistent version of the key to system keychain

      [publicKey setObject:data forKey:(__bridge id)kSecValueData];

      [publicKey setObject:(__bridge id) kSecAttrKeyClassPublic forKey:(__bridge id)  kSecAttrKeyClass];

      [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id) kSecReturnPersistentRef];

      CFTypeRef persistKey = nil;

      OSStatus status = SecItemAdd((__bridge CFDictionaryRef)publicKey, &persistKey);

      if (persistKey != nil){

          CFRelease(persistKey);

      }

      if ((status != noErr) && (status != errSecDuplicateItem)) {

           return nil;

      }

      [publicKey removeObjectForKey:(__bridge id)kSecValueData];

      [publicKey removeObjectForKey:(__bridge id)kSecReturnPersistentRef];

      [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];

      [publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];

       // Now fetch the SecKeyRef version of the key

     SecKeyRef keyRef = nil;

      status = SecItemCopyMatching((__bridge CFDictionaryRef)publicKey, (CFTypeRef *)&keyRef);

      if(status != noErr){

          return nil;

      }

      return keyRef;
  }

  + (NSData *)stripPublicKeyHeader:(NSData *)d_key{

      // Skip ASN.1 public key header

      if (d_key == nil) return(nil);

      unsigned long len = [d_key length];

      if (!len) return(nil);

      unsigned char *c_key = (unsigned char *)[d_key bytes];

      unsigned int  idx    = 0;

      if (c_key[idx++] != 0x30) return(nil);

      if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;

      else idx++;

      // PKCS #1 rsaEncryption szOID_RSA_RSA

      static unsigned char seqiod[] =

      { 0x30,  0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,  0x0d,0x01, 0x01,  0x01, 0x05, 0x00 };

      if (memcmp(&c_key[idx], seqiod, 15)) return(nil);

      idx += 15;

      if (c_key[idx++] != 0x03) return(nil);

      if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;

      else idx++;

      if (c_key[idx++] != '\0') return(nil);

      // Now make a new NSData from this buffer

      return([NSData dataWithBytes:&c_key[idx] length:len - idx]);

  }

2.分段加密

對(duì)公鑰處理之后我們要對(duì)需要加密的明文進(jìn)行分段加密,原因是因?yàn)槟愕募用苊魑牡拈L(zhǎng)度不得超過(guò)你的密鑰長(zhǎng)度,我這里的密鑰上面說(shuō)過(guò)是4096位的也就是512字節(jié),由于后臺(tái)是PHP,解密使用的OPENSSL_PKCS1_PADDING長(zhǎng)11字節(jié),所以我使用公鑰進(jìn)行加密的單串明文長(zhǎng)度為501字節(jié).我這里的明文中存在少量漢字內(nèi)容,所以為了省事,統(tǒng)一采用200字符標(biāo)準(zhǔn)進(jìn)行分段.加密方式如下:

const uint8_t *srcbuf = (const uint8_t *)[sourceData bytes];

size_t srclen = (size_t)sourceData.length;

size_t block_size = SecKeyGetBlockSize(keyRef) * sizeof(uint8_t);

void *outbuf = malloc(block_size);

size_t src_block_size = block_size - 11;

NSMutableData *ret = [[NSMutableData alloc] init];

for(int idx=0; idx<srclen; idx+=src_block_size){

    //NSLog(@"%d/%d block_size: %d", idx, (int)srclen, (int)block_size);

    size_t data_len = srclen - idx;

    if(data_len > src_block_size){

        data_len = src_block_size;

    }

    size_t outlen = block_size;

    OSStatus status = noErr;

    status = SecKeyEncrypt(keyRef,kSecPaddingPKCS1,srcbuf + idx,data_len,outbuf,&outlen);

    if (status != 0) {

        NSLog(@"SecKeyEncrypt fail. Error Code: %d", status);

        ret = nil;

        break;

    }else{

        [ret appendBytes:outbuf length:outlen];

    }

}

free(outbuf);

CFRelease(keyRef);

NSData *data = ret;

拿到密文之后我們只需要再對(duì)這串密文進(jìn)行base64編碼就可以存起來(lái)準(zhǔn)備提交了

NSString *retStr = [data base64EncodedString];

以上只是對(duì)RSA公鑰加密在OC中使用的簡(jiǎn)單整理,理解不到位的地方還請(qǐng)海涵,如果各位大佬有更深的理解也可以隨時(shí)指教.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末纵散,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌瞧壮,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件直砂,死亡現(xiàn)場(chǎng)離奇詭異幌陕,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)存崖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)冻记,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人金句,你說(shuō)我怎么就攤上這事檩赢。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵贞瞒,是天一觀的道長(zhǎng)偶房。 經(jīng)常有香客問(wèn)我,道長(zhǎng)军浆,這世上最難降的妖魔是什么棕洋? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮乒融,結(jié)果婚禮上掰盘,老公的妹妹穿的比我還像新娘。我一直安慰自己赞季,他們只是感情好愧捕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著申钩,像睡著了一般次绘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上撒遣,一...
    開(kāi)封第一講書(shū)人閱讀 51,482評(píng)論 1 302
  • 那天邮偎,我揣著相機(jī)與錄音,去河邊找鬼义黎。 笑死禾进,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的廉涕。 我是一名探鬼主播泻云,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼火的!你這毒婦竟也來(lái)了壶愤?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤馏鹤,失蹤者是張志新(化名)和其女友劉穎征椒,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體湃累,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡勃救,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了治力。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蒙秒。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖宵统,靈堂內(nèi)的尸體忽然破棺而出晕讲,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布瓢省,位于F島的核電站弄息,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏勤婚。R本人自食惡果不足惜摹量,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望馒胆。 院中可真熱鬧缨称,春花似錦、人聲如沸祝迂。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)液兽。三九已至骂删,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間四啰,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工粗恢, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留柑晒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓眷射,卻偏偏與公主長(zhǎng)得像匙赞,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子妖碉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354