iOS:密码学(非对称加密)

Page content

公钥加密,私钥解密;私钥签名,公钥验签

概要

本篇内容主要说明非对称加密算法(RSA)的使用。

算法特点

  1. 现代加密算法,加密可逆(解密);
  2. 公钥和私钥:使用公钥加密,私钥解密,使用私钥加密,公钥解密。
  3. 大文件加密效率较低。

数字签名

  1. 数据传送过程中,会被分拆成为一个个数据报文;
  2. 对数据报文进行MD5,获取特征值,将该特征值使用公钥进行RSA加密;
  3. RSA加密后的特征值与原始数据报文一起发送到服务器;
  4. 服务器获取到原始数据报文后,进行MD5,再用私钥解密特征值,将MD5与特征值进行比对,可知数据有无篡改。

RSA

实现说明

  1. 公钥加密,要确定数据块block的填充方式,有不填充、PKCS1填充以及OAEP填充三种;
  2. 私钥签名,要确定签名的HASH算法,有SHA1、SHA256、SHA512等;
  3. 本代码实现方式为NSData的Category;

先引入头文件:

~~~objective-c
#import <Foundation/Foundation.h>
#import <Security/SecBase.h>
~~~

定义填充方式和HASH算法:

~~~objective-c
/**
 加密填充方式
 分组加密,支持最大的加密块为 block 和填充方式有关
 */
typedef enum : NSUInteger {
    RSAPaddingNONE,     //不填充,最大数据块为 blockSize
    RSAPaddingPKCS1,    //填充方式pkcs1,最大数据块为 blockSize -11
    RSAPaddingOAEP,     //填充方式OAEP, 最大数据块为 blockSize -42
} RSAPaddingTYPE;

/**
 签名类型HASH算法
 签名数据 一般签名,数据的HASH值;
 主要使用PKCS1 方式的填充,最大签名数据长度为blockSize-11
 签名算法从ios5以后不再支持md5,md2
 */
typedef enum : NSUInteger {
    SEC_PKCS1SHA1 = 2000,
    SEC_PKCS1SHA224,
    SEC_PKCS1SHA256,
    SEC_PKCS1SHA384,
    SEC_PKCS1SHA512,
} SEC_PKCS1_ALGORITHM;
~~~

公钥加密

~~~objective-c
/**
 公钥加密
 
 @param publicKey publicKey
 @param pdType pdType
 @return 加密结果data
 */
- (NSData *)RSAEncryptWith:(SecKeyRef )publicKey paddingType:(RSAPaddingTYPE )pdType {
    
    if (!publicKey || self.length <1) {
        return nil;
    }
    
    SecPadding rsaPdd;
    switch (pdType) {
        case RSAPaddingNONE:
            rsaPdd = kSecPaddingNone;
            break;
        case RSAPaddingPKCS1:
            rsaPdd = kSecPaddingPKCS1;
            break;
        case RSAPaddingOAEP:
            rsaPdd = kSecPaddingOAEP;
            break;
        default:
            rsaPdd = kSecPaddingPKCS1;
            break;
    }
    
    const uint8_t *srcbuf = (const uint8_t *)self.bytes;
    size_t srclen = (size_t)self.length;
    size_t block_size = SecKeyGetBlockSize(publicKey) * sizeof(uint8_t);
    void *outbuf = malloc(block_size);
    size_t src_block_size = block_size - 11;
    NSMutableData *resultData = [[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 = SecKeyEncrypt(publicKey, rsaPdd, srcbuf+idx, data_len, outbuf, &outlen);
        if (status!=errSecSuccess) {
            NSLog(@"SecKeyEncrypt fail. Error Code: %d", status);
            resultData = nil;
            break;
        }
        [resultData appendBytes:outbuf length:outlen];
    }
    free(outbuf);
    CFRelease(publicKey);
    
    return resultData;
}
~~~

私钥解密

~~~objective-c
/**
 私钥解密
 
 @param privateKey privateKey
 @param pdType pdType
 @return 解密结果data
 */
- (NSData *)RSADecryptWith:(SecKeyRef )privateKey paddingType:(RSAPaddingTYPE )pdType {
    
    if (!privateKey || self.length <1) {
        return nil;
    }
    
    SecPadding rsaPdd;
    switch (pdType) {
        case RSAPaddingNONE:
            rsaPdd = kSecPaddingNone;
            break;
        case RSAPaddingPKCS1:
            rsaPdd = kSecPaddingPKCS1;
            break;
        case RSAPaddingOAEP:
            rsaPdd = kSecPaddingOAEP;
            break;
            
        default:
            rsaPdd = kSecPaddingPKCS1;
            break;
    }
    
    size_t block_size = SecKeyGetBlockSize(privateKey);
    const uint8_t *srcbuf = (const uint8_t *)self.bytes;
    size_t srclen = (size_t)self.length;
    UInt8 *outbuf = malloc(block_size);
    NSMutableData *resultData = [[NSMutableData alloc] init];
    
    for (int idx = 0; idx < srclen; idx+=block_size) {
        
        size_t data_len = srclen - idx;
        data_len = data_len>block_size ? block_size : srclen-idx;
        size_t outlen = block_size;
        
        OSStatus status = SecKeyDecrypt(privateKey, rsaPdd, srcbuf+idx, data_len, outbuf, &outlen);
        if (status!=errSecSuccess) {
            NSLog(@"SecKeyEncrypt fail. Error Code: %d", status);
            resultData = nil;
            break;
        }
        
        //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) {
                continue;
            }
            if (idxFirstZero >= 0) {
                idxNextZero = i;
                break;
            }
            idxFirstZero = i;
        }
        [resultData appendBytes:&outbuf[idxFirstZero+1] length:idxNextZero-idxFirstZero-1];
    }
    free(outbuf);
    CFRelease(privateKey);
    return resultData;
}
~~~

