iOS加密算法之RSA公鑰加密

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

iOS

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

1.處理密鑰

首先我們會(huì)拿到一份公鑰用來加密,我們要先對(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)義字符,一眼看過去滿是不靠譜的樣子,而且,整個(gè)密鑰是經(jīng)過base64編碼的,所以我們還要經(jīng)過解碼,才能拿到可用的密鑰.然而這還不是我們可用的公鑰,我們需要把他存到鑰匙串中,然后取出可用的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)度不得超過你的密鑰長(zhǎng)度,我這里的密鑰上面說過是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編碼就可以存起來準(zhǔn)備提交了

NSString *retStr = [data base64EncodedString];

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容