iOS AES加密

作者: Mi_Manchi_ | 来源:发表于2023-05-18 10:35 被阅读0次

AES:(Advanced Encryption Standard)高级加密标准。是一个对称分组秘钥算法,旨在取代 DES 成为广泛使用的标准。

秘钥长度有三种,分别是 AES-128、AES-192 和 AES-256。

加密模式有四种,分别是 ECB(Elecyronic Code Book,电子密码本)、CBC(Cipher Block Chaining,加密块链)、CFB(Cipher FeedBack Mode,加密反馈)、OFB(Output FeedBack,输出反馈)。

填充模式:
由于块加密只能对特定长度的数据块进行加密,因此CBC、ECB模式需要在最后一数据块加密前进行数据填充。(CFB,OFB和CTR模式由于与key进行加密操作的是上一块加密后的密文,因此不需要对最后一段明文进行填充)

在iOS SDK中提供了PKCS7Padding,而JDK则提供了PKCS5Padding。原则上PKCS5Padding限制了填充的Block Size为8 bytes,而Java实际上当块大于该值时,其PKCS5Padding与PKCS7Padding是相等的。

初始向量(偏移量)
使用除ECB以外的其他加密模式均需要传入一个初始向量,其大小与Block Size相等(AES的Block Size为128 bits(16字节)),而两个平台的API文档均指明当不传入初始向量时,系统将默认使用一个全0的初始向量。

引入头文件

import <CommonCrypto/CommonDigest.h>

import <CommonCrypto/CommonCryptor.h>

+ (NSString *)SB_AES_Encrypt:(NSString *)originalStr
                      mode:(SBMode)mode
                       key:(NSString *)key
                   keySize:(SBKeySizeAES)keySize
                        iv:(NSString * _Nullable )iv
                   padding:(SBCryptorPadding)padding;
{
    NSData *data = [originalStr dataUsingEncoding:NSUTF8StringEncoding];
    data = [self SB_AES_WithData:data operation:kCCEncrypt mode:mode key:key keySize:keySize iv:iv padding:padding];
    return [data base64EncodedStringWithOptions:0];
}

+ (NSString *)SB_AES_Decrypt:(NSString *)originalStr
                      mode:(SBMode)mode
                       key:(NSString *)key
                   keySize:(SBKeySizeAES)keySize
                        iv:(NSString * _Nullable )iv
                   padding:(SBCryptorPadding)padding
{
    NSData *data = [[originalStr dataUsingEncoding:NSUTF8StringEncoding] base64EncodedDataWithOptions:0];
    
    data = [self SB_AES_WithData:data operation:kCCDecrypt mode:mode key:key keySize:keySize iv:iv padding:padding];
    return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}

