美文网首页
【iOS开发】常用加密算法(二)RSA加密算法

【iOS开发】常用加密算法(二)RSA加密算法

作者: Dnaleci | 来源:发表于2021-06-08 08:34 被阅读0次

    非对称算法

    定义

    非对称加密需要两个密钥:公钥 (publickey) 和私钥 (privatekey)。公钥和私钥是一对,如果用公钥对数据加密,那么只能用对应的私钥解密。如果用私钥对数据加密,只能用对应的公钥进行解密。因为加密和解密用的是不同的密钥,所以称为非对称加密。

    特点

    算法强度复杂、安全性依赖于算法与密钥但是由于其算法复杂,而使得加密解密速度没有对称加密解密的速度快。对称密码体制中只有一种密钥,并且是非公开的,如果要解密就得让对方知道密钥。所以保证其安全性就是保证密钥的安全,而非对称密钥体制有两种密钥,其中一个是公开的,这样就可以不需要像对称密码那样传输对方的密钥了。这样安全性就大了很多。

    主要算法

    RSA、Elgamal、背包算法、Rabin、D-H、ECC (椭圆曲线加密算法)。使用最广泛的是 RSA 算法,Elgamal 是另一种常用的非对称加密算法

    RSA算法

    简介

    RSA 是目前最有影响力的公钥加密算法,该算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即公钥,而两个大素数组合成私钥。公钥是可发布的供任何人使用,私钥则为自己所有,供解密之用。

    原理

    基于篇幅问题,有兴趣的推荐看看下面文章:
    RSA算法原理(一):https://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html
    RSA算法原理(二):https://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html

    iOS端Security框架使用小结

    支持的RSA keySize 大小有:512,768,1024,2048位
    支持的RSA 填充方式有三种:NOPadding,PKCS1,OAEP 三种方式 ,填充方式影响最大分组加密数据块的大小
    签名使用的填充方式PKCS1, 支持的签名算法有 sha1,sha256,sha224,sha384,sha512
    Nopadding填充最大数据块为 下面接口 SecKeyGetBlockSize 大小;
    PKCS1 填充方式最大数据为 SecKeyGetBlockSize大小 减去11
    OAEP 填充方式最大数据为 SecKeyGetBlockSize 大小减去 42
    RSA加密解密签名,适合小块的数据处理,大量数量需要处理分组逻辑;密码学中推荐使用对称加密进行数据加密,使用RSA来加密对称密钥

    OC实现RSA算法

    导入头文件

    #import <security/Security.h>
    

    RSA加密(Ps:笔者这里的密钥是通过字符串去生成,现在一般使用PEM文件,iOS端也用p12文件,请对照相应途径生成密钥)

    #pragma mark - 使用公钥字符串加密
    
    /**
     使用公钥字符串加密
    
     @param str 数据
     @param pubKey 公钥
     @return 返回16进制的hexstring
     */
    + (NSString *)encryptString:(NSString *)str publicKey:(NSString *)pubKey{
        if(!str)return nil;
        
        NSData *data = [self encryptData:[str dataUsingEncoding:NSUTF8StringEncoding] publicKey:pubKey];
        NSString *ret = [self hexStringFromData:data];
        
        return ret;
    }
    
    /**
     加密数据
    
     @param data 数据
     @param pubKey 公钥
     @return 操作结果
     */
    + (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];
    }
    
    /**
     添加公钥
    
     @param key 公钥
     @return 公钥对象
     */
    + (SecKeyRef)addPublicKey:(NSString *)key{
        //对于 PEM 格式的密钥文件的密钥多余信息的处理,通常 DER 不需要这一步
        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;
    }
    
    /**
     制作公钥数据头部
    
     @param d_key 公钥
     @return 操作结果数据
     */
    + (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]);
    }
    
    /**
     加密数据
    
     @param data 数据
     @param keyRef 密钥
     @return 操作结果
     */
    + (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){
            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) {
                ret = nil;
                break;
            }else{
                [ret appendBytes:outbuf length:outlen];
            }
        }
        
        free(outbuf);
        CFRelease(keyRef);
        return ret;
    }
    

    RSA解密

    #pragma mark - 使用私钥字符串解密
    /**
     使用私钥字符串解密
     
     @param base64Str 解密内容:必须传base64编码str,不然解密失败
     @param priKey 解密私钥
     @return 解密结果
     */
    + (NSString *)decryptString:(NSString *)base64Str privateKey:(NSString *)priKey{
        if(!base64Str)return nil;
    
        NSData *data = [[NSData alloc] initWithBase64EncodedString:base64Str options:NSDataBase64DecodingIgnoreUnknownCharacters];
        data = [self decryptData:data privateKey:priKey];
    
        NSString *ret = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    
        return ret;
    }
    
    /**
     解密数据
    
     @param data 数据
     @param priKey 私钥
     @return 操作结果
     */
    + (NSData *)decryptData:(NSData *)data privateKey:(NSString *)priKey{
        if(!data || !priKey){
        return nil;
        }
        SecKeyRef keyRef = [self addPrivateKey:priKey];
        if(!keyRef){
        return nil;
        }
        return [self decryptData:data withKeyRef:keyRef];
    }
    
    /**
     添加私钥
    
     @param key 私钥
     @return 私钥对象
     */
    + (SecKeyRef)addPrivateKey:(NSString *)key{
        //对于 PEM 格式的密钥文件的密钥多余信息的处理,通常 DER 不需要这一步
        NSRange spos = [key rangeOfString:@"-----BEGIN PRIVATE KEY-----"];
        NSRange epos = [key rangeOfString:@"-----END 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;
    }
    
    /**
     制作私钥数据头部
    
     @param d_key 私钥
     @return 操作结果数据
     */
    + (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)];
    }
    
    /**
     解密数据
    
     @param data 数据
     @param keyRef 密钥
     @return 操作结果
     */
    + (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) {
            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,
                                   kSecPaddingPKCS1,
                                   srcbuf + idx,
                                   data_len,
                                   outbuf,
                                   &outlen
                                   );
            
            if (status !=0) {
                NSLog(@"SecKeyDecrypt 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;
    }
    

    工具方法

    #pragma mark - Utils
    
    /**
     base64解码
     
     @param str 数据
     @return 解码后结果
     */
    static NSData *base64_decode(NSString *str){
        NSData *data = [[NSData alloc] initWithBase64EncodedString:str options:NSDataBase64DecodingIgnoreUnknownCharacters];
        return data;
    }
    
    static inline char itoh(int i) {
        if (i > 9) return 'A' + (i - 10);
        return '0' + i;
    }
    
    //data数据转hex string
    + (NSString *) hexStringFromData:(NSData*) data
    {
        NSUInteger i, len;
        unsigned char *buf, *bytes;
        
        len = data.length;
        bytes = (unsigned char*)data.bytes;
        buf = (unsigned char *)malloc(len*2);
        for (i=0; i<len; i++) {
            buf[i*2] = itoh((bytes[i] >> 4) & 0xF);
            buf[i*2+1] = itoh(bytes[i] & 0xF);
        }
        
        return [[NSString alloc] initWithBytesNoCopy:buf
                                              length:len*2
                                            encoding:NSASCIIStringEncoding
                                        freeWhenDone:YES];
    }
    

    Ps:一个小问题值得一看

    为什么RSA公钥每次加密得到的结果都不一样?

    相关文章

      网友评论

          本文标题:【iOS开发】常用加密算法(二)RSA加密算法

          本文链接:https://www.haomeiwen.com/subject/dvmzsltx.html