待加密的明文以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];//字符串的首段和尾部追加appkeyNSString*lastStr=[NSString stringWithFormat:@"%@%@%@",encryption_Appkey,signComponentTimeStamp,encryption_Appkey];//字符串使用MD5进行加密NSString*MD5Str=[NSString md532BitLower:lastStr];/** 加密*///1.先随机生成一个16位的AES keyNSString*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 的keyNSString*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.取出所有的keyNSArray *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