IOS 使用AES/ECB/PKCS7Padding 加密、解密数据

IOS 使用AES/ECB/PKCS7Padding 加密、解密数据

AES:加密方式
ECB:工作方式
PKCS5Padding:填充方式(IOS中只有PKCS7Padding,别担心,PKCS5Padding是PKCS7Padding的一个子集,所以使用PKCS7Padding代替也是一样的)
可能用到的框架:
AESCrypt-ObjC-master - Github: https://github.com/Gurpartap/aescrypt

加密中的补位操作:
加密时,如果长度少于16个字节,需要补满16个字节,补(16-len)个(16-len),例如:@"AAAA"这个节符串是4个字节,16 - 4 =  12,所以需要再补12个十进制的12;解密时,因为加密时补的是十进制1到16,解密时,需要把这部分的补位去掉,逐一判断要解密的字符串,每个字节是不是 char >= 1 && char <=  16,如果是的话,就用0来替换以前的值,直到结束,原理是这样,在IOS中一般不需要我们自己进行补位操作,底层会帮我们完成。

字符编码:
常见的字符编码有:UTF-8、ASCII、Base64、十六进制等等,不要使用UTF-8,加密过程是使用UTF-8完成的,但是加密完后的NSData无法通过UTF-8编码格式转出成NSString,推荐使用Base64编码和十六进制编码,下面举例:

使用Base64编码:(如果不能解密中文请使用十六进制或其他编码)
+ (NSData*)base64DataFromString:(NSString*)string;
+ (NSString*)base64StringFromData:(NSData*)data length:(NSUInteger)length;

使用十六进制编码:(需要注意的是:加、解密过程中是使用UTF-8完成的,在加密时,只需要将加密结果NSData对象转为十六进制字符串,而解密时,也只需要将获取到的16进制字符串转换成NSData然后再进行解密,切记)
NSData*cipher = [XNFunctionconvertHexStrToData:content];
//方法如下(注意:这里的十六进制是指十六进制字符串,不是0x000002)

//十六进制转换为NSData
+ (
NSData*)convertHexStrToData:(NSString*)str {
   
 if (!str || [str length] ==0) {
       
 return nil;
    }
   
   
 NSMutableData *hexData = [[NSMutableDataalloc]initWithCapacity:8];
   
 NSRange range;
   
 if ([str length] %2==0) {
        range =
 NSMakeRange(0,2);
    }
 else {
        range =
 NSMakeRange(0,1);
    }
   
 for (NSIntegeri = range.location; i < [str length]; i +=2) {
       
 unsigned int anInt;
       
 NSString *hexCharStr = [str substringWithRange:range];
       
 NSScanner *scanner = [[NSScanneralloc]initWithString:hexCharStr];
       
        [scanner
 scanHexInt:&anInt];
       
 NSData *entity = [[NSDataalloc]initWithBytes:&anIntlength:1];
        [hexData
 appendData:entity];
       
        range.
location+= range.length;
        range.
length=2;
    }
   NSLog(@"hexdata: %@", hexData);
   
 return hexData;
}

//NSData转换为16进制
+ (
NSString*)convertDataToHexStr:(NSData*)data {
   
 if (!data || [data length] ==0) {
       
 return @"";
    }
   
 NSMutableString *string = [[NSMutableStringalloc]initWithCapacity:[datalength]];
   
    [data
 enumerateByteRangesUsingBlock:^(constvoid*bytes,NSRangebyteRange,BOOL*stop) {
       
 unsigned char *dataBytes = (unsignedchar*)bytes;
       
 for (NSIntegeri =0; i < byteRange.length; i++) {
           
 NSString *hexStr = [NSStringstringWithFormat:@"%x", (dataBytes[i]) & 0xff];
           
 if ([hexStr length] ==2) {
                [string
 appendString:hexStr];
            }
 else {
                [string
 appendFormat:@"0%@", hexStr];
            }
        }
    }];
   
   
 return string;
}

