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



 

两种方式测试的结果一致;

可见第一种方式其实是苹果内部对第二种方式进一步的封装处理。