一.AES(对称加密)

1.1.AES加密填充方式

      待加密的明文以16字节分组进行加密,如果数据字节长度不是16的倍数,最后的一组则需要在有效数据后面进行填充,使得数据长度变为16字节,AES填充方式分为NoPadding、PKCS5(PKCS7)、ISO10126、Zeros。

NoPadding:不填充,那就只能加密长度为16倍数的数据,一般不使用;

Zeros:补0,如果原数据长度恰好是16的倍数,也要补16个0;

ISO10126:最后一个字节是填充的字节数(包括最后一字节),其他全部填随机数

1 2 3 4 5 6 7 8 9 10 – x x x x x 6

填充6个字节

PKCS5(PKCS7):应用比较多,最后一组缺几个字节就填充几

1 2 3 4 5 6 7 8 9 10 – 6 6 6 6 6 6

前面10个字节,缺6字节才能为一组,填充6个6;如果恰好是16个字节,则填充16个16.

二.RSA(非对称加密)

        首先生成两个密钥,一个公钥,一个私钥,前者加密,后者解密,客户端和服务器各执一份.一般为了安全,私钥是不会给前端暴露出来 的,只会通过私钥生成一个公开的公钥提供给外部对数据进行加密。将加密后的数据传给后端,后端使用私钥解密.当客户端向服务器发送数据的时候,先用公钥加密,再有私钥加签,到了服务器一端,用公钥验签,用私钥解密,然后再往下走.加密是为了安全,加签是为了防止冒充APP 客户端进行请求,导致服务器的瘫痪.

三.前后端常用的加密方式

       3.1.对称加密:指加密和解密使用相同密钥的加密算法,速度较快(包括DES算法,3DES算法,RC5算法,AES算法等);

       3.2.非对称对称加密:指加密和解密使用不同密钥的加密算法,速度较慢(包括RSA等);

       3.3.哈希算法:简单来说就是一种将数据通过算法变成不可逆的数据(包括:MD5、HMAC等);

      3.4.数字签名:是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术实现,用于鉴别数字信息的方法

四.AES和RSA结合使用流程:

        客户端签名

        4.1.通讯时,客户端先将数据进行签名,签名可参照微信支付将请求参数按照参数名ASCII码从小到大排序(字典升序进行排列),使用URL键值对的格式(即key1=value1&key2=value2…)进行拼接成字符串。然后用MD5进行加密得到sign1。最后将签名sign1放在请求头中(温馨提示:请求参数中可加入时间戳和随机字符串,可防止重放攻击);

        客户端加密

        4.2.服务器先生成一对RSA的公钥和私钥,然后私钥留在服务器,公钥返回给我们客户端;

        4.3.其次客户端随机生成一个字符串作为AES加密的key1(16位)==>(用AES加密算法来加密与服务器传输的主要内容);

       4.4.最后 客户端 使用从服务器获取的RSA 公钥 将自己本地随机生成的AES的key1进行加密,将加密后的数据放入到请求头中传递给服务器;

      4.5.客户端将请求参数转换为json字符串后,使用本地随机生成的AES加密的key1进行AES加密,然后将加密后的字符串放在请求体中传递给服务器;

       服务器解密

      4.6.服务器 端先用RSA的 私钥 将AES的key1进行解密(因为AES是使用RSA的公钥加密过的,所以需要使用对应的私钥进行解密),然后使用这个key1来解密传输的主要内容;

      服务器签名比对(确认数据是否被篡改过)

     4.7. 然后服务器运用客户端使用的签名方法将主要内容进行签名得到sign,然后跟sign1进行对比。如果相同即数据没有被篡改

     4.8.第6步确认无误后,服务器将要返回给客户端的数据进行签名得到sign2

      服务器加密

     4.9.然后服务器随机生成一个字符串作为AES加密的key2,用AES加密算法来加密需要返回的数据内容;

     4.10.其次 服务器 再使用RSA的 私钥 将AES的key2进行加密,与AES加密后的内容和sign2一同返回给客户端; 

     客户端解密

     4.11.客户端 收到服务器返回的数据后先用RSA的 公钥 将AES的key2进行解密,然后使用这个key2来解密返回的主要内容; 

     4.12.最后将主要内容进行签名得到sign,与sign2进行对比。如果一致则表示数据无误。可进行后续操作

五.代码实例:

 