进入正题,我们使用框架加、解密:
NSString*str =@"ABC123!@#中文";
NSString*key =@"F8hfdtgfu**0Ka0";
NSData*password = [[keydataUsingEncoding:NSUTF8StringEncoding]MD5Sum];
CCCryptorStatusstatus =kCCSuccess;
NSData*data = [strdataUsingEncoding:NSUTF8StringEncoding];
//加密
NSData* result = [data dataEncryptedUsingAlgorithm:kCCAlgorithmAES128
                                               key:password
                                           options:kCCOptionPKCS7Padding|kCCOptionECBMode
                                             error:&status];
//解密:
NSData*encrypted = [resultdecryptedDataUsingAlgorithm:kCCAlgorithmAES128
                                                   key:password //字符串key够16位,可以直接传进去,不用转成NSdata也行
                                               options:kCCOptionPKCS7Padding|kCCOptionECBMode
                                                 error:&status];
plainString = [[NSStringalloc]initWithData:encryptedencoding:NSUTF8StringEncoding];
NSLog(@"%@", plainString);  //输出:ABC123!@#中文

问题来了,有时候后台(用Java代码使用十六进制编码加密的)传过来的密文是十六进制的字符串,这时候使用这个框架可能会解不开。正确流程应该是这样的,首先拿到密文后,需要将其转换成NSdata,而IOS中没有提供直接将十六进制字符串转成NSdata的API,所以我找来了两个个工具函数,直接拿来用,在上文中有提到,然后再将这个NSData解密,解密后将结果过按UTF-8编码将其转成NSString,结果为nil,苹果官方文档中提到如果NSData中含有非UTF-8编码时就会返回nil,这里很奇怪,明明已经在拿到密文时就使用函数convertHexStrToData将其转成了NSData(Hex->UTF-8->NSData),却里无法转出NSString。
NSString*plainString =nil;
NSString*key =@"%F8hfdtgfu**0Ka0";
CCCryptorStatusstatus =kCCSuccess;
//十六进制字符串 -> NSData
NSData*data = [selfconvertHexStrToData:text];
//解密:
NSData*encrypted = [datadecryptedDataUsingAlgorithm:kCCAlgorithmAES128
                                                 key:key
                                             options:kCCOptionPKCS7Padding|kCCOptionECBMode
                                               error:&status];
plainString = [selfconvertDataToHexStr:encrypted];  //转出成功,但看起来还是十六进制字符串,于是进行下步
plainString = [selfstringFromHexString:plainString];//十六进制字符串转成普通字符串
NSLog(@"%@", plainString); //失败

+ (NSString*)stringFromHexString:(NSString*)hexString {//
   char*myBuffer = (char*)malloc((int)[hexStringlength] /2+1);
   
 bzero(myBuffer, [hexStringlength] /2+1);
   
 for (inti =0; i < [hexStringlength] -1; i +=2) {
       
 unsigned int anInt;
       
 NSString * hexCharStr = [hexString substringWithRange:NSMakeRange(i,2)];
       
 NSScanner * scanner = [[NSScanneralloc]initWithString:hexCharStr];
        [scanner
 scanHexInt:&anInt];
        myBuffer[i /
 2] = (char)anInt;
    }
   
 NSString *unicodeString = [NSStringstringWithCString:myBufferencoding:4];
   
 NSLog(@"字符串:%@",unicodeString);
   return unicodeString; 
}

这找了很久原因,无果,后来在google上看到了另一种加、解密的步骤,直接抠下来用
//16位的key,补位操作省略
NSString*key =@"F8hfdtgfu**0Ka0";
//HexString -> NSData
NSData*cipher = [selfconvertHexStrToData:text];
//解密
NSData*plain  = [cipherAES256DecryptWithKey:key];
//直接是用UTF-8编码转出
NSString*plainString = [[NSStringalloc]initWithData:plainencoding:NSUTF8StringEncoding];
NSLog(@"[解密结果] :%@", plainString); //解密成功

