iOS加密(一):Base64 +AES +MD5模式

作者: 狮子_挽歌_ | 来源:发表于2017-04-12 23:55 被阅读386次

    由于公司对于信息安全比较注重,需要对json数据并用AES加密,然后生成url_safe base64编码。然后继续拼接时间戳 session还有其他字段进行MD5加密。然后使用RAC签名后传输。本来没觉得什么,以为加密算法之前也接触过,或者去网上直接套用就好,结果,发现没那么简单 = =

    一: Base64

    • 是网络上使用最广泛的编码系统,能够将任何二进制数据,转换成只有 65 个字符 * 组成的文本文件.
    • 编码后的数据由 a-z A-Z 0-9 + / = 表示.
    • base64 编码后的结果能够反算,不够安全.
    • base64 是所有现代加密算法的基础算法.

    base64可以用来将binary的字节序列数据编码成ASCII字符序列构成的文本,使用时,在传输编码方式中指定base64。使用的字符包括大小写字母各26个,加上10个数字,和加号“+”,斜杠“/”,一共64个字符,等号“=”用来作为后缀用途。

    完整的base64定义可见RFC 1421和RFC 2045。编码后的数据比原始数据略长,为原来的4/3.。在电子邮件中,根据RFC 822规定,每76个字符,还需要加上一个回车换行。可以估算编码后数据长度大约为原长的135.1%。( 如果源码为760个字符, 则编码后的长度为760 * (4 / 3) + 10(换行符) )

    对Man进行编码 ABC编码

    URL_Safe Base64 这个参数意思是加密时不使用对URL和文件名有特殊意义的字符来作为加密字符,因为标准base64包含"+"和"/",因此不适合直接放在URL中传输.因为URL编译器会将"+","/"转换成XX%的样式.而这些"%"在录入数据库的时候还会进行转码,为了解决此问题可采用一种适用于URL的改进base64,它不在末位添加"=",将"+"转换成"-",将"/"转换成"_".

    iOS原生API的有它自己的局限性, 所以我使用了base64的神器(GTMBase, 这个框架满足了每一种的base64模式的编解码.)
    因此为了两个平台的兼容,使用总结如下

    GTMBase64 Padded YES != Java DEFAULT
    GTMBase64 Padded NO == Java NO_PADDING | NO_WRAP
    GTMBase64 Padded YES == Java NO_WRAP
    GTMBase64 websafe Padded NO == java NO_PADDING | NO_WRAP | URL_SAFE
    GTMBase64 websafe Padded YES == java NO_WRAP | URL_SAFE
    
    GTMBase64对应的URL_Safe Base64

    GTMBase64传送门

    二:AES加解密

    • 相较于DES和3DES算法而言,AES算法有着更高的速度和资源使用效率,安全级别也较之更高了,被称为下一代加密标准。
    • AES使用的是对称加密.所谓对称加密就是加解密双方使用的密钥相同.因此通过一种保密的方法让客户端与服务器拥有该密钥,即可成功使用加解密.

    常见的IOS中AES加密

    //加密
    +(NSData *) AES128EncryptWithKey:(NSData*)key data:(NSData*)data
    {
        return [self doCipher:data iv:nil key:key context:kCCEncrypt error:nil];
    }
    
    //解密
    +(NSData *) AES128DecryptWithKey:(NSData*)key data:(NSData*)data
    {
        return [self doCipher:data iv:nil key:key context:kCCDecrypt error:nil];
    }
    
    
    + (NSData *)doCipher:(NSData *)dataIn
                      iv:(NSData *)iv
                     key:(NSData *)symmetricKey
                 context:(CCOperation)encryptOrDecrypt
                   error:(NSError **)error
    {
        CCCryptorStatus ccStatus   = kCCSuccess;
        size_t          cryptBytes = 0;
        NSMutableData  *dataOut    = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeAES128];
        
        ccStatus = CCCrypt( encryptOrDecrypt,
                           kCCAlgorithmAES,
                           kCCOptionECBMode | kCCOptionPKCS7Padding,
                           symmetricKey.bytes,
                           symmetricKey.length,
                           iv.bytes,
                           dataIn.bytes,
                           dataIn.length,
                           dataOut.mutableBytes,
                           dataOut.length,
                           &cryptBytes);
        
        if (ccStatus == kCCSuccess) {
            dataOut.length = cryptBytes;
        }
        else {
            if (error) {
                *error = [NSError errorWithDomain:@"kEncryptionError"
                                             code:ccStatus
                                         userInfo:nil];
            }
            dataOut = nil;
        }
        
        return dataOut;
    }
    

    坑爹的地方地方来了,传给后台永远是乱码.用了网上大多数方法都不行...一开始怀疑是不是URL_Safe Base64错了,或者是哪个地方没有UTF-8。与后台的沟通,反复验证数据加密后的字符串,就是对不上~~~

    • 网上拼命查资料发现好像是iv不对,iv是什么??......
    iOS原生API
    • 大概意思说,此属性可选,但只能用于CBC模式。如果出现那么他的长度必须和算法的block size保持一致。 如果是因为默认选择的CBC模式而且向量没有定义,那么向量会被定义为NULL。如果选择了ECB模式或是其他的流密码算法,之前所说的逻辑都不成立。

    CBC??ECB一脸懵逼,继续查资料

    • 初始化向量与密钥相比有不同的安全性需求,因此IV通常无须保密,然而在大多数情况中,不应当在使用同一密钥的情况下两次使用同一个IV。对于CBC和CFB,重用IV会导致泄露明文首个块的某些信息,亦包括两个不同消息中相同的前缀。对于OFB和CTR而言,重用IV会导致完全失去安全性。另外,在CBC模式中,IV在加密时必须是无法预测的;特别的,在许多实现中使用的产生IV的方法,例如SSL2.0使用的,即采用上一个消息的最后一块密文作为下一个消息的IV,是不安全的。
    ECB模式不需要初始化向量,CBC需要。
    CCCrypt重要参数

    完善之后AES的代码

    //AES加密
    + (NSData *)AES128EncryptWithKey:(NSString *)key iv:(NSString *)iv withNSData:(NSData *)data
    {
        char keyPtr[kCCKeySizeAES128+1];
        bzero(keyPtr, sizeof(keyPtr));
        [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
        char ivPtr[kCCKeySizeAES128+1];
        bzero(ivPtr, sizeof(ivPtr));
        [iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
        NSUInteger dataLength = [data length];
        int diff = kCCKeySizeAES128 - (dataLength % kCCKeySizeAES128);
        int newSize = 0;
        if(diff > 0)
        {
            newSize = (int)(dataLength + diff);
        }
        char dataPtr[newSize];
        memcpy(dataPtr, [data bytes], [data length]);
        for(int i = 0; i < diff; i++)
        {
            dataPtr[i + dataLength] = 0x00;
        }
        size_t bufferSize = newSize + kCCBlockSizeAES128;
        void *buffer = malloc(bufferSize);
        size_t numBytesEncrypted = 0;
        CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
                                              kCCAlgorithmAES128,
                                              0x00, //No padding
                                              keyPtr,
                                              kCCKeySizeAES128,
                                              ivPtr,
                                              dataPtr,
                                              sizeof(dataPtr),
                                              buffer,
                                              bufferSize,
                                              &numBytesEncrypted);
        if(cryptStatus == kCCSuccess)
        {
            //        NSData *data =[NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
            //        NSString *str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
            return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
        }
        return nil;
    }
    //AES解密
    + (NSData *)AES128DecryptWithKey:(NSString *)key iv:(NSString *)iv withNSData:(NSData *)data
    {
        char keyPtr[kCCKeySizeAES128+1];
        bzero(keyPtr, sizeof(keyPtr));
        [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
        char ivPtr[kCCKeySizeAES128+1];
        bzero(ivPtr, sizeof(ivPtr));
        [iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
        NSUInteger dataLength = [data length];
        size_t bufferSize = dataLength + kCCBlockSizeAES128;
        void *buffer = malloc(bufferSize);
        size_t numBytesEncrypted = 0;
        CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
                                              kCCAlgorithmAES128,
                                              0x00, //No padding
                                              keyPtr,
                                              kCCKeySizeAES128,
                                              ivPtr,
                                              [data bytes],
                                              dataLength,
                                              buffer,
                                              bufferSize,
                                              &numBytesEncrypted);
        if(cryptStatus == kCCSuccess)
        {
            //        NSData *data =[NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
            // NSString *str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
            return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
        }
        return nil;
    }
    
    

    写个工具类复制过去就可以了

    三:MD5

    • 对任意的数据进行计算,生成固定长度的字符串.32个字符.
    • 一般用来加密密码.
    • 有时候也用来验证文件下载时,是否被篡改过.
      文件下载完成之后计算文件的md5值,与服务器计算的MD5值比较,如果不一样那么这个文件在下载的过程中被篡改了.

    MD5终端命令

    # 得到文件的MD5值
    $ md5 文件名
    
    # 得到字符串的MD5值
    md5 -s "string"
    
    +(NSString *) md5: (NSString *) inPutText
    {
        const char *cStr = [inPutText UTF8String];
        unsigned char result[CC_MD5_DIGEST_LENGTH];
        CC_MD5(cStr, strlen(cStr), result);
        
        return [[NSString stringWithFormat:@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
                 result[0], result[1], result[2], result[3],
                 result[4], result[5], result[6], result[7],
                 result[8], result[9], result[10], result[11],
                 result[12], result[13], result[14], result[15]
                 ] lowercaseString];
    }
    
    • 如果原始密码过于简单,直接进行MD5加密是很容易被暴力破解的.
    • 为了增强密码的安全性,防止加密的密码被暴力破解,可以向原始密码中加盐.
    • 盐 : 服务器端和客户端约定的一个字符串.
    • MD5+盐 : 原始密码+盐拼接出新的密码字符串,再进行MD加密.
    • 以上为加一勺盐,比单纯的直接MD5加密安全性要高.
    • 盐要足够的咸,越咸越安全.

    RSA部分会在第二部分,如果有时间的话 笑~
    参考文章:加密解密介绍

    相关文章

      网友评论

      本文标题:iOS加密(一):Base64 +AES +MD5模式

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