+ (NSURLSessionTask *)requestPostEncryptionWithURL:(NSString *)URL parameters:(NSDictionary *)parameter networkRequestGraceTimeType:(NetworkRequestGraceTimeType)type  success:(PPRequestSuccess)success failure:(PPRequestFailure)failure
{
   
     ZHHud * hud = [DFHTTPRequest hud:type];

    AFHTTPSessionManager * manager = [DFHTTPRequest sharedZHNetWorking].manager;
    
    /**
     签名
     
     */
    //获取当前的时间戳
    NSString*timeStamp=[NSString df_getNowTimeTimestamp];
    
    //字典按照升序进行排列
    NSString*sign=[self accordingSortDic:parameter];
    //将拼接起来的字符串后面追加时间戳
    NSString*signComponentTimeStamp=[sign stringByAppendingFormat:@"_timestamp%@",timeStamp];
    //字符串的首段和尾部追加appkey
    NSString*lastStr=[NSString stringWithFormat:@"%@%@%@",encryption_Appkey,signComponentTimeStamp,encryption_Appkey];
    //字符串使用MD5进行加密
    NSString*MD5Str=[NSString md532BitLower:lastStr];
    
    /**
     加密
     
     */
    //1.先随机生成一个16位的AES key
    NSString*AES_Key=[self ret32bitString];
    //从服务器获取RSA的公钥==>使用RSA的公钥对AES的公钥进行加密并放在请求头中传递给服务器
    NSString*RSA_Public_Key=[[NSUserDefaults standardUserDefaults]objectForKey:@"publicKey"];
    //2.使用RSA进行加密(放入到请求头里面)

    NSString*RSA_Str=[RSAEncryptor encryptString:AES_Key publicKey:RSA_Public_Key];//加密后的AES公钥
    //使用RSA公钥加密后的AES Key
    [manager.requestSerializer  setValue:RSA_Str forHTTPHeaderField:@"_secret"];
    
    //当前时间戳
     [manager.requestSerializer  setValue:timeStamp forHTTPHeaderField:@"_timestamp"];
    //签名
     [manager.requestSerializer  setValue:MD5Str forHTTPHeaderField:@"_sign"];
  
     
     
     NSString*token=[DFSaveDataClass df_getLocalData:@"token"];
    
     NSString*newToken=[NSString stringWithFormat:@"bearer %@",token];
    
     [manager.requestSerializer setValue:newToken forHTTPHeaderField:@"Authorization"];
    
    //请求体使用AES加密后传给给服务器
    
     NSString*jsonStr=[NSString df_dicConversionWithJsonStringData:parameter];//将请求体转化为json字符串
    
     NSString*dicEncryption=[AESCrypt AES128Encrypt:jsonStr key:AES_Key];
    
    [manager.requestSerializer  setValue:[NSURL URLWithString:URL].host forHTTPHeaderField:@"host"];

    return [manager POST:URL parameters:dicEncryption progress:^(NSProgress * _Nonnull uploadProgress) {
        
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject)  {
        
        [hud hideAnimated:YES];
  
        if (responseObject)
        {
            //获取返回后的字符串
            NSString *result = [[NSString alloc]initWithData:responseObject encoding:NSUTF8StringEncoding];
            
            if ([task.response isKindOfClass:[NSHTTPURLResponse class]])
            {
                NSHTTPURLResponse *r = (NSHTTPURLResponse *)task.response;
                //取出所有的请求头
                NSDictionary*dict=[r allHeaderFields];
                //取出使用RSA加密后的AES 的key
                NSString*secretStr=dict[@"_secret"];

                NSDictionary*lastDic=nil;
    
                    //对加密后的AES key进行解密
                    NSString*decryptionStr=[RSA decryptString:secretStr publicKey:RSA_Public_Key];
                    //将返回的结果使用解密后的AES Key进行解密
                    NSString*responseStr=[AESCrypt AES128Decrypt:result key:decryptionStr];
                    //将JSON字符串转化为字典
                    lastDic=[NSDictionary df_JsonStringConversionWithDic:responseStr];
                
                
            }

            
        }
        
        
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {


            failure(error);
       
       

       
    }];
}

//获取当前时间戳
+(NSString*)df_getNowTimeTimestamp
{
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init] ;

       [formatter setDateStyle:NSDateFormatterMediumStyle];

       [formatter setTimeStyle:NSDateFormatterShortStyle];

       [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss SSS"]; // ----------设置你想要的格式,hh与HH的区别:分别表示12小时制,24小时制

       //设置时区,这个对于时间的处理有时很重要

       NSTimeZone* timeZone = [NSTimeZone timeZoneWithName:@"Asia/Shanghai"];

       [formatter setTimeZone:timeZone];

       NSDate *datenow = [NSDate date];//现在时间,你可以输出来看下是什么格式

       NSString *timeSp = [NSString stringWithFormat:@"%ld", (long)[datenow timeIntervalSince1970]*1000];

       return timeSp;
}

//字典按照升序进行排列
+(NSString*)accordingSortDic:(NSDictionary*)dic
{
    
    /**
     
     将字典按照升序进行排列(具体分为下面五个步骤)
     */
    
      //1.取出所有的key
    NSArray *keyArray = [dic allKeys];
      //2.将key按照升序进行排列
    NSArray *sortArray = [keyArray sortedArrayUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
        return [obj1 compare:obj2 options:NSNumericSearch];
    }];
      //3.根据排序好的key,取出对应的value值
    NSMutableArray *valueArray = [NSMutableArray array];
    
    
    NSMutableArray*sortKeyArray=[NSMutableArray array];

    for (NSString*sortString in sortArray)
    {
        id value=[dic objectForKey:sortString];
        
         if ([value isKindOfClass:[NSString class]]||[value isKindOfClass:[NSNumber class]])
         {
             NSString*valueStr=[NSString stringWithFormat:@"%@",value];
             //[[NSString stringWithFormat:@"%@",value] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
             
             if (valueStr.length>0)
             {
                 [sortKeyArray addObject:sortString];
                 [valueArray addObject:valueStr];
             }

         }
        
    }
    //4.现在我们有两个数组,分别对应升序排序的key和value,所以再创建一个keyValue的数组来存储每一个key和value的格式。
    NSMutableArray *signArray = [NSMutableArray array];
    for (int i = 0; i < sortKeyArray.count; i++) {
        NSString *keyValueStr = [NSString stringWithFormat:@"%@%@",sortKeyArray[i],valueArray[i]];
        [signArray addObject:keyValueStr];
    }
    //5.将字符串进行拼接起来
    NSString *sign =[signArray componentsJoinedByString:@""];

    return sign;
    
}

//随机生成一个32位字符串
+(NSString*)ret32bitString

{

    char data[32];

    for (int x=0; x <16; data[x++] = (char)('A' + (arc4random_uniform(26))));

    return [[NSString alloc] initWithBytes:data length:16 encoding:NSUTF8StringEncoding];

}

     

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