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中使用的简单整理,理解不到位的地方还请海涵,如果各位大佬有更深的理解也可以随时指教.
网友评论