美文网首页IOSAndroid知识iOS知识
AES加密,iOS、android、PHP 相互兼容

AES加密,iOS、android、PHP 相互兼容

作者: iccpeng | 来源:发表于2016-09-08 11:20 被阅读4024次

    DEMO 综述:

    一行代码完成AES加密,加密模式 AES128 + ECB + NoPadding

    DEMO下载地址:github.com/IMCCP/CCPAESEncode

    DEMO GIF

    DEMO 简介:

    最近项目中用到AES加密,在这里整理成篇,供大家参考阅读,在使用该demo过程中,你可能会遇到一些问题,首先你需要看一下下面的demo简介,看看该demo 是否适合你的项目。

    项目中的AES加解密主要用在网络请求过程中对上传的参数进行加密,对从后台服务器获取的数据进行解密。

    整体的加密流程为:

    加密的过程: 参数字典 --> json字符串 --> base64加密后的字符串 --> AES加密后base64再加密 --> 输出最终加密后的字符串;

    解密的过程:

    后台服务器获取加密的字符串 -->base64解密 --> AES解密后base64解密 --> json字符串 --> 数据字典;(与加密的过程相反)

    网上对AES的详细介绍已经有很多,在这里不做赘述,如果你需要了解这些知识,度娘,google 去吧.

    在这里感谢这些 blog 的作者,让我在开发过程中少走了很多弯路:

    http://www.open-open.com/lib/view/open1453530956573.html

    http://blog.csdn.net/huangwenkui1990/article/details/48292865

    http://blog.csdn.net/j_akill/article/details/44079597

    https://wordpress-xiaominfc.rhcloud.com/?p=22#comment-12

    http://www.360doc.com/content/15/1012/10/20918780_505049436.shtml

    tanqisen.github.io/blog/2014/06/06/how-to-prevent-app-crack/ (如何防止客户端被破解)

    我们公司后台为PHP,移动端有iOS与Android, 讨论后选择AES的加密模式为 AES128 + ECB + NoPadding (注意是否满足你的加密需求)。

    为什么选择这种加密模式:

    因为AES的加密规则 --> 原输入数据不够16字节的整数位时,就要补齐。因此就会有padding(填充模式),若使用不同的padding,那么加密

    出来的结果也会不一样。

    如果采用PKCS7Padding或者PKCS5Padding这种加密方式,末端添加的数据可能不固定,在解码后需要把末端多余的字符去掉,比较棘手。

    如果不管补齐多少位,末端都是'\0',去掉的话比较容易操作。 最主要的是能使得

    iOS/Android/PHP相互通信,也是加密过程中最难搞的地方,尤其需要开发者注意。

    (注意:别的加密模式也可以完成三者之间的通信,只是查找方法的时候 AES128 + ECB + NoPadding

    这种加密方式使用的比较多,希望能有更好用的加密方式)

    项目中用到了 google 的 base64 加解密库 GTMBase64,但是这个库已经有很多年没有更新 还是 MRC 开发模式,需要手动配置一下:

    1.选择项目中的Targets,选中你所要操作的Target,

    2.选Build Phases,在其中Complie Sources中选择需要ARC的文件双击,并在输入框中输入 -fno-objc-arc

    DEMO 中工具类的介绍:

    .h文件

    /************************************************************************

    函数名称 : + (NSString *)inputDictionary:(NSMutableDictionary *)dict andSecretKey:(NSString *)key;

    函数描述 : 将传进来的字典 进行 AES 加密后转成json字符串

    加密的过程: 字典 --> json字符串 --> base64加密后的字符串 --> AES加密后base64再加密 --> 输出加密后的字符串

    输入参数 : base64String  base64编码的字符串 ; key  密钥

    返回参数 :  (NSDictionary *)dic  字典

    **********************************************************************

    */

    + (NSString *)inputDictionary:(NSMutableDictionary *)dict andSecretKey:(NSString *)key;

    /************************************************************************

    函数名称 : + (NSDictionary *)inputBase64String:(NSString *)base64String andSecretKey:(NSString *)key;

    函数描述 : 将传进来的base64编码的字符 进行 AES 解密后转成字典

    解密的过程 : 与加密过程相反

    输入参数 : base64String  base64编码的字符串 ; key  密钥

    返回参数 :  (NSDictionary *)dic  字典

    **********************************************************************

    */

    + (NSDictionary *)inputBase64String:(NSString *)base64String andSecretKey:(NSString *)key;

    /************************************************************************

    函数名称 : + (NSString*)dictionaryToJson:(NSDictionary *)dic;

    函数描述 : 将字典转换成字符串

    输入参数 : (NSDictionary *)dic  字典

    返回参数 : 字符串

    **********************************************************************

    */

    + (NSString*)dictionaryToJson:(NSDictionary *)dic;

    /************************************************************************

    函数名称 : + (NSDictionary *)dictionaryWithJsonString:(NSString *)jsonString;

    函数描述 : 将json字符串转换成字典

    输入参数 : (NSString *)jsonString  Json格式的字符串

    返回参数 : 字典

    **********************************************************************

    */

    + (NSDictionary *)dictionaryWithJsonString:(NSString *)jsonString;

    .m文件

    + (NSString *)inputDictionary:(NSMutableDictionary *)dict andSecretKey:(NSString *)key{

    NSString *jsonString = [CCPAESTool dictionaryToJson:dict];

    NSString *jsonBase64Str = [GTMBase64 encodeBase64String:jsonString];

    NSString *encryptStr = [CCPAESTool AES128Encrypt:jsonBase64Str andSecretKey:key];

    return encryptStr;

    }

    + (NSDictionary *)inputBase64String:(NSString *)base64String andSecretKey:(NSString *)key {

    NSString * jsonString = [CCPAESTool AES128Decrypt:base64String andSecretKey:key];

    NSDictionary *dict = [CCPAESTool dictionaryWithJsonString:jsonString];

    return dict;

    }

    /**

    *  AES 加密 解密

    */

    +(NSString *)AES128Encrypt:(NSString *)plainText andSecretKey:(NSString *)secretKeys

    {

    char keyPtr[kCCKeySizeAES128+1];

    memset(keyPtr, 0, sizeof(keyPtr));

    [secretKeys getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

    NSData* data = [plainText dataUsingEncoding:NSUTF8StringEncoding];

    NSUInteger dataLength = [data length];

    NSUInteger diff = kCCKeySizeAES128 - (dataLength % kCCKeySizeAES128);

    NSUInteger newSize = 0;

    if(diff > 0)

    {

    newSize = dataLength + diff;

    }

    char dataPtr[newSize];

    memcpy(dataPtr, [data bytes], [data length]);

    for(int i = 0; i < diff; i++)

    {

    dataPtr[i + dataLength] = 0x0000; ////No padding

    }

    size_t bufferSize = newSize + kCCBlockSizeAES128;

    void *buffer = malloc(bufferSize);

    memset(buffer, 0, bufferSize);

    size_t numBytesCrypted = 0;

    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,

    kCCAlgorithmAES128,

    kCCOptionECBMode,  //使用的是 kCCOptionECBMode ,也就是ECB。在安卓端和PHP端,也得使用ECB

    keyPtr,

    kCCKeySizeAES128,

    NULL,//这个参数iv是个固定值,通常直接使用密钥即可。大家一定要注视这个参数,如果安卓、服务端和iOS端不统一,那么加密结果就会不一样,解密可能能解出来,但是解密后在末尾会出现一些\0、\t之类的。(注: 这里使用NULL)

    dataPtr,

    sizeof(dataPtr),

    buffer,

    bufferSize,

    &numBytesCrypted);

    if (cryptStatus == kCCSuccess) {

    NSData *resultData = [NSData dataWithBytesNoCopy:buffer length:numBytesCrypted];

    return [GTMBase64 encodeBase64Data:resultData];

    }

    free(buffer);

    return nil;

    }

    + (NSString *)AES128Decrypt:(NSString *)encryptText andSecretKey:(NSString *)secretKeys

    {

    char keyPtr[kCCKeySizeAES128 + 1];

    memset(keyPtr, 0, sizeof(keyPtr));

    [secretKeys getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

    NSData *data = [GTMBase64 decodeData:[encryptText dataUsingEncoding:NSUTF8StringEncoding]];

    NSUInteger dataLength = [data length];

    size_t bufferSize = dataLength + kCCBlockSizeAES128;

    void *buffer = malloc(bufferSize);

    size_t numBytesCrypted = 0;

    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,

    kCCAlgorithmAES128,

    kCCOptionECBMode,//使用的是 kCCOptionECBMode ,也就是ECB。在安卓端和PHP端,也得使用ECB

    keyPtr,

    kCCBlockSizeAES128,

    NULL,//这个参数iv是个固定值,通常直接使用密钥即可。大家一定要注视这个参数,如果安卓、服务端和iOS端不统一,那么加密结果就会不一样,解密可能能解出来,但是解密后在末尾会出现一些\0、\t之类的。(注:这里使用NULL)

    [data bytes],

    dataLength,

    buffer,

    bufferSize,

    &numBytesCrypted);

    if (cryptStatus == kCCSuccess) {

    NSData *resultData = [NSData dataWithBytesNoCopy:buffer length:numBytesCrypted];

    return [GTMBase64 decodeBase64Data:resultData];

    }

    free(buffer);

    return nil;

    }

    + (NSString*)dictionaryToJson:(NSMutableDictionary *)dic {

    NSError *parseError = nil;

    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&parseError];

    return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];

    }

    + (NSDictionary *)dictionaryWithJsonString:(NSString *)jsonString {

    if (jsonString == nil) {

    return nil;

    }

    NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];

    NSError *err;

    NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err];

    if(err) {

    NSLog(@"json解析失败:%@",err);

    return nil;

    }

    return dic;

    }

    DEMO 使用示例

    //加密

    -(IBAction)clickEncodeBtn:(UIButton *)sender {

    NSMutableDictionary *dict = [NSMutableDictionary dictionary];

    dict[@"HELLO"] = @"WORLD";

    dict[@"GIT"] = @"HUB";

    dict[@"https"] = @"github.com/IMCCP";

    NSString *AESString = [CCPAESTool inputDictionary:self.dict andSecretKey:secrectKey];

    self.showLabel.text = AESString;

    }

    //解密

    -(IBAction)clickDecodeBtn:(UIButton *)sender {

    //上面加密的结果 NSString *AESString = @"yNgE5k1LAo7jfWk4oLqQv5YHxhBSOG0g6SjdFJoatZ2oTDL+jv1TpL7KWVcbMTH85kQCEFX9KWbsgegrwZ3JgrQ99I70Fd

    LKjSieKe7rfTz1qmbL9gBoe8GJz3TeqmIs7252agKLSDofW8J3mK8y1F4Y3tdnMGsWO9DZLhS/1v0=";

    //解密

    NSDictionary *dict = [CCPAESTool inputBase64String:AESString andSecretKey:secrectKey];

    NSString *jsonString = [CCPAESTool dictionaryToJson:dict];

    self.showLabel.text = jsonString;

    }

    项目中遇到的一些坑,在 DEMO 中都已经注释出来,写的比较清楚,如果该 DEMO 帮助了您,也希望能

    给个 star鼓励一下,如果在使用中您有任何问题,可以在 github issues,我会尽自己能力给您答复 。

    相关文章

      网友评论

      • 9f219eed9d2a:有对应的php 的dome 吗
      • VictoryForYou:base64不叫加密 应该是编码
        iccpeng:@VictoryForYou 嗯
      • ddf82c5e7477:学一下markdown咯
        iccpeng:@巴黎有条狗 嗯,应该学习下。
      • wblearn:如果java作为后台,会不会兼容呢
        iccpeng:@wblearn 应该可以。
      • WJDKFF:从服务器端获取密钥是明文传输的吗?密钥MD5加密后,无法MD5
        解密了! :joy:
        WJDKFF: @IMCCP 请问如何将明文秘钥字符串写入秘钥😉
        iccpeng:@WJDKFF 明文当然不可以了,我感觉可以把密钥保存到钥匙串中,相对还是比较安全的。这也是对称加密的缺点啊。
      • WJDKFF:想问一下,在客户端密钥的存放位置是应用中吗?如果这样,会不会存在泄密风险。
        iccpeng:@WJDKFF 存在客户端的确是有风险的,可以从服务器获取密钥后再进行一个MD5的加密。
      • 4588e4274830:还是自己写算法吧
        iccpeng:@代码描绘人生 可以尝试,但是项目赶时间啊:sweat:
      • 十一岁的加重:有机会试试,毕竟之前找的没有效果
      • MyiOS:哎呦不错哦
        iccpeng:@MyiOS 谢谢您的支持。
      • 海云志:学习学习
        iccpeng:@__追梦人 嗯嗯,我们项目是AES+base64,您可以封装一下,供大家参考学习。
        海云志:@__追梦人 我弄过,AES+RSA
        iccpeng:@__追梦人 :blush:
      • 志城:谢谢:stuck_out_tongue_winking_eye:
        iccpeng:@志城 客气了:smile:,相互学习。
      • Pusswzy:您AES加解密比我了解的深
        iccpeng:@Pusswzy 哪有哪有,相互学习。

      本文标题:AES加密,iOS、android、PHP 相互兼容

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