私钥签名

~~~objective-c
/**
 私钥签名,根据不同的算法进行签名
 
 @param privateKey privateKey
 @param ccAlgorithm 算法类型
 @return 签名data
 */
- (NSData *)signDataWith:(SecKeyRef)privateKey algorithm:(SEC_PKCS1_ALGORITHM )ccAlgorithm {
    
    if (!privateKey || self.length <1) {
        return nil;
    }
    
    OSStatus ret;
    NSData *retData = nil;
    size_t siglen = SecKeyGetBlockSize(privateKey);
    uint8_t *sig = malloc(siglen);
    bzero(sig, siglen);
    
    SecPadding secpdal ;
    switch (ccAlgorithm) {
        case SEC_PKCS1SHA1:
            secpdal = kSecPaddingPKCS1SHA1;
            break;
        case SEC_PKCS1SHA224:
            secpdal = kSecPaddingPKCS1SHA224;
            break;
        case SEC_PKCS1SHA256:
            secpdal = kSecPaddingPKCS1SHA256;
            break;
        case SEC_PKCS1SHA384:
            secpdal = kSecPaddingPKCS1SHA384;
            break;
        case SEC_PKCS1SHA512:
            secpdal = kSecPaddingPKCS1SHA512;
            break;
        default:
            secpdal = kSecPaddingPKCS1SHA1;
            break;
    }
    
    ret = SecKeyRawSign(privateKey, secpdal, self.bytes, self.length, sig, &siglen);
    if (ret==errSecSuccess) {
        retData = [NSData dataWithBytes:sig length:siglen];
    }
    
    free(sig);
    sig = NULL;
    
    return retData;
}
~~~

公钥验签

~~~objective-c
/**
 公钥验签
 
 @param publicKey publicKey
 @param signData 签名data
 @param ccAlgorithm ccAlgorithm
 @return 验证结果
 */
- (BOOL)verifySignWith:(SecKeyRef)publicKey signData:(NSData *)signData algorithm:(SEC_PKCS1_ALGORITHM )ccAlgorithm {
    
    if (!publicKey || self.length <1) {
        return NO;
    }
    OSStatus ret;
    BOOL retStatus = NO;
    SecPadding secpdal ;
    switch (ccAlgorithm) {
        case SEC_PKCS1SHA1:
            secpdal = kSecPaddingPKCS1SHA1;
            break;
        case SEC_PKCS1SHA224:
            secpdal = kSecPaddingPKCS1SHA224;
            break;
        case SEC_PKCS1SHA256:
            secpdal = kSecPaddingPKCS1SHA256;
            break;
        case SEC_PKCS1SHA384:
            secpdal = kSecPaddingPKCS1SHA384;
            break;
        case SEC_PKCS1SHA512:
            secpdal = kSecPaddingPKCS1SHA512;
            break;
        default:
            secpdal = kSecPaddingPKCS1SHA1;
            break;
    }
    ret = SecKeyRawVerify(publicKey, secpdal, self.bytes, self.length,signData.bytes, signData.length);
    if (ret==errSecSuccess) {
        retStatus = YES;
    }
    return retStatus;
}
~~~

总结

非对称加密算法RSA,由于其公私钥的特性,使得数据的加解密较为安全,但是单向通讯加密是不严密的,一般采用双向加密;

客户端一套公私钥,公钥存放服务器,私钥存客户端;服务器一套公私钥,公钥存客户端,私钥存服务器,这样保证双向通讯都能进行加密和签名,保障传输数据安全;

但是RSA算法在效率上不如AES,一般是登录时使用RSA,登录成功后向客户端下发AES的KEY,后续通讯采用AES来保障安全性。