iOS系统加密函数详解
加密的种类
单向加密
通过对数据进行摘要计算生成密文,密文不可逆推还原,如:MD5、SHA、Base64双向加密
与单向加密相反,可以把密文逆推还原成明文,双向加密又分为对称加密和非对称加密对称加密
数据使用者必须拥有相同的秘钥才可以进行加密解密。如:DES、3DES、AES、IDEA、RC4、RC5非对称加密
相对对称加密,无需拥有同一组密钥。非对称加密是一种“信息公开的密钥交换协议”。
非对称加密需要公开密钥和私有密钥两组密钥,把公开的密钥为公钥,不公开的密钥为私钥。公钥和私钥是配对起来的,使用公钥进行数据加密,只有对应的私钥才能解密,这两个密钥是数学相关的,如果知道了其中一个,并不能计算出另外一个。因此如果公开了一对密钥中的一个,并不会危害到另外一个密钥的安全。
如:RSA、DSAiOS中系统封装好的加密函数有:
MD5
、SHA1~SHA512
、DES
等,使用系统加密函数,需要先引入头文件#import
iOS系统加密函数的用法
MD5
可以产生一个128位的散列值。8bit = 1byte
,故生成16字节的散列值。
16进制两个字符等于一个字节,故生成的16进制密文为32个字符。
1byte
可以表示的数字范围为0~255
,刚好等于2个16进制字符的范围
。
- 需要用到的方法
/** 获取字符串编码后的长度
* 也可以通过strlen([string UTF8String])获取
*/
- (unsigned int)stringLength:(NSString *)string {
return (unsigned int)[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
}
/**
将加密后的字符转为16进制
@param data 生成的密文数据
@param length 数据的长度
@return 16进制的密文
*/
- (NSString *)hexStringWithCipherTextData:(unsigned char *)data length:(unsigned int)length {
// 开辟length*2的字节空间,16进制中2个字符为一个字节
NSMutableString *hexStr = [NSMutableString stringWithCapacity:length * 2];
for (int i = 0; i < length; i++) {
[hexStr appendFormat:@"%02x", data[i]];
}
return hexStr;
}
- MD5
/**
* MD5加密后的密文长度为128bit,即16字节 = 16 * 8,转为16进制为32字符
* 16进制:2字符为一字节,16 * 2 = 32
*/
/** MD5加密 */
- (NSString *)MD5:(NSString *)string {
// 系统提供的密文长度:16字节
unsigned int outPutLength = CC_MD5_DIGEST_LENGTH;
// 获取明文编码后的长度
unsigned int inPutLength = [self stringLength:string];
// 根据密文的长度,创建一个保存密文的数组
unsigned char outPutData[outPutLength];
// 调用系统的方法
/*extern unsigned char *CC_MD5(const void *data, CC_LONG len, unsigned char *md)*/
CC_MD5([string UTF8String], inPutLength, outPutData);
// 返回16进制密文
return [self hexStringWithCipherTextData:outPutData length:outPutLength];
}
- SHA SHA的调用和MD5类似
/** SHA加密后的字节大小
* CC_SHA1的字节长度20,对应的16进制字符数是20*2
* CC_SHA256的字节长度分别是32,对应的16进制字符数是32*2
* CC_SHA384的字节长度分别是48,对应的16进制字符数是48*2
* CC_SHA512的字节长度分别是64,对应的16进制字符数是64*2
*/
/** SHA1加密 */
- (NSString *)SHA1:(NSString *)string {
unsigned int outPutLength = CC_SHA1_DIGEST_LENGTH;
unsigned int inPutLength = [self stringLength:string];
unsigned char outPutData[outPutLength];
CC_MD5([string UTF8String], inPutLength, outPutData);
return [self hexStringWithCipherTextData:outPutData length:outPutLength];
}
/** SHA256加密 */
- (NSString *)SHA256:(NSString *)string {
unsigned int outPutLength = CC_SHA256_DIGEST_LENGTH;
unsigned int inPutLength = [self stringLength:string];
unsigned char outPutData[outPutLength];
CC_MD5([string UTF8String], inPutLength, outPutData);
return [self hexStringWithCipherTextData:outPutData length:outPutLength];
}
DES加密
概念
DES
全称为Data Encryption Standard
,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。需要注意的是,在某些文献中,作为算法的DES称为数据加密算法(Data Encryption Algorithm,DEA),已与作为标准的DES区分开来.
原则
DES设计中使用了分组密码设计的两个原则:
混淆(confusion)
混淆是使密文的统计特性与密钥的取值之间的关系尽可能复杂化,以使密钥和明文以及密文之间的依赖性对密码分析者来说是无法利用的。扩散(diffusion)
扩散的作用就是将每一位明文的影响尽可能迅速地作用到较多的输出密文位中,以便在大量的密文中消除明文的统计结构,并且使每一位密钥的影响尽可能迅速地扩展到较多的密文位中,以防对密钥进行逐段破译。目的:抗击敌手对密码系统的统计分析。
加密模式
DES的加密模式有如下几种:
-
DES/CBC/PKCS5Padding
对应的OC模式:kCCOptionPKCS7Padding
-
DES/ECB/PKCS5Padding
对应的OC模式:kCCOptionPKCS7Padding | kCCOptionECBMode
每一段的解释如下:
- 第一段:加密算法的名称
- 第二段:分组加密模式,除了CBC和ECB之外,还可以是NONE/CFB/QFB等。最常用的就是CBC和ECB。
DES采用分组加密的方式,将明文按8字节(64位)分组分别加密。如果每个组独立处理,则是ECB。
CBC的处理方式是先用初始向量iv对第一组加密,再用第一组的密文座位秘钥对第二组加密,然后依次完成整个加密操作。
如果明文中有两个分组的内容相同,ECB会得到完全一样的密文,CBC则不会。 - 第三段:最后一个分组的填充方式。大部分情况下,明文并非刚好64位的倍数。对于最后一个分组,如果长度小于64位,则需要用数据填充至64位。
PKCS5Padding是常用的填充方式,如果没有指定,默认的方式就是它。
ps:DES的有效秘钥长度是56位,但要求秘钥长度是64位(8字节)。3DES则要求24字节。
加密函数CCCrypt详解
CCCryptorStatus CCCrypt(
CCOperation op, /* kCCEncrypt, etc. */
CCAlgorithm alg, /* kCCAlgorithmAES128, etc. */
CCOptions options, /* kCCOptionPKCS7Padding, etc. */
const void *key,
size_t keyLength,
const void *iv, /* optional initialization vector */
const void *dataIn, /* optional per op and alg */
size_t dataInLength,
void *dataOut, /* data RETURNED here */
size_t dataOutAvailable,
size_t *dataOutMoved);
- CCCrypt:函数名
- 摘要:无状态的, 一次加密或解密的操作。是对这些加密函数的封装:CCCrytorCreate(),CCCryptorUpdate(), CCCryptorFinal(), and CCCryptorRelease()
- op:加解密,枚举值:
kCCEncrypt
,kCCDecrypt
- alg:算法名称,
kCCAlgorithmAES128
,kCCAlgorithmAES
,kCCAlgorithmDES
,kCCAlgorithm3DES
,kCCAlgorithmCAST
,kCCAlgorithmRC4
,kCCAlgorithmRC2
,kCCAlgorithmBlowfish
- options:填充方式,通常是
kCCOptionPKCS7Padding
,默认分组模式CBC。OC中提供两种模式:kCCOptionPKCS7Padding
、kCCOptionECBMode
- key:秘钥
- keyLength:秘钥长度,必须和选择的算法相匹配,不同的算法要求的秘钥长度不一样。可选值如下:
- kCCKeySizeAES128 = 16,
- kCCKeySizeAES192 = 24,
- kCCKeySizeAES256 = 32,
- kCCKeySizeDES = 8,
- kCCKeySize3DES = 24,
- kCCKeySizeMinCAST = 5,
- kCCKeySizeMaxCAST = 16,
- kCCKeySizeMinRC4 = 1,
- kCCKeySizeMaxRC4 = 512,
- kCCKeySizeMinRC2 = 1,
- kCCKeySizeMaxRC2 = 128,
- kCCKeySizeMinBlowfish = 8,
- kCCKeySizeMaxBlowfish
- iv:加密使用的向量参数,CBC模式需要,16字节。ECB模式不需要。
原始解释:初始向量,可选类型,用于CBC模式。如果存在,则必须与所选算法的块大小相同。如果选择了CBC模式(由于选项标志中没有任何模式位),并且没有IV,将使用NULL(所有0)IV。如果使用ECB模式或选择了流密码算法,则忽略此操作。对于声音加密,总是使用随机数据初始化IV。
iv的创建有三种方式:const Byte iv[] = {1,2,3,4,5,6,7,8};
const Byte iv[] = {0,1,2,3,4,5,6,7};
NSString *testString = key; // 秘钥 NSData *testData = [testString dataUsingEncoding: NSUTF8StringEncoding]; Byte *iv = (Byte *)[testData bytes];
- dataIn:加解密的数据,
const char *
类型,使用字符串的UTF8String
进行转换 - dataInLength:数据的长度,类型
size_t
- dataOut:输出的数据,加密解密后的数据写在这里,
- dataOutAvailable:输出数据时需要的可用空间大小。数据缓冲区的大小(字节)
- dataOutMoved:输出数据实际的大小。返回成功后,写入dataOut的字节数。如果由于提供的缓冲区空间不足而返回kCCBufferTooSmall,则在这里返回所需的缓冲区空间。
- result:返回的结果
- kCCBufferTooSmall: 指示dataOut缓冲区中不充足的空间。在本例中,*dataOutMoved参数将指示完成操作所需的缓冲区大小。操作可以重试,运行时损失最小。
- kCCAlignmentError: 指示数据长度没有正确对齐。这只能对块加密返回,然后只有在解密或加密时禁用填充块时才能返回。
- kCCDecodeError : 指示格式不正确的密文或“错误的键”错误;仅在解密操作期间发生。
- kCCSuccess = 0,
- kCCParamError = -4300,
- kCCMemoryFailure = -4302,
- kCCUnimplemented = -4305,
- kCCOverflow = -4306,
- kCCRNGFailure = -4307,
- kCCUnspecifiedError = -4308,
- kCCCallSequenceError= -4309,
- kCCKeySizeError
CCCrypt调用示例
- 加密
//加密
- (NSString *)encryptUseDES:(NSString *)plainText key:(NSString *)key {
// 密文
NSString *ciphertext = nil;
// 加密后的数据
uint8_t *dataOut = NULL;
size_t dataOutAvailable = 0;
size_t dataOutMove = 0;
dataOutAvailable = (plainText.length + kCCBlockSizeDES) & ~(kCCBlockSizeDES - 1);
dataOut = malloc(dataOutAvailable * sizeof(uint8_t));
// 将已开辟内存空间buffer的首1个字节的值设为0
memset((void *)dataOut, 0x0, dataOutAvailable);
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, // 操作名称:加密
kCCAlgorithmDES, // 加密算法
kCCOptionPKCS7Padding | kCCOptionECBMode, // 填充模式,ECB模式
key.UTF8String, // 加密秘钥
kCCKeySizeDES, // 秘钥大小,和加密算法一致
NULL, // 初始向量:ECB模式为空
plainText.UTF8String, // 加密的明文
(size_t)plainText.length, // 加密明文的大小
dataOut, // 密文的接受者
dataOutAvailable, // 预计密文的大小
&dataOutMove); // 加密后密文的实际大小
if (cryptStatus == kCCSuccess) {
NSData *data = [NSData dataWithBytes:dataOut length:(NSUInteger)dataOutMove];
// 将data转为16进制字符串
ciphertext = [self convertDataToHexStr:data];
}
return ciphertext;
}
- 解密
//解密
- (NSString *)decryptUseDES:(NSString*)cipherText key:(NSString*)key {
// 将16进制转为data
NSData* cipherData =[self convertHexStrToData:cipherText];
// 解密后的数据
uint8_t *dataOut = NULL;
size_t dataOutAvailable = 0;
size_t dataOutMove = 0;
dataOutAvailable = (cipherData.length + kCCBlockSizeDES) & ~(kCCBlockSizeDES - 1);
dataOut = malloc(dataOutAvailable * sizeof(uint8_t));
// 将已开辟内存空间buffer的首1个字节的值设为0
memset((void *)dataOut, 0x0, dataOutAvailable);
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmDES,
kCCOptionPKCS7Padding | kCCOptionECBMode,
key.UTF8String,
kCCKeySizeDES,
NULL,
cipherData.bytes,
(size_t)cipherData.length,
dataOut,
dataOutAvailable,
&dataOutMove);
NSString* plainText = nil;
if (cryptStatus == kCCSuccess) {
NSData* data = [NSData dataWithBytes:dataOut length:(NSUInteger)dataOutMove];
plainText = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
return plainText;
}
- 16进制和NSData相互转换
//将NSData转成16进制
- (NSString *)convertDataToHexStr:(NSData *)data {
if (!data || [data length] == 0) {
return @"";
}
NSMutableString *string = [[NSMutableString alloc] init];
[data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) {
unsigned char *dataBytes = (unsigned char*)bytes;
for (NSInteger i = 0; i < byteRange.length; i++) {
NSString *hexStr = [NSString stringWithFormat:@"%02x", (dataBytes[i]) & 0xff];
[string appendString:hexStr];
}
}];
return string;
}
//将16进制字符串转成NSData
- (NSData *)convertHexStrToData:(NSString *)str {
if (!str || [str length] == 0) {
return nil;
}
NSMutableData *hexData = [[NSMutableData alloc] init];
NSRange range;
if ([str length] % 2 == 0) {
range = NSMakeRange(0, 2);
} else {
range = NSMakeRange(0, 1);
}
for (NSInteger i = range.location; i < [str length]; i += 2) {
unsigned int anInt;
NSString *hexCharStr = [str substringWithRange:range];
// 扫描字符串
NSScanner *scanner = [[NSScanner alloc] initWithString:hexCharStr];
[scanner scanHexInt:&anInt];
NSData *entity = [[NSData alloc] initWithBytes:&anInt length:1];
[hexData appendData:entity];
range.location += range.length;
range.length = 2;
}
return hexData;
}