iOS加密算法之RSA公钥加密

RSA加密算法是一种非对称加密,即采用不同密钥进行加密解密操作的加密算法.这里不说公钥加密私钥解密是因为,在算法中,公钥和私钥都可以进行加密和解密操作,既可以公钥加密私钥解密,也可以私钥加密公钥解密.

iOS

对于移动端大部分的需求都是要对采集到的特殊数据进行加密上传,所以本文只介绍公钥加密的过程步骤,其余内容以后会做补充,首先感谢大神在github上的分享https://github.com/ideawu/Objective-C-RSA,很完善的工具,可以直接拿来用

1.处理密钥

首先我们会拿到一份公钥用来加密,我们要先对公钥进行处理,因为我们拿到的公钥是介个样子的

"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"

我这里从后台拿到是4096位的公钥,RSA中公钥位数越大,加密等级越高.从公钥内容中可以看出,里面有头有尾有转义字符,一眼看过去满是不靠谱的样子,而且,整个密钥是经过base64编码的,所以我们还要经过解码,才能拿到可用的密钥.然而这还不是我们可用的公钥,我们需要把他存到钥匙串中,然后取出可用的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.分段加密

对公钥处理之后我们要对需要加密的明文进行分段加密,原因是因为你的加密明文的长度不得超过你的密钥长度,我这里的密钥上面说过是4096位的也就是512字节,由于后台是PHP,解密使用的OPENSSL_PKCS1_PADDING长11字节,所以我使用公钥进行加密的单串明文长度为501字节.我这里的明文中存在少量汉字内容,所以为了省事,统一采用200字符标准进行分段.加密方式如下:

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;

拿到密文之后我们只需要再对这串密文进行base64编码就可以存起来准备提交了

NSString *retStr = [data base64EncodedString];

以上只是对RSA公钥加密在OC中使用的简单整理,理解不到位的地方还请海涵,如果各位大佬有更深的理解也可以随时指教.

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