您的位置:首页 > 移动开发 > IOS开发

iOS中 DES加解密详解 (中文韩文字符加密丢失情况)

2017-11-09 17:31 435 查看
目前在项目当中使用到DES加解密,遇到一些问题,进行梳理,分享一下。DES和3DES加解密的原理谷歌百度都有详细的解码,这里就不赘述了。

1.DES加解密参数类型问题:

  目前项目使用DES加解密都是针对字符串使用,要对一个NSDictionary类型进行加解密,需要转换成json字符串进行加解密:

          使用convertToJSONData把NSDictionary转成json string进行加解密;dictionaryWithJsonString进行json转化NSdictionary数据。+ (NSString*)convertToJSONData:(id)infoDict {
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:infoDict options:NSJSONWritingPrettyPrinted error:&error];
NSString *jsonString = @"";
if (! jsonData) {
NSLog(@"Got an error: %@", error);
} else {
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
jsonString = [jsonString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[jsonString stringByReplacingOccurrencesOfString:@"\n" withString:@""];
return jsonString;
}
+ (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;
}
2.DES加解密iV和填充方式:
在开发过程中,基本上碰到的问题就是iOS端和服务器、安卓java端的加解密不匹配问题。在加解密之前应和服务端安卓端商定DES加解密过程中的iV向量和填充方式。iV向量一般取8位,对于iOS来说无论采用PKCS7Padding还是PKCS5Padding的填充方式,iv的位数不影响,对于安卓java来说采用PKCS7、PKCS5
iv一般不超过8位,综合起来,iv取8位。项目进行过程中一定要商量好iv的取值和填充方式。

目前博主采用的方式如下:对key(字符串格式,一般由服务器下发),先采用MD5,再取其MD5值的前八位作为iv:(源码文后上传)

  NSString *md5Str = [key MD5];

NSString *keyString = [md5Str substringToIndex:8];

采用PKCS7Padding作为填充方式。

3.DES加解密中文字符串丢失:

在加解密过程中,一个dic转成jsonstring进行加解密时,出现数据丢失的情况,查资料发现有一篇类似的情况如下:iOS之POST请求数组样式参数DES加密问题
简而言之就是加密前进行url编码,加密后进行url解码。测试后发现不适合博主的情况。

加密解密后,出现字符串截短:



   一开始以为字符串长度太长,设置更长字符串后发现:



   
发现还是一样,几番思索下,发现加密明文中文,dataLength长度不对,导致被截短。ASC编码汉子占3个字符,而应为只有1个,直接调用【data length】方式获取的长度不对,问题找到。在进行length计算是要额外进行中文字的长度计算:

+ (NSUInteger)dateLengthForPlainText:(NSString *)plainText{
//明文中文,dataLength长度要增加 汉字占3个字节
NSUInteger dataLength = 0;
int length = [plainText length];
int hangCount = 0;//中文数量
for (int i=0; i<length; ++i)
{
NSRange range = NSMakeRange(i, 1);
NSString *subString = [plainText substringWithRange:range];
const char *cString = [subString UTF8String];
if (strlen(cString) == 3) //中文3个字节
{
hangCount++;
}
}
dataLength = dataLength + hangCount*2;
return dataLength;
}

结果如下:



加解密正常了,说明length计算不对。

4.DES加解密过程中字符串的编码方式:

与服务器和安卓端都应该约定好,采用什么方式进行字符串编码,这里博主采用的是utf-8编码。如果编码方式不一致,加解密也是失败的。

DES加解密源码如下:(加密后转换成了十六进制大写)

NHIDes.h文件:

//
// NHIdes.h
// DES
//
// Created by Asten on 17/11/1.
// Copyright © 2017年 Asten. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonCryptor.h>
#import "GTMBase64.h"
@interface NHIdes : NSObject
//加密
+ (NSString *)lcEncryUseDES:(NSString *)string andKey:(NSString *)key;
//解密
+ (NSString *)lcDecryUseDES:(NSString *)string andKey:(NSString *)key;
//url编码
+ (NSString *)UrlValueEncode:(NSString *)str;
//url解码
+ (NSString *)decodeFromPercentEscapeString: (NSString *) input;
@end
NHIDes.m文件:
//
// NHIdes.m
// DES
//
// Created by Asten on 17/11/1.
// Copyright © 2017年 Asten. All rights reserved.
//

#import "NHIdes.h"

@implementation NHIdes
+ (NSString *)lcEncryUseDES:(NSString *)string andKey:(NSString *)key{
NSString *str=[self encryptUseDES:string key:key];
return str;
}
+ (NSString *)lcDecryUseDES:(NSString *)string andKey:(NSString *)key{
NSString *str=[self decryptUseDES:string key:key];
return str;
}

/*字符串加密
*参数
*plainText : 加密明文
*key : 密钥
*/
+ (NSString *) encryptUseDES:(NSString *)plainText key:(NSString *)key
{
NSString *md5Str = [key MD5];
NSString *keyString = [md5Str substringToIndex:8];

NSString *ciphertext = nil;
const char *textBytes = [plainText UTF8String];
NSUInteger dataLength = [plainText length] +[NHIdes dateLengthForPlainText:plainText];
unsigned char buffer[1024*100];
memset(buffer, 0, sizeof(char));
Byte *iv = (Byte *)[[keyString dataUsingEncoding:NSUTF8StringEncoding] bytes];
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmDES,
kCCOptionPKCS7Padding,
[keyString UTF8String], kCCKeySizeDES,
iv,
textBytes, dataLength,
buffer, 1024*100,
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
NSData *data = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesEncrypted];
// ciphertext = [[NSString alloc] initWithData:[GTMBase64 encodeData:data] encoding:NSUTF8StringEncoding];
// NSLog(@"des加密后的字符串:%@",ciphertext);
ciphertext = [NHIdes dataTohexString:data];
NSLog(@"des加密后的十六进制:%@",ciphertext);
}
return ciphertext;
}

