Home > 语言编程, 零敲碎打 > iOS开发之Objective-c的AES加密和解密算法的实现

iOS开发之Objective-c的AES加密和解密算法的实现

高级加密标准(Advanced Encryption Standard,AES),又称Rijndael加密法。
以下实现代码中分别为NSData和NSString增加了一个Category。使用时直接调用即可。

需要注意的是,AES并不能作为HASH算法,加密并解密后的结果,并不一定与原文相同,使用时请注意进行结果验算。例如解密原文的长度,格式规则等。
NG实例

原文:170987350
密码:170

Objective-c的AES加密和解密算法的具体实现代码如下:
1.拓展NSData,增加AES256加密方法

//
//NSData+AES256.h
//
 
#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonDigest.h>
#import <CommonCrypto/CommonCryptor.h>
 
@interface NSData(AES256)
-(NSData *) aes256_encrypt:(NSString *)key;
-(NSData *) aes256_decrypt:(NSString *)key;
@end
 
 
//
//NSData+AES256.m
//
#import "NSData+AES256.h"
 
@implementation NSData(AES256)
 
- (NSData *)aes256_encrypt:(NSString *)key   //加密
{
    char keyPtr[kCCKeySizeAES256+1];
    bzero(keyPtr, sizeof(keyPtr));
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    NSUInteger dataLength = [self length];
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    size_t numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding | kCCOptionECBMode,
                                          keyPtr, kCCBlockSizeAES128,
                                          NULL,
                                          [self bytes], dataLength,
                                          buffer, bufferSize,
                                          &numBytesEncrypted);
    if (cryptStatus == kCCSuccess) {
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }
    free(buffer);
    return nil;
}
 
 
- (NSData *)aes256_decrypt:(NSString *)key   //解密
{
    char keyPtr[kCCKeySizeAES256+1];
    bzero(keyPtr, sizeof(keyPtr));
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    NSUInteger dataLength = [self length];
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    size_t numBytesDecrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding | kCCOptionECBMode,
                                          keyPtr, kCCBlockSizeAES128,
                                          NULL,
                                          [self bytes], dataLength,
                                          buffer, bufferSize,
                                          &numBytesDecrypted);
    if (cryptStatus == kCCSuccess) {
        return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
 
    }
    free(buffer);
    return nil;
}
@end

2.拓展NSString,增加AES256加密方法,需要导入NSData+AES256.h

 
//
//NSString +AES256.h
//
 
#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonDigest.h>
#import <CommonCrypto/CommonCryptor.h>
 
#import "NSData+AES256.h"
 
@interface NSString(AES256)
 
-(NSString *) aes256_encrypt:(NSString *)key;
-(NSString *) aes256_decrypt:(NSString *)key;
 
@end
 
 
//
//NSString +AES256.h
//
 
@implementation NSString(AES256)
 
-(NSString *) aes256_encrypt:(NSString *)key
{
    const char *cstr = [self cStringUsingEncoding:NSUTF8StringEncoding];
    NSData *data = [NSData dataWithBytes:cstr length:self.length];
    //对数据进行加密
    NSData *result = [data aes256_encrypt:key];
 
    //转换为2进制字符串
    if (result && result.length > 0) {
 
        Byte *datas = (Byte*)[result bytes];
        NSMutableString *output = [NSMutableString stringWithCapacity:result.length * 2];
        for(int i = 0; i < result.length; i++){
            [output appendFormat:@"%02x", datas[i]];
        }
        return output;
    }
    return nil;
}
 
