iOS:密码学(非对称加密)
Page content
公钥加密,私钥解密;私钥签名,公钥验签
概要
本篇内容主要说明非对称加密算法(RSA)的使用。
算法特点
- 现代加密算法,加密可逆(解密);
- 公钥和私钥:使用公钥加密,私钥解密,使用私钥加密,公钥解密。
- 大文件加密效率较低。
数字签名
- 数据传送过程中,会被分拆成为一个个数据报文;
- 对数据报文进行MD5,获取特征值,将该特征值使用公钥进行RSA加密;
- RSA加密后的特征值与原始数据报文一起发送到服务器;
- 服务器获取到原始数据报文后,进行MD5,再用私钥解密特征值,将MD5与特征值进行比对,可知数据有无篡改。
RSA
实现说明
- 公钥加密,要确定数据块block的填充方式,有不填充、PKCS1填充以及OAEP填充三种;
- 私钥签名,要确定签名的HASH算法,有SHA1、SHA256、SHA512等;
- 本代码实现方式为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来保障安全性。