+ (NSUInteger)dateLengthForPlainText:(NSString *)plainText{ //明文中文,dataLength长度要增加 汉字占3个字节 NSUInteger dataLength = 0; int length = [plainText length]; int hangCount = 0;//中文数量 for (int i=0; i<length; ++i) { NSRange range = NSMakeRange(i, 1); NSString *subString = [plainText substringWithRange:range]; const char *cString = [subString UTF8String]; if (strlen(cString) == 3) //中文3个字节 { hangCount++; } } dataLength = dataLength + hangCount*2; return dataLength; }

//转换成16进制
+(NSString *) parseByte2HexString:(Byte *) bytes :(int)len{
NSString *hexStr = @"";
if(bytes)
{
for(int i=0;i<len;i++)
{
NSString *newHexStr = [NSString stringWithFormat:@"%x",bytes[i]&0xff]; ///16进制数
if([newHexStr length]==1)
hexStr = [NSString stringWithFormat:@"%@0%@",hexStr,newHexStr];
else
{
hexStr = [NSString stringWithFormat:@"%@%@",hexStr,newHexStr];
}
NSLog(@"%@",hexStr);
}
}
return hexStr;
}

+ (NSString *)dataTohexString:(NSData*)data
{
Byte *bytes = (Byte *)[data bytes];
NSString *hexStr=@"";
for(int i=0;i<[data length];i++)
{
NSString *newHexStr = [NSString stringWithFormat:@"%02X",bytes[i]&0xff];//16进制数
if([newHexStr length]==1)
hexStr = [NSString stringWithFormat:@"%@0%@",hexStr,newHexStr];
else
hexStr = [NSString stringWithFormat:@"%@%@",hexStr,newHexStr];
}
return hexStr;
}
+ (NSString *)convertDataToHexStr:(NSData *)data
{
if (!data || [data length] == 0) {
return @"";
}
NSMutableString *string = [[NSMutableString alloc] initWithCapacity:[data length]];
[data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) {
unsigned char *dataBytes = (unsigned char*)bytes;
for (NSInteger i = 0; i < byteRange.length; i++) {
NSString *hexStr = [NSString stringWithFormat:@"%02X", (dataBytes[i]) & 0xff];
if ([hexStr length] == 2) {
[string appendString:hexStr];
} else {
[string appendFormat:@"0%@", hexStr];
}
}
}];
return string;
}
//解密
+ (NSString *) decryptUseDES:(NSString*)cipherText key:(NSString*)key
{
//先把密文转成字节数组 获取key的md5 取前八位
NSData *cipherData = [NHIdes convertHexStrToData:cipherText];
NSString *md5Str = [key MD5];
NSString *keyString = [md5Str substringToIndex:8];
// NSData* cipherData = [GTMBase64 decodeString:cipherText];
unsigned char buffer[1024*100];
memset(buffer, 0, sizeof(char));
size_t numBytesDecrypted = 0;
Byte *iv = (Byte *)[[keyString dataUsingEncoding:NSUTF8StringEncoding] bytes];
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmDES,
kCCOptionPKCS7Padding,
[keyString UTF8String],
kCCKeySizeDES,
iv,
[cipherData bytes],
[cipherData length],
buffer,
1024*100,
&numBytesDecrypted);
NSString* plainText = nil;
if (cryptStatus == kCCSuccess) {
NSData* data = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesDecrypted];
plainText = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"des解密后的字符串:%@",plainText);
}
return plainText;
}
//解密时转成data
+ (NSData *)convertHexStrToData:(NSString *)str {
if (!str || [str length] == 0) {
return nil;
}

NSMutableData *hexData = [[NSMutableData alloc] initWithCapacity:8];
NSRange range;
if ([str length] % 2 == 0) {
range = NSMakeRange(0, 2);
} else {
range = NSMakeRange(0, 1);
}
for (NSInteger i = range.location; i < [str length]; i += 2) {
unsigned int anInt;
NSString *hexCharStr = [str substringWithRange:range];
NSScanner *scanner = [[NSScanner alloc] initWithString:hexCharStr];

[scanner scanHexInt:&anInt];
NSData *entity = [[NSData alloc] initWithBytes:&anInt length:1];
[hexData appendData:entity];

range.location += range.length;
range.length = 2;
}

NSLog(@"hexdata: %@", hexData);
return hexData;
}
//url编码
+ (NSString *)UrlValueEncode:(NSString *)str{
NSString *result = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(CFStringRef)str,
NULL,
CFSTR("!*'();:@&=+$,/?%#[]"),
kCFStringEncodingUTF8));

return result;
}
+ (NSString *)decodeFromPercentEscapeString: (NSString *) input
{
NSMutableString *outputStr = [NSMutableString stringWithString:input];
[outputStr replaceOccurrencesOfString:@"+"
withString:@" "
options:NSLiteralSearch
range:NSMakeRange(0, [outputStr length])];

return [outputStr stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
}

@end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息