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í)指教.