iOS中使用RSA加密详解
在ios中使用rsa加密解密,需要用到.der和.p12后缀格式的文件,其中.der格式的文件存放的是公钥(public key)用于加密,.p12格式的文件存放的是私钥(private key)用于解密. 首先需要先生成这些文件,然后再将文件导入工程使用,不多说,开始做!
一、使用openssl生成所需秘钥文件
生成环境是在mac系统下,使用openssl进行生成,首先打开终端,按下面这些步骤依次来做:
1. 生成模长为1024bit的私钥文件private_key.pem
openssl genrsa -out private_key.pem 1024
2. 生成证书请求文件rsacertreq.csr
openssl req -new -key private_key.pem -out rsacerreq.csr
注意:这一步会提示输入国家、省份、mail等信息,可以根据实际情况填写,或者全部不用填写,直接全部敲回车.
3. 生成证书rsacert.crt,并设置有效时间为1年
openssl x509 -req -days 3650 -in rsacerreq.csr -signkey private_key.pem -out rsacert.crt
4. 生成供ios使用的公钥文件public_key.der
openssl x509 -outform der -in rsacert.crt -out public_key.der
5. 生成供ios使用的私钥文件private_key.p12
openssl pkcs12 -export -out private_key.p12 -inkey private_key.pem -in rsacert.crt
注意:这一步会提示给私钥文件设置密码,直接输入想要设置密码即可,然后敲回车,然后再验证刚才设置的密码,再次输入密码,然后敲回车,完毕!
在解密时,private_key.p12文件需要和这里设置的密码配合使用,因此需要牢记此密码.
6. 生成供java使用的公钥rsa_public_key.pem
openssl rsa -in private_key.pem -out rsa_public_key.pem -pubout
7. 生成供java使用的私钥pkcs8_private_key.pem
openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_private_key.pem -nocrypt
全部执行成功后,会生成如下文件,其中public_key.der和private_key.p12就是ios需要用到的文件,如下图:
生成的文件
二、将文件导入工程使用
1.新建工程, 并导入security.framework框架, 如下图:
新建工程并添加框架
2.导入秘钥文件
导入.der和.p12格式的秘钥文件, 如下图:
导入秘钥文件
3.新建用于加密、解密的类rsaencryptor, 并实现相关方法
新建rsaencryptor类, 如下图:
新建用于加密解密的类
下面开始上代码, 可以直接复制过去用:
rsaencryptor.h代码如下:
#import <foundation/foundation.h> @interface rsaencryptor : nsobject /** * 加密方法 * * @param str 需要加密的字符串 * @param path '.der'格式的公钥文件路径 */ + (nsstring *)encryptstring:(nsstring *)str publickeywithcontentsoffile:(nsstring *)path; /** * 解密方法 * * @param str 需要解密的字符串 * @param path '.p12'格式的私钥文件路径 * @param password 私钥文件密码 */ + (nsstring *)decryptstring:(nsstring *)str privatekeywithcontentsoffile:(nsstring *)path password:(nsstring *)password; /** * 加密方法 * * @param str 需要加密的字符串 * @param pubkey 公钥字符串 */ + (nsstring *)encryptstring:(nsstring *)str publickey:(nsstring *)pubkey; /** * 解密方法 * * @param str 需要解密的字符串 * @param privkey 私钥字符串 */ + (nsstring *)decryptstring:(nsstring *)str privatekey:(nsstring *)privkey; @end rsaencryptor.m代码如下: #import "rsaencryptor.h" #import <security/security.h> @implementation rsaencryptor static nsstring *base64_encode_data(nsdata *data){ data = [data base64encodeddatawithoptions:0]; nsstring *ret = [[nsstring alloc] initwithdata:data encoding:nsutf8stringencoding]; return ret; } static nsdata *base64_decode(nsstring *str){ nsdata *data = [[nsdata alloc] initwithbase64encodedstring:str options:nsdatabase64decodingignoreunknowncharacters]; return data; } #pragma mark - 使用'.der'公钥文件加密 //加密 + (nsstring *)encryptstring:(nsstring *)str publickeywithcontentsoffile:(nsstring *)path{ if (!str || !path) return nil; return [self encryptstring:str publickeyref:[self getpublickeyrefwithcontentsoffile:path]]; } //获取公钥 + (seckeyref)getpublickeyrefwithcontentsoffile:(nsstring *)filepath{ nsdata *certdata = [nsdata datawithcontentsoffile:filepath]; if (!certdata) { return nil; } seccertificateref cert = seccertificatecreatewithdata(null, (cfdataref)certdata); seckeyref key = null; sectrustref trust = null; secpolicyref policy = null; if (cert != null) { policy = secpolicycreatebasicx509(); if (policy) { if (sectrustcreatewithcertificates((cftyperef)cert, policy, &trust) == noerr) { sectrustresulttype result; if (sectrustevaluate(trust, &result) == noerr) { key = sectrustcopypublickey(trust); } } } } if (policy) cfrelease(policy); if (trust) cfrelease(trust); if (cert) cfrelease(cert); return key; } + (nsstring *)encryptstring:(nsstring *)str publickeyref:(seckeyref)publickeyref{ if(![str datausingencoding:nsutf8stringencoding]){ return nil; } if(!publickeyref){ return nil; } nsdata *data = [self encryptdata:[str datausingencoding:nsutf8stringencoding] withkeyref:publickeyref]; nsstring *ret = base64_encode_data(data); return ret; } #pragma mark - 使用'.12'私钥文件解密 //解密 + (nsstring *)decryptstring:(nsstring *)str privatekeywithcontentsoffile:(nsstring *)path password:(nsstring *)password{ if (!str || !path) return nil; if (!password) password = @""; return [self decryptstring:str privatekeyref:[self getprivatekeyrefwithcontentsoffile:path password:password]]; } //获取私钥 + (seckeyref)getprivatekeyrefwithcontentsoffile:(nsstring *)filepath password:(nsstring*)password{ nsdata *p12data = [nsdata datawithcontentsoffile:filepath]; if (!p12data) { return nil; } seckeyref privatekeyref = null; nsmutabledictionary * options = [[nsmutabledictionary alloc] init]; [options setobject: password forkey:(__bridge id)ksecimportexportpassphrase]; cfarrayref items = cfarraycreate(null, 0, 0, null); osstatus securityerror = secpkcs12import((__bridge cfdataref) p12data, (__bridge cfdictionaryref)options, &items); if (securityerror == noerr && cfarraygetcount(items) > 0) { cfdictionaryref identitydict = cfarraygetvalueatindex(items, 0); secidentityref identityapp = (secidentityref)cfdictionarygetvalue(identitydict, ksecimportitemidentity); securityerror = secidentitycopyprivatekey(identityapp, &privatekeyref); if (securityerror != noerr) { privatekeyref = null; } } cfrelease(items); return privatekeyref; } + (nsstring *)decryptstring:(nsstring *)str privatekeyref:(seckeyref)privkeyref{ nsdata *data = [[nsdata alloc] initwithbase64encodedstring:str options:nsdatabase64decodingignoreunknowncharacters]; if (!privkeyref) { return nil; } data = [self decryptdata:data withkeyref:privkeyref]; nsstring *ret = [[nsstring alloc] initwithdata:data encoding:nsutf8stringencoding]; return ret; } #pragma mark - 使用公钥字符串加密 /* start: encryption with rsa public key */ //使用公钥字符串加密 + (nsstring *)encryptstring:(nsstring *)str publickey:(nsstring *)pubkey{ nsdata *data = [self encryptdata:[str datausingencoding:nsutf8stringencoding] publickey:pubkey]; nsstring *ret = base64_encode_data(data); return ret; } + (nsdata *)encryptdata:(nsdata *)data publickey:(nsstring *)pubkey{ if(!data || !pubkey){ return nil; } seckeyref keyref = [self addpublickey:pubkey]; if(!keyref){ return nil; } return [self encryptdata:data withkeyref:keyref]; } + (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 = base64_decode(key); data = [self 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]); } + (nsdata *)encryptdata:(nsdata *)data withkeyref:(seckeyref) keyref{ const uint8_t *srcbuf = (const uint8_t *)[data bytes]; size_t srclen = (size_t)data.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); return ret; } /* end: encryption with rsa public key */ #pragma mark - 使用私钥字符串解密 /* start: decryption with rsa private key */ //使用私钥字符串解密 + (nsstring *)decryptstring:(nsstring *)str privatekey:(nsstring *)privkey{ if (!str) return nil; nsdata *data = [[nsdata alloc] initwithbase64encodedstring:str options:nsdatabase64decodingignoreunknowncharacters]; data = [self decryptdata:data privatekey:privkey]; nsstring *ret = [[nsstring alloc] initwithdata:data encoding:nsutf8stringencoding]; return ret; } + (nsdata *)decryptdata:(nsdata *)data privatekey:(nsstring *)privkey{ if(!data || !privkey){ return nil; } seckeyref keyref = [self addprivatekey:privkey]; if(!keyref){ return nil; } return [self decryptdata:data withkeyref:keyref]; } + (seckeyref)addprivatekey:(nsstring *)key{ nsrange spos = [key rangeofstring:@"-----begin rsa private key-----"]; nsrange epos = [key rangeofstring:@"-----end rsa private 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 = base64_decode(key); data = [self stripprivatekeyheader:data]; if(!data){ return nil; } //a tag to read/write keychain storage nsstring *tag = @"rsautil_privkey"; nsdata *d_tag = [nsdata datawithbytes:[tag utf8string] length:[tag length]]; // delete any old lingering key with the same tag nsmutabledictionary *privatekey = [[nsmutabledictionary alloc] init]; [privatekey setobject:(__bridge id) ksecclasskey forkey:(__bridge id)ksecclass]; [privatekey setobject:(__bridge id) ksecattrkeytypersa forkey:(__bridge id)ksecattrkeytype]; [privatekey setobject:d_tag forkey:(__bridge id)ksecattrapplicationtag]; secitemdelete((__bridge cfdictionaryref)privatekey); // add persistent version of the key to system keychain [privatekey setobject:data forkey:(__bridge id)ksecvaluedata]; [privatekey setobject:(__bridge id) ksecattrkeyclassprivate forkey:(__bridge id) ksecattrkeyclass]; [privatekey setobject:[nsnumber numberwithbool:yes] forkey:(__bridge id) ksecreturnpersistentref]; cftyperef persistkey = nil; osstatus status = secitemadd((__bridge cfdictionaryref)privatekey, &persistkey); if (persistkey != nil){ cfrelease(persistkey); } if ((status != noerr) && (status != errsecduplicateitem)) { return nil; } [privatekey removeobjectforkey:(__bridge id)ksecvaluedata]; [privatekey removeobjectforkey:(__bridge id)ksecreturnpersistentref]; [privatekey setobject:[nsnumber numberwithbool:yes] forkey:(__bridge id)ksecreturnref]; [privatekey setobject:(__bridge id) ksecattrkeytypersa forkey:(__bridge id)ksecattrkeytype]; // now fetch the seckeyref version of the key seckeyref keyref = nil; status = secitemcopymatching((__bridge cfdictionaryref)privatekey, (cftyperef *)&keyref); if(status != noerr){ return nil; } return keyref; } + (nsdata *)stripprivatekeyheader:(nsdata *)d_key{ // skip asn.1 private 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 = 22; //magic byte at offset 22 if (0x04 != c_key[idx++]) return nil; //calculate length of the key unsigned int c_len = c_key[idx++]; int det = c_len & 0x80; if (!det) { c_len = c_len & 0x7f; } else { int bytecount = c_len & 0x7f; if (bytecount + idx > len) { //rsa length field longer than buffer return nil; } unsigned int accum = 0; unsigned char *ptr = &c_key[idx]; idx += bytecount; while (bytecount) { accum = (accum << 8) + *ptr; ptr++; bytecount--; } c_len = accum; } // now make a new nsdata from this buffer return [d_key subdatawithrange:nsmakerange(idx, c_len)]; } + (nsdata *)decryptdata:(nsdata *)data withkeyref:(seckeyref) keyref{ const uint8_t *srcbuf = (const uint8_t *)[data bytes]; size_t srclen = (size_t)data.length; size_t block_size = seckeygetblocksize(keyref) * sizeof(uint8_t); uint8 *outbuf = malloc(block_size); size_t src_block_size = block_size; 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 = seckeydecrypt(keyref, ksecpaddingnone, srcbuf + idx, data_len, outbuf, &outlen ); if (status != 0) { nslog(@"seckeyencrypt fail. error code: %d", status); ret = nil; break; }else{ //the actual decrypted data is in the middle, locate it! int idxfirstzero = -1; int idxnextzero = (int)outlen; for ( int i = 0; i < outlen; i++ ) { if ( outbuf[i] == 0 ) { if ( idxfirstzero < 0 ) { idxfirstzero = i; } else { idxnextzero = i; break; } } } [ret appendbytes:&outbuf[idxfirstzero+1] length:idxnextzero-idxfirstzero-1]; } } free(outbuf); cfrelease(keyref); return ret; } /* end: decryption with rsa private key */ @end
4. 测试加密、解密
首先先测试使用.der和.p12秘钥文件进行加密、解密, 在viewcontroller.m中进行测试, 代码如下:
#import "viewcontroller.h" #import "rsaencryptor.h" @interface viewcontroller () @end @implementation viewcontroller - (void)viewdidload { [super viewdidload]; //原始数据 nsstring *originalstring = @"这是一段将要使用'.der'文件加密的字符串!"; //使用.der和.p12中的公钥私钥加密解密 nsstring *public_key_path = [[nsbundle mainbundle] pathforresource:@"public_key.der" oftype:nil]; nsstring *private_key_path = [[nsbundle mainbundle] pathforresource:@"private_key.p12" oftype:nil]; nsstring *encryptstr = [rsaencryptor encryptstring:originalstring publickeywithcontentsoffile:public_key_path]; nslog(@"加密前:%@", originalstring); nslog(@"加密后:%@", encryptstr); nslog(@"解密后:%@", [rsaencryptor decryptstring:encryptstr privatekeywithcontentsoffile:private_key_path password:@"123456"]); } - (void)didreceivememorywarning { [super didreceivememorywarning]; // dispose of any resources that can be recreated. } @end
运行后, 输出信息如下:
输出结果
可以看到已经可以成功加密、解密了.
下面接着测试使用秘钥字符串进行加密、解密, 那么秘钥字符串从哪里来? 可以来这里:http://web.chacuo.net/netrsakeypair, 这是一个在线生成rsa秘钥的网站, 生成公钥和秘钥后, 复制出来用于测试. 然后在viewcontroller.m中使用rsaentryptor.h头文件中对应的加密方法进行加密, viewcontroller.m中代码如下:
#import "viewcontroller.h" #import "rsaencryptor.h" @interface viewcontroller () @end @implementation viewcontroller - (void)viewdidload { [super viewdidload]; //原始数据 nsstring *originalstring = @"这是一段将要使用'秘钥字符串'进行加密的字符串!"; //使用字符串格式的公钥私钥加密解密 nsstring *encryptstr = [rsaencryptor encryptstring:originalstring publickey:@"migfma0gcsqgsib3dqebaquaa4gnadcbiqkbgqdtbz6cnh9pgdf60aqkvelz3ftalyzhqwbp601y77szmghx3f5novuzbdk7umdoclk4fbzitewyd9dwvaerxzo9bfui96baop8wfl1vkzyyhtcznxnjfgsqd/b70/exmgmbpewkaadyuqijidvgh1fqk/4acws39yxwbs+ilhspsqidaqab"]; nslog(@"加密前:%@", originalstring); nslog(@"加密后:%@", encryptstr); nslog(@"解密后:%@", [rsaencryptor decryptstring:encryptstr privatekey:@"miiceaibadanbgkqhkig9w0baqefaascamiwggjeageaaogbanntnpw0f0+b0xrrpaq94vpcvnqxlmddbunrtxlvtloyydfcxk2hvrlt0rtqx2gisrguhojn7bgp0na8astdmj0ew4j3pscinzb+xvwrnlidnzofe0kuzjb38hvt8teyawgktcqab3jsoimh1uahuvar/hpzblf1hfbtl4iuew9jagmbaaecgya1tgeqmakqofga8xtwuxewdoads9k0+ekeuoxgxzqot/gyiihuiafjilfhoua1ndf/ycqag973sbtdhtfpmwqfnqq13+jaownsltjwgr7hwf7qplyw92r7cu0v7wffjqm1t/2fku9jkhfahfb7qqesmibo/vmjer9o4tex58uxdqjbao0o4lnwdvjr1gn02cqvxpotty6dgfbqdeaazf8obb6xqvcqgw/avms3bh8nvluwdq2k/xte8thxjw9ftbqtld8cqqdkunco35gaquf9bhsdzrs7no1j3vjlrm0itrepqjqtvevdxzc+1/urkwvaiigwaxjqcvfmqzscdbznhyxpz5fxakeagb3kmrkhl4ynpmkrjhw+ih+aserccsj6sjfbhx4xaakyzmbxxnchg+jb+bznz06ybfc5nlzm7y/n61o1f5/56wjbalw+zvze6ly5l34114ug04w9x0hcfgau7mijphfjgudatd/h9xfge4odmrpud3q9me9llmyk6mikpfm4c2+3dzccqqc8y37npgpnekd9smmwppsejpw41amlfckvp4da3z7g5bglmuicrva9ydaiaaydggck8lxc8k6hpkrfgyrxkrtt"]); } - (void)didreceivememorywarning { [super didreceivememorywarning]; // dispose of any resources that can be recreated. } @end
运行后, 输出信息如下:
输出结果
可以看到,也成功加密、解密了.
至此, rsa加密演示完毕!
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!