前语

在iOS的日常开发中,特别是设计网络恳求时,会用到加密算法,例如在客户端需求建议一个HTTP恳求给服务端,其中会传递一些参数,为了避免参数在网络传输进程中被窃取或者篡改,咱们就需求运用一些加密算法来对恳求参数加密和签名。今天就要点介绍一下AES和HMAC_SHA256两个算法,因为服务端大多数都是运用java语言来编写,AES算法在iOS的Objective-C中和java的实现有些差异,本文要点介绍AES在iOS开发中的应用和需求留意的事项。

AES 加密算法简介

AES是一种典型的对称加密/解密算法,运用加密函数和密钥来完结对明文的加密,然后运用相同的密钥和对应的函数来完结解密。AES的优点在于效率十分高,相比RSA要高得多。AES共有ECB、CBC、CFB和OFB四种加密形式。

在iOS中的实现

Objective-C中支撑AES的ECB和CBC两种形式。
1、电码本形式(Electronic Codebook Book (ECB))
这种形式主要是将明文划分为几个明文段,分块加密,但是加密密钥是相同的。
2、暗码分组链接形式(Cipher Block Chaining (CBC))
这种形式是先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。

ECB是最简单的一种形式,只需求传入待加密的内容和加密的key即可。(一般不引荐ECB形式)
CBC的特点是,除了需求传入加密的内容和加密的key,还需求传入初始化向量iv。即使每次加密的内容和加密的key相同,只要调整iv就能够让终究生成的密文不同。
在客户端和服务端之间传输数据一般是运用约定好的key对指定参数做AES的CBC加密,初始化向量能够随机动态生成,终究将生成好的密文和随机向量iv拼接在一起传给服务端。如:iv+密文。
iv是指定的长度如16位,这样服务端拿到客户端传输过来的数据能够先取前16位作为iv,剩余的是需求解析的密文。这么做大大提升了数据的安全性和破解难度。即使相同的带加密参数,因为有随机向量的参入,终究生成的密文也不相同。

iOS中一般运用#import <CommonCrypto/CommonCryptor.h>库中的这个函数:

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)
    API_AVAILABLE(macos(10.4), ios(2.0));
  • CCOperationkCCEncrypt 加密,kCCDecrypt 解密
enum {
    kCCEncrypt = 0,
    kCCDecrypt,
};
typedef uint32_t CCOperation;
  • CCAlgorithm:加密算法、默认为AES
enum {
    kCCAlgorithmAES128 = 0, /* Deprecated, name phased out due to ambiguity with key size */
    kCCAlgorithmAES = 0,
    kCCAlgorithmDES,
    kCCAlgorithm3DES,
    kCCAlgorithmCAST,
    kCCAlgorithmRC4,
    kCCAlgorithmRC2,
    kCCAlgorithmBlowfish
};
typedef uint32_t CCAlgorithm;
  • CCOptions:加密形式
    ECBkCCOptionPKCS7Padding | kCCOptionECBMode
    CBCkCCOptionPKCS7Padding
enum {
    /* options for block ciphers */
    kCCOptionPKCS7Padding   = 0x0001,
    kCCOptionECBMode        = 0x0002
    /* stream ciphers currently have no options */
};
typedef uint32_t CCOptions;
  • key:密钥
  • keyLength:密钥长度
  • iviv 初始化向量,ECB 不需求。iv定长所以不需求长度(8字节)。
  • dataIn:加密/解密的数据
  • dataInLength:加密/解密的数据长度
  • dataOut:缓冲区(地址),存放密文/明文
  • dataOutAvailable:缓冲区巨细
  • dataOutMoved:加密/解密成果巨细

封装如下:

/**
     *  解密字符串
     *
     *  @param string    加密并base64编码后的字符串
     *  @param keyString 解密密钥
     *  @param iv        初始化向量(8个字节)
     *
     *  @return 返回解密后的字符串
     */
- (NSString *)decryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv {
    // 设置秘钥
    NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
    uint8_t cKey[self.keySize];
    bzero(cKey, sizeof(cKey));
    [keyData getBytes:cKey length:self.keySize];
    // 设置iv
    uint8_t cIv[self.blockSize];
    bzero(cIv, self.blockSize);
    int option = 0;
    if (iv) {
        [iv getBytes:cIv length:self.blockSize];
        option = kCCOptionPKCS7Padding;//CBC 加密!
    } else {
        option = kCCOptionPKCS7Padding | kCCOptionECBMode;//ECB加密!
    }
    // 设置输出缓冲区
    NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0];
    size_t bufferSize = [data length] + self.blockSize;
    void *buffer = malloc(bufferSize);
    // 开始解密
    size_t decryptedSize = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
                                          self.algorithm,
                                          option,
                                          cKey,
                                          self.keySize,
                                          cIv,
                                          [data bytes],
                                          [data length],
                                          buffer,
                                          bufferSize,
                                          &decryptedSize);
    NSData *result = nil;
    if (cryptStatus == kCCSuccess) {
        result = [NSData dataWithBytesNoCopy:buffer length:decryptedSize];
    } else {
        free(buffer);
        NSLog(@"[过错] 解密失利|状况编码: %d", cryptStatus);
    }
    return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
}

上文说到运用CBC形式,能够创立一个随机的iv:

#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonCryptor.h>
NSData *generateRandomIV(size_t length) {
    NSMutableData *randomIV = [NSMutableData dataWithLength:length];
    int result = SecRandomCopyBytes(kSecRandomDefault, length, randomIV.mutableBytes);
    if (result == errSecSuccess) {
        return randomIV;
    } else {
        // 处理生成随机IV失利的情况
        return nil;
    }
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 设置AES加密参数
        NSData *key = [@"YourAESKey123456" dataUsingEncoding:NSUTF8StringEncoding];
        size_t ivLength = kCCBlockSizeAES128; // IV长度为16字节(AES-128)
        // 生成随机IV
        NSData *randomIV = generateRandomIV(ivLength);
        if (randomIV) {
            // 运用randomIV进行AES加密
            // 这儿你能够调用相应的加密办法,传入randomIV作为IV参数
            // 例如,运用CommonCrypto库进行AES加密
            // 详细实现将取决于你所运用的加密库和算法
            // 示例:在这儿调用AES加密函数,传入key和randomIV
            // ...
        } else {
            NSLog(@"生成随机IV失利");
        }
    }
    return 0;
}