-(NSString *) aes256_decrypt:(NSString *)key
{   
    //转换为2进制Data
    NSMutableData *data = [NSMutableData dataWithCapacity:self.length / 2];
    unsigned char whole_byte;
    char byte_chars[3] = {'\0','\0','\0'};
    int i;
    for (i=0; i < [self length] / 2; i++) {
        byte_chars[0] = [self characterAtIndex:i*2];
        byte_chars[1] = [self characterAtIndex:i*2+1];
        whole_byte = strtol(byte_chars, NULL, 16);
        [data appendBytes:&whole_byte length:1];
    }
 
    //对数据进行解密
    NSData* result = [data aes256_decrypt:key];
    if (result && result.length > 0) {
        return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding]autorelease];
    }
    return nil;
}
@end
  1. scfhao
    January 20th, 2016 at 10:47 | #1

    博主,您好,这几天遇到个AES解密的问题,看到了你的这篇博客。
    我觉得在NSData的添加的那两个加密、解密方法有点问题。从方法名上看,使用的是AES256加密方式,AES256加密方式需要的密钥长度是32字节即kCCKeySizeAES256,代码里创建的密钥字符数组的长度也是这个,但在CCCrypt函数的第5个参数却传了kCCBlockSizeAES128(即16),这样加解密其实质上是AES128而非AES256。

  2. 李大仁
    January 20th, 2016 at 18:29 | #2

    AES256是密钥长度是256bits,所有Block都选定为128bits,虽然AES定义时支持128, 192 and 256 bits三种blocksize。

    From Wikipedia
    For AES, NIST selected three members of the Rijndael family, each with a block size of 128 bits, but three different key lengths: 128, 192 and 256 bits.

  3. scfhao
    January 21st, 2016 at 09:40 | #3

    对,AES中Block的长度都是128bits,但CCCrypt函数的第五个参数传的是第四个参数的长度(keyLength),而不是Block的大小。

    苹果文档中的对第四个参数key和第五个参数keyLength的说明:
    @param key Raw key material, length keyLength bytes.

    @param keyLength Length of key material. Must be appropriate
    for the select algorithm. Some algorithms may
    provide for varying key lengths.

  4. Jasfen
    December 23rd, 2016 at 10:07 | #4

    你好,我用了你这种方法去加密和解密,但是我发现一个问题:用这种方式加密后的数据,再解密回来,数据就有一部分丢失了(尤其是中文部分,解密出来的报文,最后的字典的大括号会被丢失),请问一下需要怎样去解决呢?

  5. Jasfen
    December 23rd, 2016 at 10:08 | #5

    主要是加密的那一步,数据出现了问题,我的代码:

    NSString+AES.m中:
    //加密
    – (NSString *) AES256_Encrypt:(NSString *)key{
    const char *cstr = [self cStringUsingEncoding:NSUTF8StringEncoding];
    // NSData *data = [NSData dataWithBytes:cstr length:self.length];

    NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];

    //对数据进行加密
    NSData *result = [data AES256_Encrypt:key];

    // NSData *result = [data AES256EncryptWithKey:[key dataUsingEncoding:NSUTF8StringEncoding]];

    //转换为2进制字符串
    if (result && result.length > 0) {

    Byte *datas = (Byte*)[result bytes];
    NSMutableString *output = [NSMutableString stringWithCapacity:result.length * 2];
    for(int i = 0; i < result.length; i++){
    [output appendFormat:@"%02x", datas];
    }
    return output;
    }
    return nil;
    }

    //解密
    – (NSString *) AES256_Decrypt:(NSString *)key{
    //转换为2进制Data
    NSMutableData *data = [NSMutableData dataWithCapacity:self.length / 2];
    unsigned char whole_byte;
    char byte_chars[3] = {'\0','\0','\0'};
    int i;
    for (i=0; i 0) {
    return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
    }
    return nil;
    }

    DownloadHelper.m里面的代码:
    //发送请求的方法
    -(void)startRequestPost:(NSDictionary *)postInfo delegate:(id)delegate tag:(int)tag userInfo:(NSMutableDictionary *)userInfo cancelTagReqeust:(BOOL)cancelTagReqeust
    {
    NSAssert(postInfo!=nil, @””);
    //url信息
    NSString *urlString = [postInfo objectForKey:@”url”];
    urlString = [urlString stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
    //请求的报文,包含了服务名和方法名,参数列表。是一个json串

    //ssq modify
    NSString *data = [postInfo objectForKey:@”body”];
    NSLog(@”postInfo中获取出来的发送请求的报文 — 时双齐 –%@”,data);

    data = [data base64Decoding];

    NSLog(@”postInfo中获取出来的经过base64处理后的发送请求的报文 — 时双齐 –%@”,data);

    //加密后报文
    // NSString *encryptData = [data AES256_Encrypt:MIMAkey];
    NSString *encryptData = [data AES256_Encrypt:MIMAkey];

    NSLog(@”经过AES256_Encrypt加密后的报文 — 时双齐 –%@”,encryptData);

    NSLog(@”经过AES256_Encrypt解密后的报文 — 时双齐 –%@”,[encryptData AES256_Decrypt:MIMAkey]);

    NSString *base64Data = [encryptData base64Encoding];
    NSLog(@”经过base64Data加密处理后的encryptData报文 — 时双齐 –%@”,base64Data);

    NSLog(@”经过base64Data解密处理后的encryptData报文 — 时双齐 –%@”,[base64Data base64Decoding]);

    NSLog(@”经过base64Data解密处理后,再经过AES256_Decrypt解密后的报文 — 时双齐 –%@”,[[base64Data base64Decoding] AES256_Decrypt:MIMAkey]);

    控制台输出的内容:
    2016-12-23 09:48:40.902586 AnhuiYdswj[1500:394917] 未进行JsonFragement之前的数据为:{
    body = {
    appQf = 2;
    “app_id” = 5c4e1710c6894fdeba768947188fcd2e;
    “app_key” = 5c4e1710c6894fdeba768947188fcd2e;
    contact = 15056014204;
    content = “\U5c0f\U5b69\U5b50”;
    “device_id” = e105481032e90aefd7fc6ceba5d76d25fa565046;
    “device_name” = Jasfen;
    djxh = 20123400009002504000;
    img = “”;
    minVer = “2.0.0”;
    nsrsbh = 34260119871210121X;
    “user_id” = 34260119871210121X;
    };
    head = {
    appKey = 5c4e1710c6894fdeba768947188fcd2e;
    method = addFeedback;
    service = FeedbackService;
    };
    }
    2016-12-23 09:48:40.902981 AnhuiYdswj[1500:394917] 进行JsonFragement后的数据为:eyJoZWFkIjp7InNlcnZpY2UiOiJGZWVkYmFja1NlcnZpY2UiLCJtZXRob2QiOiJhZGRGZWVkYmFjayIsImFwcEtleSI6IjVjNGUxNzEwYzY4OTRmZGViYTc2ODk0NzE4OGZjZDJlIn0sImJvZHkiOnsiYXBwX2lkIjoiNWM0ZTE3MTBjNjg5NGZkZWJhNzY4OTQ3MTg4ZmNkMmUiLCJhcHBfa2V5IjoiNWM0ZTE3MTBjNjg5NGZkZWJhNzY4OTQ3MTg4ZmNkMmUiLCJkanhoIjoiMjAxMjM0MDAwMDkwMDI1MDQwMDAiLCJhcHBRZiI6IjIiLCJjb250YWN0IjoiMTUwNTYwMTQyMDQiLCJtaW5WZXIiOiIyLjAuMCIsInVzZXJfaWQiOiIzNDI2MDExOTg3MTIxMDEyMVgiLCJpbWciOiIiLCJuc3JzYmgiOiIzNDI2MDExOTg3MTIxMDEyMVgiLCJkZXZpY2VfbmFtZSI6Ikphc2ZlbiIsImRldmljZV9pZCI6ImUxMDU0ODEwMzJlOTBhZWZkN2ZjNmNlYmE1ZDc2ZDI1ZmE1NjUwNDYiLCJjb250ZW50Ijoi5bCP5a2p5a2QIn19
    2016-12-23 09:48:40.903122 AnhuiYdswj[1500:394917] postInfo中获取出来的发送请求的报文 — 时双齐 –eyJoZWFkIjp7InNlcnZpY2UiOiJGZWVkYmFja1NlcnZpY2UiLCJtZXRob2QiOiJhZGRGZWVkYmFjayIsImFwcEtleSI6IjVjNGUxNzEwYzY4OTRmZGViYTc2ODk0NzE4OGZjZDJlIn0sImJvZHkiOnsiYXBwX2lkIjoiNWM0ZTE3MTBjNjg5NGZkZWJhNzY4OTQ3MTg4ZmNkMmUiLCJhcHBfa2V5IjoiNWM0ZTE3MTBjNjg5NGZkZWJhNzY4OTQ3MTg4ZmNkMmUiLCJkanhoIjoiMjAxMjM0MDAwMDkwMDI1MDQwMDAiLCJhcHBRZiI6IjIiLCJjb250YWN0IjoiMTUwNTYwMTQyMDQiLCJtaW5WZXIiOiIyLjAuMCIsInVzZXJfaWQiOiIzNDI2MDExOTg3MTIxMDEyMVgiLCJpbWciOiIiLCJuc3JzYmgiOiIzNDI2MDExOTg3MTIxMDEyMVgiLCJkZXZpY2VfbmFtZSI6Ikphc2ZlbiIsImRldmljZV9pZCI6ImUxMDU0ODEwMzJlOTBhZWZkN2ZjNmNlYmE1ZDc2ZDI1ZmE1NjUwNDYiLCJjb250ZW50Ijoi5bCP5a2p5a2QIn19
    2016-12-23 09:48:40.903419 AnhuiYdswj[1500:394917] postInfo中获取出来的经过base64处理后的发送请求的报文 — 时双齐 –{“head”:{“service”:”FeedbackService”,”method”:”addFeedback”,”appKey”:”5c4e1710c6894fdeba768947188fcd2e”},”body”:{“app_id”:”5c4e1710c6894fdeba768947188fcd2e”,”app_key”:”5c4e1710c6894fdeba768947188fcd2e”,”djxh”:”20123400009002504000″,”appQf”:”2″,”contact”:”15056014204″,”minVer”:”2.0.0″,”user_id”:”34260119871210121X”,”img”:””,”nsrsbh”:”34260119871210121X”,”device_name”:”Jasfen”,”device_id”:”e105481032e90aefd7fc6ceba5d76d25fa565046″,”content”:”小孩子”}}
    2016-12-23 09:48:40.904282 AnhuiYdswj[1500:394917] 经过AES256_Encrypt加密后的报文 — 时双齐 –4ac1e4a4cbd5df933137b73e932f7341a6b41d4ba275154cec3c81261e2d2dbca66cb4558191899850d4d328c6595094efb7825eefeb01d8fddeeca402d1545fdffcc78ce04b4fe49af9b3b21ae46c133455592cb0c776cbab8192c31c037c35ec5da8d15be71bfb34325b4c1c76df3473ca1684d912218dc87a84b45cc196ccdadeab3a58003e097ae829849ee57cf0502259a349e8224d2215fdbbaad0ad48627de5ece809f9bb8f689b7865e4dd0c6afcaeb6a5297c34bf81e9fcabbb389c17dfb8dcac1d8a569b1b7d6a373e0bc3cdd9abf087ba4eb0aa3a003c7171f8f2ddb52c0e99b6c4046d8d6fee1df0461b27d895d2aa8c3196044f8f5bcd2b88b1fc273b1be5d2582b5be9052fcee470d793021446ff7ccdadb7f3483649ef0f06e7af84f15f34c78953d87b44929eee3e6f947184ec3c9fba9f6e9caf10ac028ba3c9b6db97c3e267872f9888114e1bfe864f06ea5b253c9b7b836eb33de803929fb2d9354c7ad2c7dfd034117647c8e8b7ba7d7945a92d25900bed8bad533e9ec7c8cd81d7fdbbc01f394f01d27dd353fb22cf029e3c3829b811901c2e4df7d5ed255f90bf4e6701ae3dc5c24bd4cf375ec17eb22856c7f71aa5318b80ce2c13136ebecd533d71f901f7fe6e1620f15c
    2016-12-23 09:48:40.905588 AnhuiYdswj[1500:394917] 经过AES256_Encrypt解密后的报文 — 时双齐 –{“head”:{“service”:”FeedbackService”,”method”:”addFeedback”,”appKey”:”5c4e1710c6894fdeba768947188fcd2e”},”body”:{“app_id”:”5c4e1710c6894fdeba768947188fcd2e”,”app_key”:”5c4e1710c6894fdeba768947188fcd2e”,”djxh”:”20123400009002504000″,”appQf”:”2″,”contact”:”15056014204″,”minVer”:”2.0.0″,”user_id”:”34260119871210121X”,”img”:””,”nsrsbh”:”34260119871210121X”,”device_name”:”Jasfen”,”device_id”:”e105481032e90aefd7fc6ceba5d76d25fa565046″,”content”:”小孩
    2016-12-23 09:48:40.906245 AnhuiYdswj[1500:394917] 经过base64Data加密处理后的encryptData报文 — 时双齐 –NGFjMWU0YTRjYmQ1ZGY5MzMxMzdiNzNlOTMyZjczNDFhNmI0MWQ0YmEyNzUxNTRjZWMzYzgxMjYxZTJkMmRiY2E2NmNiNDU1ODE5MTg5OTg1MGQ0ZDMyOGM2NTk1MDk0ZWZiNzgyNWVlZmViMDFkOGZkZGVlY2E0MDJkMTU0NWZkZmZjYzc4Y2UwNGI0ZmU0OWFmOWIzYjIxYWU0NmMxMzM0NTU1OTJjYjBjNzc2Y2JhYjgxOTJjMzFjMDM3YzM1ZWM1ZGE4ZDE1YmU3MWJmYjM0MzI1YjRjMWM3NmRmMzQ3M2NhMTY4NGQ5MTIyMThkYzg3YTg0YjQ1Y2MxOTZjY2RhZGVhYjNhNTgwMDNlMDk3YWU4Mjk4NDllZTU3Y2YwNTAyMjU5YTM0OWU4MjI0ZDIyMTVmZGJiYWFkMGFkNDg2MjdkZTVlY2U4MDlmOWJiOGY2ODliNzg2NWU0ZGQwYzZhZmNhZWI2YTUyOTdjMzRiZjgxZTlmY2FiYmIzODljMTdkZmI4ZGNhYzFkOGE1NjliMWI3ZDZhMzczZTBiYzNjZGQ5YWJmMDg3YmE0ZWIwYWEzYTAwM2M3MTcxZjhmMmRkYjUyYzBlOTliNmM0MDQ2ZDhkNmZlZTFkZjA0NjFiMjdkODk1ZDJhYThjMzE5NjA0NGY4ZjViY2QyYjg4YjFmYzI3M2IxYmU1ZDI1ODJiNWJlOTA1MmZjZWU0NzBkNzkzMDIxNDQ2ZmY3Y2NkYWRiN2YzNDgzNjQ5ZWYwZjA2ZTdhZjg0ZjE1ZjM0Yzc4OTUzZDg3YjQ0OTI5ZWVlM2U2Zjk0NzE4NGVjM2M5ZmJhOWY2ZTljYWYxMGFjMDI4YmEzYzliNmRiOTdjM2UyNjc4NzJmOTg4ODExNGUxYmZlODY0ZjA2ZWE1YjI1M2M5YjdiODM2ZWIzM2RlODAzOTI5ZmIyZDkzNTRjN2F
    2016-12-23 09:48:40.907045 AnhuiYdswj[1500:394917] 经过base64Data解密处理后的encryptData报文 — 时双齐 –4ac1e4a4cbd5df933137b73e932f7341a6b41d4ba275154cec3c81261e2d2dbca66cb4558191899850d4d328c6595094efb7825eefeb01d8fddeeca402d1545fdffcc78ce04b4fe49af9b3b21ae46c133455592cb0c776cbab8192c31c037c35ec5da8d15be71bfb34325b4c1c76df3473ca1684d912218dc87a84b45cc196ccdadeab3a58003e097ae829849ee57cf0502259a349e8224d2215fdbbaad0ad48627de5ece809f9bb8f689b7865e4dd0c6afcaeb6a5297c34bf81e9fcabbb389c17dfb8dcac1d8a569b1b7d6a373e0bc3cdd9abf087ba4eb0aa3a003c7171f8f2ddb52c0e99b6c4046d8d6fee1df0461b27d895d2aa8c3196044f8f5bcd2b88b1fc273b1be5d2582b5be9052fcee470d793021446ff7ccdadb7f3483649ef0f06e7af84f15f34c78953d87b44929eee3e6f947184ec3c9fba9f6e9caf10ac028ba3c9b6db97c3e267872f9888114e1bfe864f06ea5b253c9b7b836eb33de803929fb2d9354c7ad2c7dfd034117647c8e8b7ba7d7945a92d25900bed8bad533e9ec7c8cd81d7fdbbc01f394f01d27dd353fb22cf029e3c3829b811901c2e4df7d5ed255f90bf4e6701ae3dc5c24bd4cf375ec17eb22856c7f71aa5318b80ce2c13136ebecd533d71f901f7fe6e1620f15c
    2016-12-23 09:48:40.907789 AnhuiYdswj[1500:394917] 经过base64Data解密处理后,再经过AES256_Decrypt解密后的报文 — 时双齐 –{“head”:{“service”:”FeedbackService”,”method”:”addFeedback”,”appKey”:”5c4e1710c6894fdeba768947188fcd2e”},”body”:{“app_id”:”5c4e1710c6894fdeba768947188fcd2e”,”app_key”:”5c4e1710c6894fdeba768947188fcd2e”,”djxh”:”20123400009002504000″,”appQf”:”2″,”contact”:”15056014204″,”minVer”:”2.0.0″,”user_id”:”34260119871210121X”,”img”:””,”nsrsbh”:”34260119871210121X”,”device_name”:”Jasfen”,”device_id”:”e105481032e90aefd7fc6ceba5d76d25fa565046″,”content”:”小孩
    2016-12-23 09:48:40.909008 AnhuiYdswj[1500:394917] 请求的post的报文 — 时双齐 –<4e47466a 4d575530 5954526a 596d5131 5a475935 4d7a4d78 4d7a6469 4e7a4e6c 4f544d79 5a6a637a 4e444668 4e6d4930 4d575130 596d4579 4e7a5578 4e54526a 5a574d7a 597a6778 4d6a5978 5a544a6b 4d6d5269 59324532 4e6d4e69 4e445531 4f444535 4d546735 4f546731 4d475130 5a444d79 4f474d32 4e546b31 4d446b30 5a575a69 4e7a6779 4e57566c 5a6d5669 4d44466b 4f475a6b 5a47566c 59324530 4d444a6b 4d545530 4e575a6b 5a6d5a6a 597a6334 59325577 4e474930 5a6d5530 4f57466d 4f57497a 596a4978 59575530 4e6d4d78 4d7a4d30 4e545531 4f544a6a 596a426a 4e7a6332 59324a68 596a6778 4f544a6a 4d7a466a 4d444d33 597a4d31 5a574d31 5a474534 5a444531 596d5533 4d574a6d 596a4d30 4d7a4931 596a526a 4d574d33 4e6d526d 4d7a5133 4d324e68 4d545934 4e475135 4d544979 4d54686b 597a6733 59546730 596a5131 59324d78 4f545a6a 59325268 5a475668 596a4e68 4e546777 4d444e6c 4d446b33 59575534 4d6a6b34 4e446c6c 5a545533 59325977 4e544179 4d6a5535 59544d30 4f575534 4d6a4930 5a444979 4d54566d 5a474a69 5957466b 4d47466b 4e446732 4d6a646b 5a54566c 59

    ,昨天看了一晚上,也没找到解决办法。

  6. October 19th, 2017 at 19:27 | #6

    @Jasfen
    中文有问题是因为这里
    const char *cstr = [self cStringUsingEncoding:NSUTF8StringEncoding];
    NSData *data = [NSData dataWithBytes:cstr length:self.length];

    应该用cstr来计算长度,而不是self.length

  1. No trackbacks yet.
You must be logged in to post a comment.