CommonCrypto 為蘋果提供的系統加密接口,支持iOS 和 mac 開發;
不僅限於AES加密,提供的接口還支持其他DES,3DES,RC4,BLOWFISH等算法,
本文章主要討論AES在iOS的處理,從接口資料描述和測試來看CommonCrypto 與AES相關的一些小結,
- 支持的AES key size 有 128位,192位,256位
- 目前僅支持 AES 128位 blocks 分組
- 數據填充方式:Nopadding,PKCS7 兩種
- 分組模式:cbc,ecb 兩種默認為 cbc
#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上的對稱加密方式;
- 使用本文蘋果API,本文:http://www.cnblogs.com/cocoajin/p/6150203.html
- 使用openssl ,見文章:http://www.cnblogs.com/cocoajin/p/6121706.html
- 使用 Cryptopp,見文章:http://www.cnblogs.com/cocoajin/p/6112562.html
封裝工具:https://github.com/cocoajin/Security-iOS