+ (NSData *)SB_AES_WithData:(NSData *)originalData
                 operation:(CCOperation)operation
                      mode:(CCMode)mode
                       key:(NSString *)key
                   keySize:(SBKeySizeAES)keySize
                        iv:(NSString *)iv
                   padding:(SBCryptorPadding)padding
{
    NSAssert((mode != kCCModeECB && iv != nil && iv != NULL) || mode == kCCModeECB, @"使用 CBC 模式,initializationVector(即iv,填充值)必须有值");
    
    CCCryptorRef cryptor = NULL;
    CCCryptorStatus status = kCCSuccess;
    
    NSMutableData * keyData = [[key dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
    NSMutableData * ivData = [[iv dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
    
#if !__has_feature(objc_arc)
    [keyData autorelease];
    [ivData autorelease];
#endif
    
    [keyData setLength:keySize];
    [ivData setLength:keySize];
    
    //填充模式(系统API只提供了两种)
    CCPadding paddingMode = (padding == ccPKCS7Padding) ? ccPKCS7Padding : ccNoPadding ;
    NSData *sourceData = originalData;
    if (operation == kCCEncrypt) {
        sourceData =  [self bitPaddingWithData:originalData mode:mode padding:padding];    //FIXME: 实际上的填充模式
    }
    
    status = CCCryptorCreateWithMode(operation, mode, kCCAlgorithmAES, paddingMode, ivData.bytes, keyData.bytes, keyData.length, NULL, 0, 0, 0, &cryptor);
    if ( status != kCCSuccess ){
        NSLog(@"Encrypt Error:%d",status);
        return nil;
    }
    
    //确定处理给定输入所需的输出缓冲区大小尺寸。
    size_t bufsize = CCCryptorGetOutputLength( cryptor, (size_t)[sourceData length], true );
    void * buf = malloc( bufsize );
    size_t bufused = 0;
    size_t bytesTotal = 0;
    
    //处理(加密,解密)一些数据。如果有结果的话,写入提供的缓冲区.
    status = CCCryptorUpdate( cryptor, [sourceData bytes], (size_t)[sourceData length],
                             buf, bufsize, &bufused );
    if ( status != kCCSuccess ){
        NSLog(@"Encrypt Error:%d",status);
        free( buf );
        return nil;
    }
    bytesTotal += bufused;
    if (padding == SBCryptorPKCS7Padding) {
        status = CCCryptorFinal( cryptor, buf + bufused, bufsize - bufused, &bufused );
        if ( status != kCCSuccess ){
            NSLog(@"Encrypt Error:%d",status);
            free( buf );
            return nil;
        }
        bytesTotal += bufused;
    }
    
    NSData *result = [NSData dataWithBytesNoCopy:buf length: bytesTotal];
    if (operation == kCCDecrypt) {
        //解密时移除填充
        result = [self removeBitPaddingWithData:result mode:mode operation:operation andPadding:padding];
    }
    
    CCCryptorRelease(cryptor);

    return result;
}

// 填充需要加密的字节
+ (NSData *)bitPaddingWithData:(NSData *)data
                          mode:(CCMode)mode
                       padding:(SBCryptorPadding)padding;
{
    NSMutableData *sourceData = data.mutableCopy;
    int blockSize = kCCBlockSizeAES128;         //FIXME: AES的块大小都是128bit,即16bytes
    
    switch (padding) {
        case SBCryptorPKCS7Padding:
        {
            if (mode == kCCModeCFB || mode == kCCModeOFB) {
                //MARK: CCCryptorCreateWithMode方法在这两个模式下,并不会给块自动填充,所以需要手动去填充
                NSUInteger shouldLength = blockSize * ((sourceData.length / blockSize) + 1);
                NSUInteger diffLength = shouldLength - sourceData.length;
                uint8_t *bytes = malloc(sizeof(*bytes) * diffLength);
                for (NSUInteger i = 0; i < diffLength; i++) {
                    // 补全缺失的部分
                    bytes[i] = diffLength;
                }
                [sourceData appendBytes:bytes length:diffLength];
            }
        }
            break;
        case SBCryptorZeroPadding:
        {
            int pad = 0x00;
            int diff =   blockSize - (sourceData.length % blockSize);
            for (int i = 0; i < diff; i++) {
                [sourceData appendBytes:&pad length:1];
            }
        }
            break;
        case SBCryptorANSIX923:
        {
            int pad = 0x00;
            int diff =   blockSize - (sourceData.length % blockSize);
            for (int i = 0; i < diff - 1; i++) {
                [sourceData appendBytes:&pad length:1];
            }
            [sourceData appendBytes:&diff length:1];
        }
            break;
        case SBCryptorISO10126:
        {
            int diff = blockSize - (sourceData.length % blockSize);
            for (int i = 0; i < diff - 1; i++) {
                int pad  = arc4random() % 254 + 1;      //FIXME: 因为是随机填充,所以相同参数下,每次加密都是不一样的结果(除了分段后最后一个分段的长度为15bytes的时候加密结果相同)
                [sourceData appendBytes:&pad length:1];
            }
            [sourceData appendBytes:&diff length:1];
        }
            break;
        default:
            break;
    }
    return sourceData;
}

+ (NSData *)removeBitPaddingWithData:(NSData *)sourceData mode:(CCMode)mode operation:(CCOperation)operation andPadding:(SBCryptorPadding)padding
{
    int correctLength = 0;
    int blockSize = kCCBlockSizeAES128;
    Byte *testByte = (Byte *)[sourceData bytes];
    char end = testByte[sourceData.length - 1];
    
    if (padding == SBCryptorPKCS7Padding) {
        if ((mode == kCCModeCFB || mode == kCCModeOFB) && (end > 0 && end < blockSize + 1)) {
            correctLength = (short)sourceData.length - end;
        }else{
            return sourceData;
        }
    }else if (padding == SBCryptorZeroPadding && end == 0) {
        for (int i = (short)sourceData.length - 1; i > 0 ; i--) {
            if (testByte[i] != end) {
                correctLength = i + 1;
                break;
            }
        }
    }else if ((padding == SBCryptorANSIX923 || padding == SBCryptorISO10126) && (end > 0 && end < blockSize + 1)){
        correctLength = (short)sourceData.length - end;
//        if (padding == MIUCryptorISO10126 || ( testByte[sourceData.length - end] == 0 && testByte[sourceData.length - 2] == 0)) {
//            correctLength = (short)sourceData.length - end;
//        }
    }
    
    NSData *data = [NSData dataWithBytes:testByte length:correctLength];
    return data;
}

Demo代码请参考gitHub:https://github.com/Manchitor/SBTools

import "SBTools+AES.h"

相关文章

网友评论

    本文标题:iOS AES加密

    本文链接:https://www.haomeiwen.com/subject/vnmwsdtx.html