CommonCrypto 为苹果提供的系统加密接口,支持iOS 和 mac 开发;
不仅限于AES加密,提供的接口还支持其他DES,3DES,RC4,BLOWFISH等算法,
本文章主要讨论AES在iOS的处理,从接口资料描述和测试来看CommonCrypto 与AES相关的一些小结,
#import <CommonCrypto/CommonCryptor.h>
1. 主要接口CCCrypt
/*! @function CCCrypt @abstract 一个接口来处理加密解密方式. 还可以使用方式二:下面会有示例说明 ,调用流程分几步 ,CCCrytorCreate(), CCCryptorUpdate(), CCCryptorFinal(), and CCCryptorRelease(). @param alg 加解密使用的算法. @param op 操作类型解密或解密: kCCEncrypt or kCCDecrypt. @param options 填充方式式通常是kCCOptionPKCS7Padding,默认分组模式cbc,. @param key 密钥. @param keyLength 密钥长度. @param iv 加密使用的向量参数,cbc模式需要,16个字节,ecb模式不需要,. @param dataIn 输入的数据. @param dataInLength 输入的数据长度. @param dataOut 输出的数据. @param dataOutAvailable 输出数据时需要的可用空间大小. @param dataOutMoved 成功之后实际占用的空间大小. @result 结果在为CCCryptorStatus 枚举 */ CCCryptorStatus CCCrypt( CCOperation op, /* kCCEncrypt, 等. */ CCAlgorithm alg, /* kCCAlgorithmAES128, 等. */ CCOptions options, /* kCCOptionPKCS7Padding, 等. */ const void *key, size_t keyLength, const void *iv, /* 可选的向量 */ const void *dataIn, /*输入*/ size_t dataInLength, void *dataOut, /* 输出 */ size_t dataOutAvailable, size_t *dataOutMoved)
如下的封装调用方式,可以根据需要修改
AES_256_cbc 加密或解密
NSData *aes_cbc_256(NSData *inData,NSData *key,CCOperation coType) { NSData *retData = nil; if (!inData || !key) { return nil; } if (key.length!=32) { return nil; } NSUInteger dataLength = [inData length]; size_t bufferSize = dataLength + kCCBlockSizeAES128; void *buffer = malloc(bufferSize); size_t numBytesEncrypted = 0; CCCryptorStatus cryptStatus;
//ecb 模式不需要使用 iv,cbc模式需要,当cbc模式时,如果不传iv,则默任全0 Byte iv[16] = {0}; for (int i = 0; i < 16; i++) { iv[i] = 1; } //加密 if (coType==kCCEncrypt) { cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128,//使用AES算法 kCCOptionPKCS7Padding, key.bytes, kCCKeySizeAES256, iv, [inData bytes], dataLength, buffer, bufferSize, &numBytesEncrypted); } //解密 else if(coType ==kCCDecrypt) { cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, key.bytes, kCCKeySizeAES256, iv, [inData bytes], dataLength, buffer, bufferSize, &numBytesEncrypted); } if (cryptStatus == kCCSuccess) { return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted]; } free(buffer); return retData; }
测试:
Byte bkey[32] = {0}; for (int i = 0; i < 32; i++) { bkey[i] = 8; } NSData *dkey = [NSData dataWithBytes:bkey length:32]; NSLog(@"key:%@",dkey); NSString *srcStr = @"this is test hello string"; NSData *srcData = [srcStr dataUsingEncoding:NSASCIIStringEncoding]; NSLog(@"src:%@",srcData); NSData *encData = aes_cbc_256(srcData, dkey, kCCEncrypt); NSLog(@"enc:%@",encData); NSData *decData = aes_cbc_256(encData, dkey, kCCDecrypt); NSLog(@"dec:%@",decData); if (memcmp(srcData.bytes, decData.bytes, srcData.length)==0) { NSLog(@"test PASS"); } else { NSLog(@"NO PASS"); }
输出日志
2016-12-09 16:25:09.781 TestCrypt[2384:170598] key:<08080808 08080808 08080808 08080808 08080808 08080808 08080808 08080808> 2016-12-09 16:25:09.782 TestCrypt[2384:170598] src:<74686973 20697320 74657374 2068656c 6c6f2073 7472696e 67> 2016-12-09 16:25:09.782 TestCrypt[2384:170598] enc:<45430426 f51ac83c bd687f22 d9591dfe e413a769 89b07c41 b047d061 8e0a590c> 2016-12-09 16:25:09.782 TestCrypt[2384:170598] dec:<74686973 20697320 74657374 2068656c 6c6f2073 7472696e 67> 2016-12-09 16:25:09.782 TestCrypt[2384:170598] test PASS
2. 另外 为了测试,在mac下安装了openssl 1.1.0c的库
在终端下使用 openssl 命令来配合测试,
在终端下openssl 命令加密数据,在ios上解密
在ios上加密数据,在mac终端下使用openssl 命令解密
配合测试的流程如上,我已在本机测试,不在这里贴示例了,说下openssl的命令吧
主要使用openssl enc 命令,如下命令的帮助,其中部分用中文备注了
Valid options are: -help 查看帮助-ciphers 查看算法列表 -in infile 输入文件 -out outfile 输出结果文件 -pass val 密钥的密码 -e 加密 -d 解密 -p 打印key和iv-P 打印key和iv并退出,这个是大写,实测不会生成out,用上面的小写-v Verbose output -nopad Disable standard block padding -salt Use salt in the KDF (default) -nosalt Do not use salt in the KDF -debug Print debug info -a Base64 encode/decode, depending on encryption flag -base64 Same as option -a -A Used with -[base64|a] to specify base64 buffer as a single line -bufsize val Buffer size -k val Passphrase -kfile infile Read passphrase from file -K val 密钥key,16进制-S val Salt, in hex -iv val 向量iv,16进制-md val Use specified digest to create a key from the passphrase -none Don't encrypt -* Any supported cipher -engine val Use engine, possibly a hardware device
如配合上面测试的 aes_256_cbc 方式,使用上面的密钥和key
加密:
openssl enc -aes-256-cbc -e -K 0808080808080808080808080808080808080808080808080808080808080808 -iv 01010101010101010101010101010101 -in srcTest.txt -out enc.txt -p
解密:
openssl enc -aes-256-cbc -d -K 0808080808080808080808080808080808080808080808080808080808080808 -iv 01010101010101010101010101010101 -in enc.txt -out dec.txt -p
3. 对称加密分步方式二
主要接口
//创建加密器CCCryptorRef
CCCryptorStatus CCCryptorCreate( CCOperation op, /* kCCEncrypt, etc. */ CCAlgorithm alg, /* kCCAlgorithmDES, etc. */ CCOptions options, /* kCCOptionPKCS7Padding, etc. */ const void *key, /* raw key material */ size_t keyLength, const void *iv, /* optional initialization vector */ CCCryptorRef *cryptorRef) /* RETURNED */
//获取输出数据的最大长度 size_t CCCryptorGetOutputLength( CCCryptorRef cryptorRef, size_t inputLength, bool final)
//加密处理 CCCryptorStatus CCCryptorUpdate( CCCryptorRef cryptorRef, const void *dataIn, size_t dataInLength, void *dataOut, /* data RETURNED here */ size_t dataOutAvailable, size_t *dataOutMoved) /* number of bytes written */ //处理最后的数据块 CCCryptorStatus CCCryptorFinal( CCCryptorRef cryptorRef, void *dataOut, size_t dataOutAvailable, size_t *dataOutMoved) /* number of bytes written */
//释放 CCCryptorStatus CCCryptorRelease( CCCryptorRef cryptorRef)
如下封装示例调用
aes_256_cbc 加密或解密
NSData *TEST_AES(NSData *indata,CCOperation otype) { NSData *retData = nil;
//测试的密钥或向量 Byte tkey[32] = {0}; for (int i = 0; i < 32; i++) { tkey[i] = 8; } Byte iv[16] = {0}; for (int i =0; i < 16; i++) { iv[i] = 1; } CCCryptorRef cryptor = NULL; CCCryptorStatus ccret;
//创建加密解密器 if (otype==kCCEncrypt) { ccret = CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, tkey, kCCKeySizeAES256, iv, &cryptor); } else if (otype == kCCDecrypt) { ccret = CCCryptorCreate(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, tkey, kCCKeySizeAES256, iv, &cryptor); } if (ccret!=kCCSuccess) { return nil; } size_t bufsize = 0; size_t moved = 0; size_t total = 0;
//获取最大长度 bufsize = CCCryptorGetOutputLength(cryptor, indata.length, true); char * buf = (char*)malloc(bufsize); bzero(buf, bufsize);
//加解密 ccret = CCCryptorUpdate(cryptor, indata.bytes,indata.length, buf, bufsize, &moved); total += moved; if (ccret!=kCCSuccess) { return nil; }
//处理最后的数据块 ccret = CCCryptorFinal(cryptor, buf+total, bufsize-total, &moved); if (ccret!=kCCSuccess) { return nil; } total +=moved; CCCryptorRelease(cryptor);
retData = [NSData dataWithBytes:buf length:total]; free(buf); return retData; }
测试使用
NSString *srcStr = @"this is test hello string"; NSData *srcData = [srcStr dataUsingEncoding:NSASCIIStringEncoding]; NSData *encData = TEST_AES(srcData, kCCEncrypt); NSData *decData = TEST_AES(encData, kCCDecrypt); NSLog(@"src:%@",srcData); NSLog(@"enc:%@",encData); NSLog(@"dec:%@",decData); if (memcmp(srcData.bytes, decData.bytes, srcData.length)==0) { NSLog(@"PASS"); } else { NSLog(@"NP_PASS"); }
输出日志
2016-12-09 16:42:49.105 TestCrypt[2404:177716] src:<74686973 20697320 74657374 2068656c 6c6f2073 7472696e 67> 2016-12-09 16:42:49.106 TestCrypt[2404:177716] enc:<45430426 f51ac83c bd687f22 d9591dfe e413a769 89b07c41 b047d061 8e0a590c> 2016-12-09 16:42:49.106 TestCrypt[2404:177716] dec:<74686973 20697320 74657374 2068656c 6c6f2073 7472696e 67> 2016-12-09 16:42:49.106 TestCrypt[2404:177716] PASS
两种方式测试的结果一致;
可见第一种方式其实是苹果内部对第二种方式进一步的封装处理。
4. 总结:
关于在iOS上的对称加密方式;
封装工具:https://github.com/cocoajin/Security-iOS