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

由于公司对于信息安全比较注重,需要对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模式)