于是我查看了这两个方法的加密步骤,确实有所不同:
AESCrypt-ObjC-master的解密步骤:
1.自动识别参数key和参数iv的类型,如果传入的是NSString,就将其按UTF-8编码转成NSData
2.调用FixKeyLengths将key和iv按 传入的参数algorithm(CCAlgorithm)自动进行补位
3.调用CCCryptorCreate创建一个Cryptorr
/*创建 cryptor
     *
 参数1:解密
     *
 参数2:填充方式,这里传入kCCAlgorithmAES128
     *
 参数3:工作模式:kCCOptionPKCS7Padding | kCCOptionECBMode
     *
 参数4key
     *
 参数5key的长度
     *
 参数6iv
     *参数7CCCryptorRef cryptor = NULL;
*/
CCCryptorCreate(kCCEncrypt, algorithm, options, [keyDatabytes], [keyDatalength], [ivDatabytes], &cryptor );

NSData* resultData = [self_runCryptor: cryptorresult: &status];

4.更新Crypto,最终得到resultData
- (NSData*) _runCryptor: (CCCryptorRef) cryptor result: (CCCryptorStatus*) status {
    size_tbufsize = CCCryptorGetOutputLength( cryptor, (size_t)[selflength],true );
    void* buf =malloc( bufsize );
    size_tbufused = 0;
    size_tbytesTotal = 0;
    /*更新 cryptor
       *参数1:cryptor
       *参数2:密文
       *参数3:密文大小
       *参数4buf
       *参数5bufsize
       *参数6...
    */
    *status =CCCryptorUpdate( cryptor, [selfbytes], (size_t)[selflength],buf, bufsize, &bufused );
    if( *status !=kCCSuccess{
       free( buf );
       return( nil );
    }
 
    bytesTotal += bufused;

    // From Brent Royal-Gordon (Twitter: architechies):
    //  Need to update buf ptr past used bytes when calling CCCryptorFinal()
    *status =CCCryptorFinal( cryptor, buf + bufused, bufsize - bufused, &bufused );
    if( *status != kCCSuccess ) {
       free( buf );
       return( nil );
    }
 
    bytesTotal += bufused;

    return( [NSDatadataWithBytesNoCopy: buflength: bytesTotal] );
}

另一种解密步骤:(也就是本次需求中遇到的AES/ECB/PKCS5Padding + 16进制编码)
- (NSData*)AES256DecryptWithKey:(NSString*)key  //解密
{
    //AES的密钥长度有128字节、192字节、256字节几种,这里举出可能存在的最大长度
   charkeyPtr[kCCKeySizeAES256+1];
   
 bzero(keyPtr,sizeof(keyPtr));
    [keygetCString:keyPtrmaxLength:sizeof(keyPtr)encoding:NSUTF8StringEncoding];
    //密文的长度
   NSUIntegerdataLength = [selflength];
    //密文长度+补位长度
   size_tbufferSize = dataLength +kCCBlockSizeAES128;
    //为解密结果开辟空间
   void*buffer =malloc(bufferSize);
   size_t numBytesDecrypted = 0;
    /* kCCDecrypt:解密
     * kCCAlgorithmAES128:加密方式
     * kCCOptionPKCS7Padding | kCCOptionECBMode:工作模式
     * keyPtr:UTF-8格式的key
     * kCCBlockSizeAES128:按16位长度解密
     * iv:AES不用iv
     * [self bayes]:密文
     * ...
     */
   CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,kCCAlgorithmAES128
                                         kCCOptionPKCS7Padding|kCCOptionECBMode,
                                          keyPtr,
 kCCBlockSizeAES128,
                                         
 NULL,
                                          [
selfbytes], dataLength,
                                          buffer, bufferSize,
                                          &numBytesDecrypted);
   
 if (cryptStatus == kCCSuccess) {
       
 return [NSDatadataWithBytesNoCopy:bufferlength:numBytesDecrypted];
    }
   
 free(buffer);
   
 return nil;
}

建议使用第二种方式 根据不同加密方式和填充方式传入相应的参数即可,其实第一种使用CCCryptor应该也可以,但是不知道为什么在解密十六进制编码时不成功,同样是API/usr/include/CommonCrypto中的API,虽然方法不同,但底层实现应该都是一样的,估计是AESCrypt-ObjC-master框架的问题吧,暂不深究。




  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值