- 参考文章:https://www.jianshu.com/p/ec7b848f83a7
- 参考文章:https://blog.csdn.net/siwen1990/article/details/79297744
目录
简介
Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间.简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。(来源百度百科解释)
HASH算法是密码学的基础,比较常用的有MD5和SHA,最重要的两条性质,就是不可逆和无冲突。
本篇先介绍MD5,下篇介绍SHA。
什么是MD5
MD5即Message-Digest Algorithm 5(信息-摘要算法5),用于确保信息传输完整一致。是计算机广泛使用的杂凑算法之一(又译摘要算法、哈希算法),主流编程语言普遍已有MD5实现。将数据(如汉字)运算为另一固定长度值,是杂凑算法的基础原理,MD5的前身有MD2、MD3和MD4。 MD5算法具有以下特点:
1、压缩性:任意长度的数据,算出的MD5值长度都是固定的32个字符。
2、容易计算:从原数据计算出MD5值很容易。
3、抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
4、强抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。
问题及解决办法
接下来隆重介绍一个网站:http://www.cmd5.com/
仔细看下网站的介绍不难发现:其实它是一个巨大的数据库.利用明文和Hash的数据记录,进行反向查询
在终端输入 md5 -m “abc”得到abc的散列值:900150983cd24fb0d6963f7d28e17f72
将散列值在网站输入
当然,提供哈希反向查询服务的不仅仅只有这个网站,还有很多盈利性的公司提供有偿服务。所以如果我们单纯的直接使用Hash算法,安全性会非常低。那么如何解决这个问题,提高安全性呢?
- 解决方案一:加盐
在原有的数据的基础上拼接一段字符串,然后再做散列运算,即使被反向查询也拿不到具体的原始数据。但这种方式同样存在问题,如果数据被反查出来真正的数据也是包含在其中的。另外程序员在加盐时一般是将盐固定的写死在程序中,如果程序被逆向同样存在安全隐患。所以加盐目前也不安全,不推荐。
- 解决方案二:截取部分数据或混排进行散列运算
将原始数据进行截取,针对部分数据进行散列运算,即使被反查也拿不到原始数据。在此基础上还可以对原始数据进行混排打乱次序后再截取部分数据进行散列运算以提高安全性。
- 解决方案三:HMAC(Hash-based Message Authentication Code)
HMAC它使用一个密钥,并且做了两次散列!具体用法在文章后边的OC代码实现部分。
注意:在开发过程中,这个密钥KEY是从服务器获取的.并且一个用户对应一个KEY
对于这种加密方案.就可以很好的保护用户的隐私信息.因为就算泄露了KEY.这个KEY也只是一个用户的.不会污染整个项目.如果拿到这个KEY,然后想反查询出用户的明文密码.这个成本,除非这是马云的账户.
应用场景
场景一:版权识别 完整性校验
-
很多软件厂家会将软件的MD5值挂在网站上,用户下载软件后可以对软件进行一个MD5运算,查看散列值与商家提供的散列值是否一致,来确认将要安装的软件是否是正版软件,是否经过了修改。
-
数据在网络传输过程中很可能被别有用心的人拦截篡改,这时可以对数据进行散列运算,对散列值进行加密传输(原始数据较大的话对原始数据加密效率会很低),对方在收到数据和散列值后,做同样的运算来辨别数据是否完整。
场景二:登录密码保护
用户在登录或注册时需要输入密码,此时应对密码进行部分截取,然后进行散列运算,将散列值上送服务器,保护用户密码安全。
现在很多APP只提供了密码重置功能,而不再提供密码找回,因为后台服务器也不知道用户的真实密码,它存储的只是一个散列值,因为散列值的不可逆性无法反推用户的密码,目的也是为了保护用户的密码安全,一旦数据发生泄漏也不会泄漏用户的密码。从这也可以看出有密码找回功能的App,后台肯定存储了用户的真实密码。与存储散列值的方式相比是存在安全隐患的。
OC代码实现
- MD5值的获取
获取字符串的Md5值
/**
获取字符串的Md5值
@param string 字符串
@return 加密后Md5值
*/
+(NSString *)md5:(NSString *)string{
//@""空字符串也是字符串 同样会生成Md5摘要
const char *cStr = [string UTF8String];
CC_LONG length = (CC_LONG)strlen(cStr);
unsigned char digest[CC_MD5_DIGEST_LENGTH];
//将结果写入digest
CC_MD5(cStr,length,digest);
//将结果转为字符串
NSMutableString *md5Str = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++){
[md5Str appendFormat:@"%02x", digest[i]];
}
return md5Str;
}
获取文件的Md5值
/**
获取文件的Md5值
@param filePath 文件路径
@return 加密后Md5值
*/
+ (NSString *)getFileMd5:(NSString *)filePath{
if([[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:nil]){
NSData *data = [NSData dataWithContentsOfFile:filePath];
unsigned char digest[CC_MD5_DIGEST_LENGTH];
CC_MD5(data.bytes,(CC_LONG)data.length,digest);
NSMutableString *md5Str = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
for( int i = 0; i < CC_MD5_DIGEST_LENGTH; i++ ){
[md5Str appendFormat:@"%02x", digest[i]];
}
return md5Str;
}
return nil;
}
获取NSData的Md5值
/**
获取NSData的Md5值
@param data 字节流
@return 加密后Md5值
*/
+ (NSString *)getDataMd5:(NSData *)data{
unsigned char digest[CC_MD5_DIGEST_LENGTH];
CC_MD5(data.bytes,(CC_LONG)data.length,digest);
NSMutableString *md5Str = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
for( int i = 0; i < CC_MD5_DIGEST_LENGTH; i++ ){
[md5Str appendFormat:@"%02x", digest[i]];
}
return md5Str;
}
- HMAC值的获取
获取字符串的HMAC值
/**
获取字符串的HMAC值
@param string 字符串
@param key key
@return 加密后HMAC值
*/
+(NSString *)HMAC:(NSString *)string key:(NSString*)key{
// //用户密码
// NSStirng * pwd = @"123456";
// //加密用的KEY,注意是从服务器获取的
// NSString * key = @"hmackey";
//转成C串
const char *strData = string.UTF8String;
const char *keyData = key.UTF8String;
uint8_t buffer[CC_SHA256_DIGEST_LENGTH];
//hmac加密
//kCCHmacAlgSHA256 选项 与 CC_SHA256_DIGEST_LENGTH要对应上
CCHmac(kCCHmacAlgSHA256, keyData, strlen(keyData), strData, strlen(strData), buffer);
NSMutableString *hmacStr = [NSMutableString string];
for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
[hmacStr appendFormat:@"%02x", buffer[i]];
}
return hmacStr;
}
获取文件的HMAC值
/**
获取文件的HMAC值
@param filePath 文件路径
@param key key
@return 加密后HMAC值
*/
+ (NSString *)getFileHMAC:(NSString *)filePath key:(NSString*)key{
if([[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:nil]){
NSData *data = [NSData dataWithContentsOfFile:filePath];
const char *keyData = key.UTF8String;
uint8_t buffer[CC_SHA256_DIGEST_LENGTH];
//hmac加密
CCHmac(kCCHmacAlgSHA256, keyData, strlen(keyData), data.bytes, data.length, buffer);
NSMutableString *hmacStr = [NSMutableString string];
for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
[hmacStr appendFormat:@"%02x", buffer[i]];
}
return hmacStr;
}
return nil;
}
获取NSData的HMAC值
/**
获取NSData的HMAC值
@param data 字节流
@param key key
@return 加密后HMAC值
*/
+ (NSString *)getDataHMAC:(NSData *)data key:(NSString*)key{
const char *keyData = key.UTF8String;
uint8_t buffer[CC_SHA256_DIGEST_LENGTH];
//hmac加密
CCHmac(kCCHmacAlgSHA256, keyData, strlen(keyData), data.bytes, data.length, buffer);
NSMutableString *hmacStr = [NSMutableString string];
for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
[hmacStr appendFormat:@"%02x", buffer[i]];
}
return hmacStr;
}
行者常至,为者常成!